├── .circleci ├── config.yml └── src │ ├── config.tpl │ ├── jobs │ ├── release.yml.tpl │ └── release_no_change.yml.tpl │ └── workflows │ └── edxapp │ ├── release_jobs.yml.tpl │ └── release_no_change.yml.tpl ├── .dockerignore ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .gitlint ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── bin ├── _config.sh ├── activate ├── ci ├── compose ├── deactivate ├── setup-ssl └── watch ├── docker-compose.yml ├── docker └── files │ ├── etc │ └── nginx │ │ ├── conf.d │ │ ├── cms.conf │ │ └── lms.conf │ │ └── ssl │ │ └── ssl.conf.tpl │ └── usr │ └── local │ └── bin │ ├── auth_init │ └── entrypoint.sh ├── docs ├── images │ ├── edx_oauth2_client.png │ ├── moodle_oauth2_configure_endpoints.png │ ├── moodle_oauth2_configure_mappings.png │ ├── moodle_oauth2_enable_plugin.png │ └── moodle_oauth2_service.png ├── moodle-oauth-configuration.md └── richie-configuration.md ├── env.d ├── development ├── production ├── redis └── redis-sentinel ├── gitlint └── gitlint_emoji.py └── releases ├── dogwood └── 3 │ └── fun │ ├── CHANGELOG.md │ ├── Dockerfile │ ├── activate │ ├── config │ ├── cms │ │ ├── __init__.py │ │ ├── docker_build_development.py │ │ ├── docker_build_production.py │ │ ├── docker_run.py │ │ ├── docker_run_development.py │ │ ├── docker_run_preprod.py │ │ ├── docker_run_production.py │ │ ├── docker_run_staging.py │ │ ├── secrets.yml │ │ └── settings.yml │ └── lms │ │ ├── __init__.py │ │ ├── backends.py │ │ ├── docker_build_development.py │ │ ├── docker_build_production.py │ │ ├── docker_run.py │ │ ├── docker_run_development.py │ │ ├── docker_run_preprod.py │ │ ├── docker_run_production.py │ │ ├── docker_run_staging.py │ │ ├── root_urls.py │ │ ├── settings.yml │ │ ├── storage.py │ │ └── utils.py │ ├── demo-course.tar.gz │ ├── entrypoint.sh │ ├── patches │ ├── edx-platform_dogwood.3-fun_XBlock.patch │ ├── edx-platform_dogwood.3-fun_moto.patch │ └── edx-platform_dogwood.3-fun_strftime-1900.patch │ ├── pip.conf │ └── requirements.txt ├── eucalyptus └── 3 │ └── wb │ ├── CHANGELOG.md │ ├── Dockerfile │ ├── activate │ ├── config │ ├── cms │ │ ├── __init__.py │ │ ├── docker_build_development.py │ │ ├── docker_build_production.py │ │ ├── docker_run.py │ │ ├── docker_run_development.py │ │ ├── docker_run_preprod.py │ │ ├── docker_run_production.py │ │ └── docker_run_staging.py │ └── lms │ │ ├── __init__.py │ │ ├── backends.py │ │ ├── docker_build_development.py │ │ ├── docker_build_production.py │ │ ├── docker_run.py │ │ ├── docker_run_development.py │ │ ├── docker_run_preprod.py │ │ ├── docker_run_production.py │ │ ├── docker_run_staging.py │ │ ├── storage.py │ │ └── utils.py │ ├── demo-course.tar.gz │ ├── entrypoint.sh │ ├── patches │ └── edx-platform_eucalyptus.3-remove_faulthandler.patch │ └── requirements.txt ├── hawthorn └── 1 │ └── oee │ ├── CHANGELOG.md │ ├── Dockerfile │ ├── activate │ ├── config │ ├── cms │ │ ├── __init__.py │ │ ├── docker_build_development.py │ │ ├── docker_build_production.py │ │ ├── docker_run.py │ │ ├── docker_run_ci.py │ │ ├── docker_run_development.py │ │ ├── docker_run_feature.py │ │ ├── docker_run_preprod.py │ │ ├── docker_run_production.py │ │ ├── docker_run_staging.py │ │ └── root_urls.py │ └── lms │ │ ├── __init__.py │ │ ├── backends.py │ │ ├── docker_build_development.py │ │ ├── docker_build_production.py │ │ ├── docker_run.py │ │ ├── docker_run_ci.py │ │ ├── docker_run_development.py │ │ ├── docker_run_feature.py │ │ ├── docker_run_preprod.py │ │ ├── docker_run_production.py │ │ ├── docker_run_staging.py │ │ ├── root_urls.py │ │ ├── storage.py │ │ └── utils.py │ ├── entrypoint.sh │ ├── patches │ ├── edx-ora2_hawthorn.1-oee.patch │ ├── edx-platform_hawthorn.1-oee.patch │ ├── edx-platform_hawthorn.1-oee_moto.patch │ └── edx-platform_hawthorn.1-oee_requirements-py2neo.patch │ └── requirements.txt └── ironwood └── 2 └── oee ├── CHANGELOG.md ├── Dockerfile ├── activate ├── config ├── cms │ ├── __init__.py │ ├── docker_build_development.py │ ├── docker_build_production.py │ ├── docker_run.py │ ├── docker_run_ci.py │ ├── docker_run_development.py │ ├── docker_run_feature.py │ ├── docker_run_preprod.py │ ├── docker_run_production.py │ ├── docker_run_staging.py │ └── root_urls.py └── lms │ ├── __init__.py │ ├── backends.py │ ├── docker_build_development.py │ ├── docker_build_production.py │ ├── docker_run.py │ ├── docker_run_ci.py │ ├── docker_run_development.py │ ├── docker_run_feature.py │ ├── docker_run_preprod.py │ ├── docker_run_production.py │ ├── docker_run_staging.py │ ├── root_urls.py │ ├── storage.py │ └── utils.py ├── entrypoint.sh ├── patches ├── edx-ora2_ironwood.2-oee.patch ├── edx-platform_ironwood.2-oee.patch ├── edx-platform_ironwood.2-oee_moto.patch └── edx-platform_ironwood.2-oee_requirements-py2neo.patch └── requirements.txt /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # THIS FILE WAS AUTOMATICALLY GENERATED. 2 | # Please edit src/config.tpl file instead. 3 | 4 | 5 | # Open edX 6 | # Docker containers CI 7 | version: 2 8 | 9 | # Templates 10 | defaults: &defaults 11 | machine: 12 | image: default 13 | resource_class: large 14 | working_directory: ~/fun 15 | 16 | build_steps: &build_steps 17 | steps: 18 | # Checkout openedx-docker sources 19 | - checkout 20 | 21 | # Login to DockerHub with encrypted credentials stored as secret 22 | # environment variables (set in CircleCI project settings) if the expected 23 | # environment variable is set; switch to anonymous mode otherwise. 24 | - run: 25 | name: Login to DockerHub 26 | command: > 27 | test -n "$DOCKER_USER" && 28 | echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin || 29 | echo "Docker Hub anonymous mode" 30 | 31 | # Skip release build & testing if changes are not targeting it 32 | - run: 33 | name: Check if changes are targeting the current release 34 | command: bin/ci checkpoint 35 | 36 | 37 | # Production image build 38 | - run: 39 | name: Build production image 40 | command: | 41 | source $(bin/ci activate_path) 42 | make build 43 | no_output_timeout: 30m 44 | 45 | # Development image build. It uses the "development" Dockerfile target 46 | # file 47 | - run: 48 | name: Build development image 49 | command: | 50 | source $(bin/ci activate_path) 51 | make dev-build 52 | 53 | # Bootstrap 54 | - run: 55 | name: Bootstrap the CMS & LMS 56 | command: | 57 | source $(bin/ci activate_path) 58 | make tree 59 | make migrate 60 | make run 61 | 62 | # Check that the production build starts 63 | - run: 64 | name: Check production build 65 | command: | 66 | source $(bin/ci activate_path) 67 | make test-cms 68 | make test-lms 69 | 70 | # Check that the demo course can be imported 71 | - run: 72 | name: Import demonstration course 73 | command: | 74 | source $(bin/ci activate_path) 75 | make demo-course 76 | 77 | # List openedx-docker jobs that will be integrated and executed in a workflow 78 | jobs: 79 | # Quality jobs 80 | 81 | # Check that the git history is clean and complies with our expectations 82 | lint-git: 83 | docker: 84 | - image: circleci/python:3.7-stretch 85 | auth: 86 | username: $DOCKER_USER 87 | password: $DOCKER_PASS 88 | working_directory: ~/fun 89 | steps: 90 | - checkout 91 | # Make sure the changes don't add a "print" statement to the code base. 92 | # We should exclude the ".circleci" folder from the search as the very command that checks 93 | # the absence of "print" is including a "print(" itself. 94 | - run: 95 | name: enforce absence of print statements in code 96 | command: | 97 | ! git diff origin/master..HEAD -- . ':(exclude).circleci' | grep "print(" 98 | - run: 99 | name: Check absence of fixup commits 100 | command: | 101 | ! git log | grep 'fixup!' 102 | - run: 103 | name: Install gitlint 104 | command: | 105 | pip install --user gitlint 106 | - run: 107 | name: lint commit messages added to master 108 | command: | 109 | ~/.local/bin/gitlint --commits origin/master..HEAD 110 | 111 | # Check that the CHANGELOG has been updated in the current branch 112 | check-changelog: 113 | docker: 114 | - image: circleci/buildpack-deps:stretch-scm 115 | auth: 116 | username: $DOCKER_USER 117 | password: $DOCKER_PASS 118 | working_directory: ~/fun 119 | steps: 120 | - checkout 121 | - run: 122 | name: Check that the CHANGELOG has been modified in the current branch 123 | command: | 124 | git whatchanged --name-only --pretty="" origin..HEAD | grep CHANGELOG 125 | 126 | # Check that the CHANGELOG max line length does not exceed 80 characters 127 | lint-changelog: 128 | docker: 129 | - image: debian:stretch 130 | auth: 131 | username: $DOCKER_USER 132 | password: $DOCKER_PASS 133 | working_directory: ~/fun 134 | steps: 135 | - checkout 136 | - run: 137 | name: Check CHANGELOG max line length 138 | command: | 139 | # Get the longuest line width (ignoring release links) 140 | test $(cat CHANGELOG.md | grep -Ev "^\[.*\]: https://github.com/openfun" | wc -L) -le 80 141 | 142 | check-configuration: 143 | <<: *defaults 144 | steps: 145 | - checkout 146 | - run: 147 | name: Check that the circle-ci config.yml file has been updated in the current branch 148 | command: bin/ci check_configuration 149 | 150 | # Build jobs 151 | # 152 | # Note that the job name should match the EDX_RELEASE value 153 | 154 | # Run jobs for the dogwood.3-fun release 155 | dogwood.3-fun: 156 | <<: [*defaults, *build_steps] 157 | # No changes detected for eucalyptus.3-wb 158 | # No changes detected for hawthorn.1-oee 159 | # No changes detected for ironwood.2-oee 160 | 161 | # Hub job 162 | hub: 163 | <<: *defaults 164 | 165 | steps: 166 | - checkout 167 | 168 | # Login to DockerHub with encrypted credentials stored as secret 169 | # environment variables (set in CircleCI project settings) 170 | - run: 171 | name: Login to DockerHub 172 | command: echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin 173 | 174 | # Thanks to docker layer caching, rebuilding the image should be blazing 175 | # fast! 176 | - run: 177 | name: Rebuild production image 178 | command: | 179 | source $(bin/ci activate_path) 180 | make build 181 | no_output_timeout: 30m 182 | 183 | # Tag images with our DockerHub namespace (fundocker/), and list images to 184 | # check that they have been properly tagged. 185 | - run: 186 | name: Tag production image 187 | command: | 188 | source $(bin/ci activate_path) 189 | docker tag edxapp:${EDX_RELEASE}-${FLAVOR} fundocker/edxapp:${CIRCLE_TAG} 190 | docker images fundocker/edxapp:${CIRCLE_TAG} 191 | 192 | - run: 193 | name: Tag nginx production images 194 | command: | 195 | source $(bin/ci activate_path) 196 | docker tag edxapp-nginx:${EDX_RELEASE}-${FLAVOR} fundocker/edxapp-nginx:${CIRCLE_TAG} 197 | docker images fundocker/edxapp-nginx:${CIRCLE_TAG} 198 | 199 | # Publish the production images to DockerHub 200 | - run: 201 | name: Publish production image 202 | command: | 203 | docker push fundocker/edxapp:${CIRCLE_TAG} 204 | docker push fundocker/edxapp-nginx:${CIRCLE_TAG} 205 | 206 | # CI workflows 207 | workflows: 208 | version: 2 209 | 210 | # We have a single workflow 211 | edxapp: 212 | jobs: 213 | # Quality 214 | - lint-git: 215 | filters: 216 | branches: 217 | ignore: master 218 | tags: 219 | ignore: /.*/ 220 | - check-changelog: 221 | filters: 222 | branches: 223 | ignore: master 224 | tags: 225 | ignore: /.*/ 226 | - lint-changelog: 227 | filters: 228 | branches: 229 | ignore: master 230 | tags: 231 | ignore: /.*/ 232 | - check-configuration: 233 | filters: 234 | branches: 235 | ignore: master 236 | tags: 237 | ignore: /.*/ 238 | 239 | # Build jobs 240 | 241 | # Run jobs for the dogwood.3-fun release 242 | - dogwood.3-fun: 243 | requires: 244 | - check-configuration 245 | filters: 246 | tags: 247 | ignore: /.*/ 248 | # No changes detected so no job to run for eucalyptus.3-wb 249 | # No changes detected so no job to run for hawthorn.1-oee 250 | # No changes detected so no job to run for ironwood.2-oee 251 | 252 | # We are pushing to Docker only images that are the result of a tag respecting the pattern: 253 | # **{branch-name}-x.y.z** 254 | # 255 | # Where branch-name is of the form: **{edx-version}[-{fork-name}]** 256 | # - **edx-version:** name of the upstream `edx-platform` version (e.g. ginkgo.1), 257 | # - **fork-name:** name of the specific project fork, if any (e.g. funwb). 258 | # 259 | # Some valid examples: 260 | # - dogwood.3-1.0.3 261 | # - dogwood.2-funmooc-17.6.1 262 | # - eucalyptus-funwb-2.3.19 263 | - hub: 264 | filters: 265 | branches: 266 | ignore: /.*/ 267 | tags: 268 | only: /^[a-z0-9.]*-?[a-z]*-(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/ 269 | -------------------------------------------------------------------------------- /.circleci/src/config.tpl: -------------------------------------------------------------------------------- 1 | # Open edX 2 | # Docker containers CI 3 | version: 2 4 | 5 | # Templates 6 | defaults: &defaults 7 | machine: 8 | image: default 9 | resource_class: large 10 | working_directory: ~/fun 11 | 12 | build_steps: &build_steps 13 | steps: 14 | # Checkout openedx-docker sources 15 | - checkout 16 | 17 | # Login to DockerHub with encrypted credentials stored as secret 18 | # environment variables (set in CircleCI project settings) if the expected 19 | # environment variable is set; switch to anonymous mode otherwise. 20 | - run: 21 | name: Login to DockerHub 22 | command: > 23 | test -n "$DOCKER_USER" && 24 | echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin || 25 | echo "Docker Hub anonymous mode" 26 | 27 | # Skip release build & testing if changes are not targeting it 28 | - run: 29 | name: Check if changes are targeting the current release 30 | command: bin/ci checkpoint 31 | 32 | 33 | # Production image build 34 | - run: 35 | name: Build production image 36 | command: | 37 | source $(bin/ci activate_path) 38 | make build 39 | no_output_timeout: 30m 40 | 41 | # Development image build. It uses the "development" Dockerfile target 42 | # file 43 | - run: 44 | name: Build development image 45 | command: | 46 | source $(bin/ci activate_path) 47 | make dev-build 48 | 49 | # Bootstrap 50 | - run: 51 | name: Bootstrap the CMS & LMS 52 | command: | 53 | source $(bin/ci activate_path) 54 | make tree 55 | make migrate 56 | make run 57 | 58 | # Check that the production build starts 59 | - run: 60 | name: Check production build 61 | command: | 62 | source $(bin/ci activate_path) 63 | make test-cms 64 | make test-lms 65 | 66 | # Check that the demo course can be imported 67 | - run: 68 | name: Import demonstration course 69 | command: | 70 | source $(bin/ci activate_path) 71 | make demo-course 72 | 73 | # List openedx-docker jobs that will be integrated and executed in a workflow 74 | jobs: 75 | # Quality jobs 76 | 77 | # Check that the git history is clean and complies with our expectations 78 | lint-git: 79 | docker: 80 | - image: circleci/python:3.7-stretch 81 | auth: 82 | username: $DOCKER_USER 83 | password: $DOCKER_PASS 84 | working_directory: ~/fun 85 | steps: 86 | - checkout 87 | # Make sure the changes don't add a "print" statement to the code base. 88 | # We should exclude the ".circleci" folder from the search as the very command that checks 89 | # the absence of "print" is including a "print(" itself. 90 | - run: 91 | name: enforce absence of print statements in code 92 | command: | 93 | ! git diff origin/master..HEAD -- . ':(exclude).circleci' | grep "print(" 94 | - run: 95 | name: Check absence of fixup commits 96 | command: | 97 | ! git log | grep 'fixup!' 98 | - run: 99 | name: Install gitlint 100 | command: | 101 | pip install --user gitlint 102 | - run: 103 | name: lint commit messages added to master 104 | command: | 105 | ~/.local/bin/gitlint --commits origin/master..HEAD 106 | 107 | # Check that the CHANGELOG has been updated in the current branch 108 | check-changelog: 109 | docker: 110 | - image: circleci/buildpack-deps:stretch-scm 111 | auth: 112 | username: $DOCKER_USER 113 | password: $DOCKER_PASS 114 | working_directory: ~/fun 115 | steps: 116 | - checkout 117 | - run: 118 | name: Check that the CHANGELOG has been modified in the current branch 119 | command: | 120 | git whatchanged --name-only --pretty="" origin..HEAD | grep CHANGELOG 121 | 122 | # Check that the CHANGELOG max line length does not exceed 80 characters 123 | lint-changelog: 124 | docker: 125 | - image: debian:stretch 126 | auth: 127 | username: $DOCKER_USER 128 | password: $DOCKER_PASS 129 | working_directory: ~/fun 130 | steps: 131 | - checkout 132 | - run: 133 | name: Check CHANGELOG max line length 134 | command: | 135 | # Get the longuest line width (ignoring release links) 136 | test $(cat CHANGELOG.md | grep -Ev "^\[.*\]: https://github.com/openfun" | wc -L) -le 80 137 | 138 | check-configuration: 139 | <<: *defaults 140 | steps: 141 | - checkout 142 | - run: 143 | name: Check that the circle-ci config.yml file has been updated in the current branch 144 | command: bin/ci check_configuration 145 | 146 | # Build jobs 147 | # 148 | # Note that the job name should match the EDX_RELEASE value 149 | ${JOBS_LIST} 150 | 151 | # Hub job 152 | hub: 153 | <<: *defaults 154 | 155 | steps: 156 | - checkout 157 | 158 | # Login to DockerHub with encrypted credentials stored as secret 159 | # environment variables (set in CircleCI project settings) 160 | - run: 161 | name: Login to DockerHub 162 | command: echo "$DOCKER_PASS" | docker login -u "$DOCKER_USER" --password-stdin 163 | 164 | # Thanks to docker layer caching, rebuilding the image should be blazing 165 | # fast! 166 | - run: 167 | name: Rebuild production image 168 | command: | 169 | source $(bin/ci activate_path) 170 | make build 171 | no_output_timeout: 30m 172 | 173 | # Tag images with our DockerHub namespace (fundocker/), and list images to 174 | # check that they have been properly tagged. 175 | - run: 176 | name: Tag production image 177 | command: | 178 | source $(bin/ci activate_path) 179 | docker tag edxapp:${EDX_RELEASE}-${FLAVOR} fundocker/edxapp:${CIRCLE_TAG} 180 | docker images fundocker/edxapp:${CIRCLE_TAG} 181 | 182 | - run: 183 | name: Tag nginx production images 184 | command: | 185 | source $(bin/ci activate_path) 186 | docker tag edxapp-nginx:${EDX_RELEASE}-${FLAVOR} fundocker/edxapp-nginx:${CIRCLE_TAG} 187 | docker images fundocker/edxapp-nginx:${CIRCLE_TAG} 188 | 189 | # Publish the production images to DockerHub 190 | - run: 191 | name: Publish production image 192 | command: | 193 | docker push fundocker/edxapp:${CIRCLE_TAG} 194 | docker push fundocker/edxapp-nginx:${CIRCLE_TAG} 195 | 196 | # CI workflows 197 | workflows: 198 | version: 2 199 | 200 | # We have a single workflow 201 | edxapp: 202 | jobs: 203 | # Quality 204 | - lint-git: 205 | filters: 206 | branches: 207 | ignore: master 208 | tags: 209 | ignore: /.*/ 210 | - check-changelog: 211 | filters: 212 | branches: 213 | ignore: master 214 | tags: 215 | ignore: /.*/ 216 | - lint-changelog: 217 | filters: 218 | branches: 219 | ignore: master 220 | tags: 221 | ignore: /.*/ 222 | - check-configuration: 223 | filters: 224 | branches: 225 | ignore: master 226 | tags: 227 | ignore: /.*/ 228 | 229 | # Build jobs 230 | ${WORKFLOW_JOBS_LIST} 231 | 232 | # We are pushing to Docker only images that are the result of a tag respecting the pattern: 233 | # **{branch-name}-x.y.z** 234 | # 235 | # Where branch-name is of the form: **{edx-version}[-{fork-name}]** 236 | # - **edx-version:** name of the upstream `edx-platform` version (e.g. ginkgo.1), 237 | # - **fork-name:** name of the specific project fork, if any (e.g. funwb). 238 | # 239 | # Some valid examples: 240 | # - dogwood.3-1.0.3 241 | # - dogwood.2-funmooc-17.6.1 242 | # - eucalyptus-funwb-2.3.19 243 | - hub: 244 | filters: 245 | branches: 246 | ignore: /.*/ 247 | tags: 248 | only: /^[a-z0-9.]*-?[a-z]*-(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/ 249 | -------------------------------------------------------------------------------- /.circleci/src/jobs/release.yml.tpl: -------------------------------------------------------------------------------- 1 | # Run jobs for the ${RELEASE} release 2 | ${RELEASE}: 3 | <<: [*defaults, *build_steps] 4 | -------------------------------------------------------------------------------- /.circleci/src/jobs/release_no_change.yml.tpl: -------------------------------------------------------------------------------- 1 | # No changes detected for ${RELEASE} 2 | -------------------------------------------------------------------------------- /.circleci/src/workflows/edxapp/release_jobs.yml.tpl: -------------------------------------------------------------------------------- 1 | # Run jobs for the ${RELEASE} release 2 | - ${RELEASE}: 3 | requires: 4 | - check-configuration 5 | filters: 6 | tags: 7 | ignore: /.*/ 8 | -------------------------------------------------------------------------------- /.circleci/src/workflows/edxapp/release_no_change.yml.tpl: -------------------------------------------------------------------------------- 1 | # No changes detected so no job to run for ${RELEASE} 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | releases/**/data/ 2 | releases/**/src/ 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Expected Behavior 2 | 3 | Description... 4 | 5 | 6 | ## Actual Behavior 7 | 8 | Description... 9 | 10 | 11 | ## Steps to Reproduce 12 | 13 | Description... 14 | 15 | 1. item 1... 16 | 2. item 2... 17 | 18 | 19 | ## Specifications 20 | 21 | - Version: ... 22 | - Platform: ... 23 | 24 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | 3 | Description... 4 | 5 | 6 | ## Proposal 7 | 8 | Description... 9 | 10 | - [] item 1... 11 | - [] item 2... 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # env 2 | .env 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | 8 | # Site media 9 | data/ 10 | 11 | # Sources 12 | src/ 13 | 14 | # Unit test / coverage reports 15 | htmlcov/ 16 | .coverage 17 | .cache 18 | nosetests.xml 19 | coverage.xml 20 | 21 | # Translations 22 | *.mo 23 | 24 | # Vagrant 25 | .vagrant 26 | 27 | # PyCharm IDE 28 | .idea/ 29 | 30 | # Mr Developer 31 | .mr.developer.cfg 32 | .project 33 | .pydevproject 34 | 35 | # Eclipse 36 | .settings/ 37 | 38 | # Rope 39 | .ropeproject 40 | 41 | # Sublime-Text: 42 | /*.sublime-project 43 | /*.sublime-workspace 44 | 45 | # TextMate 2 46 | .tm_properties 47 | 48 | # VS Code 49 | .vscode 50 | 51 | # Mac Os 52 | .DS_Store 53 | 54 | # Temp files 55 | *~ 56 | .~lock* 57 | 58 | # Swap files 59 | *.sw[po] 60 | 61 | # Development nginx conf 62 | docker/files/etc/nginx/ssl/* 63 | !docker/files/etc/nginx/ssl/ssl.conf.tpl 64 | -------------------------------------------------------------------------------- /.gitlint: -------------------------------------------------------------------------------- 1 | # All these sections are optional, edit this file as you like. 2 | [general] 3 | # Ignore certain rules, you can reference them by their id or by their full name 4 | # ignore=title-trailing-punctuation, T3 5 | 6 | # verbosity should be a value between 1 and 3, the commandline -v flags take precedence over this 7 | # verbosity = 2 8 | 9 | # By default gitlint will ignore merge commits. Set to 'false' to disable. 10 | # ignore-merge-commits=true 11 | 12 | # By default gitlint will ignore fixup commits. Set to 'false' to disable. 13 | # ignore-fixup-commits=true 14 | 15 | # By default gitlint will ignore squash commits. Set to 'false' to disable. 16 | # ignore-squash-commits=true 17 | 18 | # Enable debug mode (prints more output). Disabled by default. 19 | # debug=true 20 | 21 | # Set the extra-path where gitlint will search for user defined rules 22 | # See http://jorisroovers.github.io/gitlint/user_defined_rules for details 23 | extra-path=gitlint/ 24 | 25 | # [title-max-length] 26 | # line-length=80 27 | 28 | [title-must-not-contain-word] 29 | # Comma-separated list of words that should not occur in the title. Matching is case 30 | # insensitive. It's fine if the keyword occurs as part of a larger word (so "WIPING" 31 | # will not cause a violation, but "WIP: my title" will. 32 | words=wip 33 | 34 | #[title-match-regex] 35 | # python like regex (https://docs.python.org/2/library/re.html) that the 36 | # commit-msg title must be matched to. 37 | # Note that the regex can contradict with other rules if not used correctly 38 | # (e.g. title-must-not-contain-word). 39 | #regex= 40 | 41 | # [B1] 42 | # B1 = body-max-line-length 43 | # line-length=120 44 | # [body-min-length] 45 | # min-length=5 46 | 47 | # [body-is-missing] 48 | # Whether to ignore this rule on merge commits (which typically only have a title) 49 | # default = True 50 | # ignore-merge-commits=false 51 | 52 | # [body-changed-file-mention] 53 | # List of files that need to be explicitly mentioned in the body when they are changed 54 | # This is useful for when developers often erroneously edit certain files or git submodules. 55 | # By specifying this rule, developers can only change the file when they explicitly reference 56 | # it in the commit message. 57 | # files=gitlint/rules.py,README.md 58 | 59 | # [author-valid-email] 60 | # python like regex (https://docs.python.org/2/library/re.html) that the 61 | # commit author email address should be matched to 62 | # For example, use the following regex if you only want to allow email addresses from foo.com 63 | # regex=[^@]+@foo.com 64 | 65 | [ignore-by-title] 66 | # Allow empty body & wrong title pattern only when bots (pyup/greenkeeper) 67 | # upgrade dependencies 68 | regex=^(⬆️.*|Update (.*) from (.*) to (.*)|(chore|fix)\(package\): update .*)$ 69 | ignore=B6,UC1 70 | 71 | # [ignore-by-body] 72 | # Ignore certain rules for commits of which the body has a line that matches a regex 73 | # E.g. Match bodies that have a line that that contain "release" 74 | # regex=(.*)release(.*) 75 | # 76 | # Ignore certain rules, you can reference them by their id or by their full name 77 | # Use 'all' to ignore all rules 78 | # ignore=T1,body-min-length 79 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | As the tooling part of this repository is not intended to get released, all 4 | notable changes to this project will be documented in each flavored OpenEdx 5 | release directory. 6 | 7 | You can list (and possibly read) them using the following shell command: 8 | 9 | ```bash 10 | $ find releases -maxdepth 4 -name CHANGELOG.md 11 | ``` 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Open edX Docker 2 | 3 | France Université Numérique introduces an alternative `Docker` approach to 4 | install a complete and customized version of [Open edX](https://open.edx.org). 5 | 6 | The idea is to handcraft a `Dockerfile`, in order to make the project simpler, 7 | more flexible and fully operable by developers. 8 | 9 | ## Quick preview 10 | 11 | If you're looking for a quick preview of OpenEdX Docker, you can take a look 12 | at our dedicated [demo site](https://demo.richie.education). 13 | 14 | It is connected back-to-back with a demo of Richie, 15 | [a CMS for Open Education](https://richie.education) based on Django. 16 | 17 | Two users are available for testing: 18 | 19 | - admin: `admin@example.com`/`admin` 20 | - student: `edx@example.com`/`edx` 21 | 22 | Richie's admin is available at https://demo.richie.education/admin/ and can be 23 | accessed via the following user account: `admin`/`admin`. 24 | 25 | The demonstration databases are regularly flushed. 26 | 27 | ## Approach 28 | 29 | This project builds a docker image that is ready for production. 30 | 31 | At France Université Numérique, we are deploying Open edX Docker to OpenShift, 32 | for many customers and in multiple environments using 33 | [Arnold](https://github.com/openfun/arnold): 34 | 35 | - The Open edX settings were polished to unlock powerful configuration 36 | management features: sensible defaults, flexible overrides using YAML files 37 | and/or environment variables, secure credentials using Ansible Vault and PGP 38 | keys, 39 | - We focused on a best practice installation of `edxapp`, the core Open edX 40 | application. You can build you own image by adding specific Xblocks or Django 41 | apps in a `Dockerfile` inheriting from this one (see 42 | https://github.com/openfun/fonzie for an example). 43 | 44 | Docker compose is only used for development purposes so that we can code locally 45 | and see our changes immediately: 46 | 47 | - sources and configuration files are mounted from the host, 48 | - the Docker CMD launches Django's development server instead of `gunicorn`, 49 | - ports are opened on the application containers to allow bypassing `nginx`. 50 | 51 | Docker compose also allows us to run a complete project in development, 52 | including database services which in production are not run on Docker. See the 53 | [docker-compose file](./docker-compose.yml) for details on each service: 54 | 55 | - **mysql:** the SQL database used to structure and persist the application 56 | data, 57 | - **mongodb:** the no-SQL database used to store course content, 58 | - **memcached:** the cache engine, 59 | - **lms:** the Django web application used by learners, 60 | - **cms:** the Django web application used by teachers, 61 | - **nginx:** the front end web server configured to serve static/media files and 62 | proxy other requests to Django. 63 | - **mailcatcher** the email backend 64 | 65 | Concerning Redis, it is possible to run a single redis instance (the default choice) 66 | or to run redis with sentinel to simulate a HA instance. 67 | To use Redis sentinel you have to set the `REDIS_SERVICE` environment variable 68 | to `redis-sentinel`: 69 | 70 | ```bash 71 | $ export REDIS_SERVICE=redis-sentinel 72 | ``` 73 | 74 | To switch back to the single redis instance, unset this environment variable: 75 | 76 | ```bash 77 | $ unset REDIS_SERVICE 78 | ``` 79 | 80 | ## Prerequisite 81 | 82 | Make sure you have a recent version of [Docker](https://docs.docker.com/install) 83 | and [Docker Compose](https://docs.docker.com/compose/install) installed on your 84 | laptop: 85 | 86 | ```bash 87 | $ docker -v 88 | Docker version 26.0.0, build 2ae903e 89 | 90 | $ docker compose version 91 | docker compose version v2.24.5 92 | ``` 93 | 94 | ## Getting started 95 | 96 | First, you need to choose a release/flavor of OpenEdx versions we support. You 97 | can list them and get instructions about how to select/activate a target release 98 | using the `bin/activate` script. An example output follows: 99 | 100 | ```bash 101 | $ bin/activate 102 | Select an available release to activate: 103 | [1] master/0/bare (default) 104 | [2] hawthorn/1/bare 105 | [3] hawthorn/1/oee 106 | Your choice: 3 107 | 108 | # Copy/paste hawthorn/1/oee environment: 109 | export EDX_RELEASE="hawthorn.1" 110 | export FLAVOR="oee" 111 | export EDX_RELEASE_REF="open-release/hawthorn.1" 112 | export EDX_DEMO_RELEASE_REF="open-release/hawthorn.1" 113 | 114 | # Or run the following command: 115 | . ${HOME}/Work/openedx-docker/releases/hawthorn/1/oee/activate 116 | 117 | # Check your environment with: 118 | make info 119 | ``` 120 | 121 | Once your environment is set, start the full project by running: 122 | 123 | ```bash 124 | $ make bootstrap 125 | ``` 126 | 127 | You should now be able to view the web applications: 128 | 129 | - LMS served by `nginx` at: [http://localhost:8073](http://localhost:8073) 130 | - CMS served by `nginx` at: [http://localhost:8083](http://localhost:8083) 131 | 132 | See other available commands by running: 133 | 134 | ```bash 135 | $ make --help 136 | ``` 137 | 138 | ## Developer guide 139 | 140 | If you intend to work on edx-platform or its configuration, you'll need to 141 | compile static files in local directories that are mounted as docker volumes in 142 | the target container: 143 | 144 | ```bash 145 | $ make dev-assets 146 | ``` 147 | 148 | Now you can start services development server _via_: 149 | 150 | ```bash 151 | $ make dev 152 | ``` 153 | 154 | You should be able to view the web applications: 155 | 156 | - LMS served by Django's development server at: 157 | [http://localhost:8072](http://localhost:8072) 158 | - CMS served by Django's development server at: 159 | [http://localhost:8082](http://localhost:8082) 160 | 161 | ### Hacking with themes 162 | 163 | To work on a particular theme, we invite you to use the `paver watch_assets` 164 | command; _e.g._: 165 | 166 | ```bash 167 | $ make dev-watch 168 | ``` 169 | 170 | **Troubleshooting**: if the command above raises the following error: 171 | 172 | ``` 173 | OSError: inotify watch limit reached 174 | ``` 175 | 176 | Then you will need to increase the **host**'s `fs.inotify.max_user_watches` 177 | kernel setting (for reference, see https://unix.stackexchange.com/a/13757): 178 | 179 | ```ini 180 | # /etc/sysctl.conf (debian based) 181 | fs.inotify.max_user_watches=524288 182 | ``` 183 | 184 | ## Available Docker images 185 | 186 | The aim of this project is to prepare several flavors of images, the docker 187 | files and settings of which are living in their own directory (see 188 | [`releases/`](./releases/)) 189 | 190 | Release paths on the current repository are of the form: 191 | 192 | ``` 193 | {release name}/{release number}/{flavor} 194 | ``` 195 | 196 | With: 197 | 198 | - **release name**: OpenEdx release name (_e.g._ `hawthorn`) 199 | - **release number**: OpenEdx release number (_e.g._ `1`) 200 | - **flavor**: the release flavor (_e.g._ `bare` for standard OpenEdx release and 201 | `oee` for OpenEdx Extended release). 202 | 203 | We are pushing to `DockerHub` only images that are the result of a tag 204 | respecting the following pattern: 205 | 206 | ``` 207 | {release}(-{flavor})-x.y.z 208 | ``` 209 | 210 | The `release` (_e.g._ `hawthorn.1`) typically includes the `release name` 211 | (_e.g._ `hawthorn`) and the `release number` (_e.g._ `1`). The flavor is 212 | optional. 213 | 214 | Here are some valid examples: 215 | 216 | - `dogwood.3-1.0.3` 217 | - `hawthorn.1-oee-2.0.1` 218 | 219 | ## Nginx 220 | 221 | This project also provides an nginx companion image that can be used 222 | alongside the `edxapp` image for faster deployments and better performance. 223 | 224 | The classical way to handle and serve static files in a Django application is 225 | to collect them (using the `collectstatic` management command) and post-process 226 | them using an appropriate storage back-end that uses cache-busting techniques 227 | to avoid old _versus_ new static files collisions (_e.g._ 228 | `ManifestStaticFilesStorage`). 229 | 230 | Depending on the `edx-platform` release, some files may not benefit from a 231 | cache busting md5 hash, leading to unexpected side effects during deployment. 232 | 233 | To prevent such behavior, for each new `openedx-docker` release, we will also 234 | release an 235 | [`edxapp-nginx`](https://hub.docker.com/repository/docker/fundocker/edxapp-nginx/) 236 | image with the same Docker tag. This image is an [OpenShift-ready `nginx` 237 | image](https://github.com/openfun/openshift-docker#nginx) with embedded 238 | `edxapp`'s static files. You are supposed to use this image to serve your 239 | static files, media and reverse proxy to the version-matching `edxapp` instance. 240 | 241 | ## Alternative projects 242 | 243 | If what you're looking for is a quick 1-click installation of the complete Open 244 | edX stack, you may take a look at [Tutor](https://github.com/overhangio/tutor). 245 | 246 | ## License 247 | 248 | The code in this repository is licensed under the GNU AGPL-3.0 terms unless 249 | otherwise noted. 250 | 251 | Please see [`LICENSE`](./LICENSE) for details. 252 | -------------------------------------------------------------------------------- /bin/_config.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | PROJECT_DIRECTORY=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/.. 4 | RELEASES_DIRECTORY="releases" 5 | NGINX_CONF_DIRECTORY="docker/files/etc/nginx/conf.d" 6 | 7 | export PROJECT_DIRECTORY 8 | export RELEASES_DIRECTORY 9 | -------------------------------------------------------------------------------- /bin/activate: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | declare ENV_FILENAME 4 | declare PROJECT_DIRECTORY 5 | declare RELEASES_ROOT 6 | 7 | ENV_FILENAME="activate" 8 | PROJECT_DIRECTORY="$(dirname "${BASH_SOURCE[0]}")/.." 9 | RELEASES_ROOT="${PROJECT_DIRECTORY}/releases" 10 | 11 | 12 | function select_release(){ 13 | 14 | declare prompt 15 | declare release 16 | declare -a releases 17 | declare -i default=1 18 | declare -i choice 19 | declare -i n_releases 20 | 21 | # List releases by looking for Dockerfiles in the "releases" directory and 22 | # store them in the releases array 23 | read -r -a releases <<< $( 24 | find "${PROJECT_DIRECTORY}/releases/" -maxdepth 4 -name Dockerfile | 25 | sed "s|${PROJECT_DIRECTORY}/releases/\\(.*\\)/Dockerfile|\\1|g" | 26 | sort -fir | 27 | xargs 28 | ) 29 | n_releases=${#releases[@]} 30 | 31 | prompt="Select an available flavored release to activate:\\n" 32 | for (( i=0; i&2 echo "Invalid choice ${choice} (should be <= ${n_releases})") 44 | exit 10 45 | fi 46 | 47 | if [[ ${choice} -le 0 ]]; then 48 | choice=${default} 49 | fi 50 | 51 | release="${releases[$((choice-1))]}" 52 | echo "${release}" 53 | } 54 | 55 | 56 | function activate_release(){ 57 | 58 | declare release="$1" 59 | declare -i is_sourced="$2" 60 | 61 | release_path="${RELEASES_ROOT}/${release}" 62 | env_file_path="${release_path}/${ENV_FILENAME}" 63 | 64 | if [[ ! -d ${release_path} ]]; then 65 | (>&2 echo "Release path ${release_path} doesn't exists!") 66 | exit 20 67 | fi 68 | 69 | if [[ ! -e ${env_file_path} ]]; then 70 | (>&2 echo "Environment file (${ENV_FILENAME}) not found for this release!") 71 | exit 21 72 | fi 73 | 74 | if [[ ${is_sourced} -eq 1 ]]; then 75 | source ${env_file_path} 76 | echo -e "\\n# Following variables are now exported:" 77 | cat "${env_file_path}" 78 | else 79 | echo -e "\\n# Copy/paste ${release} environment:" 80 | cat "${env_file_path}" 81 | 82 | echo -e "\\n# Or run the following command:" 83 | echo -e ". ${env_file_path}" 84 | fi 85 | 86 | echo -e "\\n# You can check your environment with:" 87 | echo -e "make info" 88 | } 89 | 90 | release=$(select_release) 91 | (return 2> /dev/null) && sourced=1 || sourced=0 92 | activate_release "${release}" "${sourced}" 93 | -------------------------------------------------------------------------------- /bin/ci: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | # shellcheck source=bin/_config.sh 6 | source "$(dirname "${BASH_SOURCE[0]}")/_config.sh" 7 | 8 | # usage: display usage with the appropriate exit code 9 | # 10 | # usage: usage [EXIT_CODE] 11 | # 12 | # EXIT_CODE: program exit code (default: 0) 13 | function usage(){ 14 | 15 | declare -i exit_code="${1:-0}" 16 | 17 | echo "Usage: bin/ci COMMAND 18 | 19 | Available commands: 20 | 21 | activate_path display CIRCLE_TAG (or CIRCLE_JOB) release activate script path 22 | checkpoint skip release job if changes are not targeting the current release 23 | get_changes get a list of changed files in the current branch compared to master 24 | update update the circle-ci config.yml file 25 | check_configuration check that the circle-ci config.yml reflect current changes 26 | " 27 | 28 | # shellcheck disable=SC2086 29 | exit ${exit_code} 30 | } 31 | 32 | # Get active release activation path given the CIRCLE_TAG or CIRCLE_JOB 33 | # environment variable. If both are defined the CIRCLE_TAG variable will 34 | # prevail. 35 | function activate_path(){ 36 | 37 | declare reference 38 | declare release 39 | 40 | reference=${CIRCLE_TAG:-${CIRCLE_JOB}} 41 | if [[ -z ${reference} ]]; then 42 | (>&2 echo "CIRCLE_TAG or CIRCLE_JOB environment variable should be defined!") 43 | exit 20 44 | fi 45 | 46 | # We need to convert the reference (_e.g._ something like hawthorn.1-1.0.3 or dogwood.3-fun) 47 | # to a flavored release path (e.g. something like hawthorn/1/bare or dogwood/3/fun for the 48 | # later examples). In the following, we have a three-steps pipeline 49 | # to do so: i. get the release name, number and optionally a flavor from the 50 | # reference, ii. in case of empty release number and/or flavor, our sed 51 | # substitution will generate duplicated slashes that should be fixed, and 52 | # iii. the default flavor (empty third group from our regular expression) 53 | # should be named "bare". 54 | release=$(\ 55 | echo "${reference}" | \ 56 | sed -E 's|^([a-z]*)\.?([0-9]*)-?([a-z]*)?(-[0-9.]+)?$|\1/\2/\3|g' | \ 57 | sed -E 's|//|/|g' | \ 58 | sed -E 's|/$|/bare|g' 59 | ) 60 | 61 | echo "releases/${release}/activate" 62 | } 63 | 64 | # Skip release job if changes are not targeting the current release 65 | function checkpoint(){ 66 | 67 | changes=$(get_changes) 68 | 69 | if echo "${changes}" | grep -v "releases/" > /dev/null ; then 70 | (>&2 echo "Current work scope is global, all releases should build.") 71 | exit 0 72 | fi 73 | 74 | release_path=$(activate_path | sed -E 's|/activate$||') 75 | 76 | if ! echo "${changes}" | grep "${release_path}" > /dev/null ; then 77 | (>&2 echo "Skipping release (out of scope).") 78 | circleci-agent step halt 79 | fi 80 | } 81 | 82 | # Get a list of changed files in the current branch 83 | function get_changes() { 84 | git whatchanged --name-only --pretty="" "origin/master..HEAD" | sort -u 85 | } 86 | 87 | 88 | function check_changes() { 89 | to="${CIRCLE_SHA1:-HEAD}" 90 | git whatchanged --name-only --pretty="" origin/master.."${to}" | grep "${1}" &> /dev/null 91 | } 92 | 93 | 94 | # Utility function that updates circle configuration source files with a 95 | # workflow for each site and running only the necessary jobs 96 | # 97 | # Usage: update_sources 98 | function update_sources() { 99 | 100 | # Add a workflow for the release from our template 101 | echo "" > ".circleci/src/workflows/edxapp/@jobs.yml" 102 | echo "" > ".circleci/src/jobs/@jobs.yml" 103 | 104 | while IFS= read -r -d '' release_path 105 | do 106 | # Extract release name from path 107 | release="${release_path//${RELEASES_DIRECTORY}\//}" 108 | # Replace first slash of release name with a . and second slash with a - 109 | release=$(echo ${release} | sed 's/\//\./' | sed 's/\//-/') 110 | 111 | # Conditions to run a release's workflow 112 | if \ 113 | check_changes "^docker/$" || \ 114 | check_changes "^${release_path}/config/" || \ 115 | check_changes "^${release_path}/activate" || \ 116 | check_changes "^${release_path}/Dockerfile" || \ 117 | check_changes "^${release_path}/requirements.txt" \ 118 | check_changes "^${release_path}/entrypoint.sh" 119 | then 120 | workflow_template=".circleci/src/workflows/edxapp/release_jobs.yml.tpl" 121 | job_template=".circleci/src/jobs/release.yml.tpl" 122 | else 123 | workflow_template=".circleci/src/workflows/edxapp/release_no_change.yml.tpl" 124 | job_template=".circleci/src/jobs/release_no_change.yml.tpl" 125 | fi 126 | 127 | # shellcheck disable=SC2016 128 | sed 's|${RELEASE}|'"${release}"'|g;' < "${workflow_template}" >> ".circleci/src/workflows/edxapp/@jobs.yml" 129 | sed 's|${RELEASE}|'"${release}"'|g;' < "${job_template}" >> ".circleci/src/jobs/@jobs.yml" 130 | 131 | done < <(find "releases" -maxdepth 3 -mindepth 3 -type d -print0 | sort -z) 132 | 133 | # Replace in config.tpl 134 | sed -e '/${WORKFLOW_JOBS_LIST}/ {' -e 'r .circleci/src/workflows/edxapp/@jobs.yml' -e 'd' -e '}' .circleci/src/config.tpl > .circleci/config.yml.part1 135 | sed -e '/${JOBS_LIST}/ {' -e 'r .circleci/src/jobs/@jobs.yml' -e 'd' -e '}' .circleci/config.yml.part1 > .circleci/config.yml.part2 136 | echo -e "# THIS FILE WAS AUTOMATICALLY GENERATED.\n# Please edit src/config.tpl file instead.\n\n" > .circleci/config.yml 137 | cat .circleci/config.yml.part2 >> .circleci/config.yml 138 | rm .circleci/config.yml.part1 139 | rm .circleci/config.yml.part2 140 | } 141 | 142 | 143 | # Utility function that generates a circle configuration file with a workflow 144 | # for each site and running only the necessary jobs 145 | # 146 | # Usage: update 147 | function update() { 148 | update_sources 149 | 150 | # Check config validity 151 | docker run --rm -v "${PROJECT_DIRECTORY}:/data" circleci/circleci-cli:alpine \ 152 | config validate /data/.circleci/config.yml 153 | } 154 | 155 | 156 | # Utility function that checks if the circle configuration committed to git 157 | # corresponds to the state of the project so that the workflows and jobs are 158 | # run according to what has changed as compared to the "master" branch. 159 | # 160 | # Usage: check_configuration 161 | function check_configuration() { 162 | update 163 | if git diff --name-only | grep "^.circleci/"; then 164 | echo "check: the .circleci configuration does not reflect latest changes." 165 | exit 1 166 | fi 167 | echo "All good" 168 | exit 0 169 | } 170 | 171 | 172 | # ---- Main ---- 173 | 174 | # Check if this script is being sourced or executed. Explanation: Bash allows 175 | # return statements only from functions and, in a script's top-level scope, only 176 | # if the script is sourced. 177 | (return 2> /dev/null) && sourced=1 || sourced=0 178 | 179 | if [[ ${sourced} == 0 ]]; then 180 | 181 | action="${1:-usage}" 182 | 183 | # Remove current action from arguments array 184 | if [[ -n "${1}" ]]; then 185 | shift 186 | fi 187 | 188 | "$action" "$@" 189 | fi 190 | -------------------------------------------------------------------------------- /bin/compose: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | DOCKER_UID=$(id -u) 4 | DOCKER_GID=$(id -g) 5 | EDXAPP_IMAGE_TAG="${EDX_RELEASE:-dogwood.3}-${FLAVOR:-fun}" 6 | FLAVORED_EDX_RELEASE_PATH="releases/$(echo ${EDX_RELEASE:-dogwood.3} | sed -E "s|\.|/|")/${FLAVOR:-bare}" 7 | 8 | export DOCKER_UID 9 | export DOCKER_GID 10 | export EDXAPP_IMAGE_TAG 11 | export FLAVORED_EDX_RELEASE_PATH 12 | 13 | docker compose "${@}" 14 | -------------------------------------------------------------------------------- /bin/deactivate: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | unset EDX_RELEASE 4 | unset FLAVOR 5 | unset EDX_RELEASE_REF 6 | unset EDX_DEMO_RELEASE_REF 7 | 8 | # Check if this script is being sourced or executed. Explanation: Bash allows 9 | # return statements only from functions and, in a script's top-level scope, only 10 | # if the script is sourced. 11 | (return 2> /dev/null) && sourced=1 || sourced=0 12 | 13 | if [[ ${sourced} == 0 ]]; then 14 | # Terminal colors 15 | declare -r COLOR_ERROR='\033[0;31m' 16 | declare -r COLOR_RESET='\033[0m' 17 | 18 | echo -e "${COLOR_ERROR}ERROR: this script has no effect if not sourced.${COLOR_RESET}" 1>&2 19 | echo -e "Use the following shell command instead:\\n\\n . bin/activate\\n" 1>&2 20 | exit 10 21 | fi 22 | -------------------------------------------------------------------------------- /bin/setup-ssl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | COLOR_INFO="\033[1;34m" 5 | COLOR_RESET="\033[0m" 6 | COLOR_WARNING="\033[0;33m" 7 | COLOR_SUCCESS="\033[0;32m" 8 | 9 | # shellcheck source=bin/_config.sh 10 | source "$(dirname "${BASH_SOURCE[0]}")/_config.sh" 11 | 12 | function usage_setup() { 13 | echo -e "Usage: ${0} ${1} [OPTIONS] COMMAND 14 | 15 | Generate certificate and its key (except if you specified you do not want with --no-cert option) 16 | then generate Nginx SSL configuration file. 17 | 18 | OPTIONS: 19 | -nc, --no-cert skip certificate generation with mkcert 20 | -h, --help print this message 21 | 22 | COMMAND: 23 | help print this message 24 | " 25 | } 26 | 27 | # Utility function to generate nginx configuration to reverse proxy OpenEdx apps over https 28 | with_cert=true 29 | 30 | while true; do 31 | case "${1}" in 32 | -nc|--no-cert) 33 | with_cert=false 34 | break 35 | ;; 36 | -h | --help | help) 37 | usage_setup "${FUNCNAME[0]}" 38 | exit 0 39 | ;; 40 | *) 41 | break 42 | ;; 43 | esac 44 | done 45 | 46 | # Check if nginx ssl conf already exist and if certificate exists and is still valid 47 | if [ -f "docker/files/etc/nginx/ssl/cms.conf" ] && 48 | [ -f "docker/files/etc/nginx/ssl/lms.conf" ] && 49 | [ -f "docker/files/etc/nginx/ssl/edx.local.dev.key" ] && 50 | [ -f "docker/files/etc/nginx/ssl/edx.local.dev.pem" ] && 51 | openssl x509 -checkend 0 -noout -in "docker/files/etc/nginx/ssl/edx.local.dev.pem" ; 52 | then 53 | echo -e "${COLOR_SUCCESS}✅ SSL configuration is already enabled and certificate is valid.${COLOR_RESET}\\n" 54 | else 55 | if [[ "$with_cert" = true ]] ; then 56 | echo -e "${COLOR_INFO}> Generating certificate for edx.local.dev domain${COLOR_RESET}" 57 | # Generate fresh key and certificate files for edx.local.dev domain 58 | mkcert -key-file "docker/files/etc/nginx/ssl/edx.local.dev.key" \ 59 | -cert-file "docker/files/etc/nginx/ssl/edx.local.dev.pem" \ 60 | edx.local.dev 61 | else 62 | echo -e "${COLOR_INFO}> Certificate generation skipped.${COLOR_RESET}" 63 | echo -e "\\n${COLOR_WARNING}⚠️ A certificate is required! 64 | Skipping generation supposes that you generated your certificate and its key manually, 65 | at the following locations 66 | - docker/files/etc/nginx/ssl/edx.local.dev.key 67 | - docker/files/etc/nginx/ssl/edx.local.dev.pem 68 | ${COLOR_RESET}" 69 | fi 70 | 71 | echo -e "${COLOR_INFO}> Generating nginx configuration${COLOR_RESET}" 72 | 73 | ssl_template="docker/files/etc/nginx/ssl/ssl.conf.tpl" 74 | disclaimer="# /!\ DO NOT EDIT: this file is autogenerated" 75 | 76 | while read -r -d '' nginx_conf 77 | do 78 | # extract nginx conf name 79 | conf_filename="${nginx_conf//${NGINX_CONF_DIRECTORY}\//}" 80 | printf "%s\n" "${disclaimer}" > "docker/files/etc/nginx/ssl/${conf_filename}" 81 | 82 | # Search listen line then insert ssl between port number and ; 83 | # then append ssl.conf.tpl content after these line. 84 | # finally update permanent redirect on https://richie.local.dev:8070 85 | sed "s|^\(.*listen.*\)\(;\)$|\1 ssl\2|; 86 | s|http://localhost:8070|https://richie.local.dev:8070|g; 87 | /listen.*;$/r $ssl_template 88 | " \ 89 | < "$nginx_conf" \ 90 | >> "docker/files/etc/nginx/ssl/${conf_filename}" 91 | 92 | done < <(find "docker/files/etc/nginx/conf.d" -maxdepth 1 -mindepth 1 -type f -print0) 93 | echo -e "\\n✅ SSL is ready to use!\\n" 94 | fi 95 | 96 | echo -e "\\n${COLOR_INFO}> Starting SSL apps${COLOR_RESET}" 97 | make run-ssl 98 | echo -e "\\n${COLOR_SUCCESS}✅ OpenEdx is up and running on https://edx.local.dev:8073 !" 99 | echo -e "\\n${COLOR_WARNING}Next time, just use the command below to start OpenEdx over ssl" 100 | echo -e "> make run-ssl\\n" 101 | 102 | exit 0 103 | -------------------------------------------------------------------------------- /bin/watch: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | THEME="${1:-red-theme}" 4 | 5 | docker compose run --rm --no-deps lms-dev \ 6 | paver watch_assets \ 7 | --settings=fun.docker_build_development \ 8 | --theme-dirs=/edx/app/edxapp/edx-platform/themes \ 9 | --themes="${THEME}" 10 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # OpenEdx docker compose stack used for development/test purpose 2 | # 3 | # 4 | # 5 | # Nota bene: this docker-compose file requires that you have set the DOCKER_UID 6 | # and DOCKER_GID variables with relevant values (i.e. matching the current 7 | # system user ids). If you use the project's Makefile or the bin/compose 8 | # wrapper, those variable definitions have already been handled on your behalf. 9 | 10 | services: 11 | mysql: 12 | image: mysql:5.6 13 | platform: linux/x86_64 14 | ports: 15 | - "3316:3306" 16 | env_file: env.d/development 17 | command: mysqld --character-set-server=utf8 --collation-server=utf8_general_ci 18 | 19 | mongodb: 20 | image: mongo:3.2 21 | platform: linux/x86_64 22 | # We use WiredTiger in all environments. In development environments we use small files 23 | # to conserve disk space, and disable the journal for a minor performance gain. 24 | # See https://docs.mongodb.com/v3.0/reference/program/mongod/#options for complete details. 25 | command: mongod --smallfiles --nojournal --storageEngine wiredTiger 26 | 27 | memcached: 28 | image: memcached:1.4 29 | 30 | redis: 31 | image: redis:4.0.10-alpine 32 | 33 | redis-master: 34 | image: redis:4.0.10-alpine 35 | 36 | redis-slave: 37 | image: redis:4.0.10-alpine 38 | command: redis-server --slaveof redis-master 6379 39 | depends_on: 40 | - redis-master 41 | 42 | redis-sentinel: 43 | image: s7anley/redis-sentinel-docker 44 | environment: 45 | - MASTER_NAME=mymaster 46 | - QUORUM=1 47 | - MASTER=redis-master 48 | depends_on: 49 | - redis-master 50 | - redis-slave 51 | 52 | mailcatcher: 53 | image: sj26/mailcatcher:latest 54 | ports: 55 | - "1080:1080" 56 | 57 | lms: 58 | build: 59 | context: ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare} 60 | target: production 61 | args: 62 | EDX_RELEASE_REF: ${EDX_RELEASE_REF:-release-2018-08-29-14.14} 63 | EDX_ARCHIVE_URL: ${EDX_ARCHIVE_URL:-https://github.com/edx/edx-platform/archive/release-2018-08-29-14.14.tar.gz} 64 | image: "${EDXAPP_IMAGE_NAME:-edxapp}:${EDXAPP_IMAGE_TAG:-master-bare}" 65 | env_file: 66 | - env.d/development 67 | - env.d/${REDIS_SERVICE:-redis} 68 | environment: 69 | SERVICE_VARIANT: lms 70 | DJANGO_SETTINGS_MODULE: lms.envs.fun.docker_run 71 | volumes: 72 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/data/media:/edx/var/edxapp/media 73 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/data/store:/edx/app/edxapp/data 74 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/data/export:/edx/var/edxapp/export 75 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/config:/config 76 | - ./docker/files/usr/local/bin/auth_init:/usr/local/bin/auth_init 77 | # Add lines here to override source code in a Python module e.g.: 78 | #- ./src/edx-ora2/openassessment:/usr/local/lib/python2.7/dist-packages/openassessment 79 | user: ${DOCKER_UID}:${DOCKER_GID} 80 | stdin_open: true 81 | tty: true 82 | depends_on: 83 | - mailcatcher 84 | - mysql 85 | - mongodb 86 | - memcached 87 | - ${REDIS_SERVICE:-redis} 88 | 89 | lms-dev: 90 | build: 91 | context: ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare} 92 | target: development 93 | args: 94 | DOCKER_UID: ${DOCKER_UID} 95 | DOCKER_GID: ${DOCKER_GID} 96 | EDX_RELEASE_REF: ${EDX_RELEASE_REF:-release-2018-08-29-14.14} 97 | EDX_ARCHIVE_URL: ${EDX_ARCHIVE_URL:-https://github.com/edx/edx-platform/archive/release-2018-08-29-14.14.tar.gz} 98 | image: "${EDXAPP_IMAGE_NAME:-edxapp}:${EDXAPP_IMAGE_TAG:-master-bare}-dev" 99 | env_file: 100 | - env.d/development 101 | - env.d/${REDIS_SERVICE:-redis} 102 | environment: 103 | SERVICE_VARIANT: lms 104 | DJANGO_SETTINGS_MODULE: lms.envs.fun.docker_run 105 | ports: 106 | - "8072:8000" 107 | volumes: 108 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/src/edx-platform:/edx/app/edxapp/edx-platform 109 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/data/static/development:/edx/app/edxapp/staticfiles 110 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/data/media:/edx/var/edxapp/media 111 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/data/store:/edx/app/edxapp/data 112 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/data/export:/edx/var/edxapp/export 113 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/config:/config 114 | # Add lines here to override source code in a Python module e.g.: 115 | #- ./src/edx-ora2/openassessment:/usr/local/lib/python2.7/dist-packages/openassessment 116 | command: > 117 | python manage.py lms runserver 0.0.0.0:8000 --settings=fun.docker_run_development 118 | depends_on: 119 | - mailcatcher 120 | - mysql 121 | - mongodb 122 | - memcached 123 | - ${REDIS_SERVICE:-redis} 124 | 125 | cms: 126 | image: "${EDXAPP_IMAGE_NAME:-edxapp}:${EDXAPP_IMAGE_TAG:-master-bare}" 127 | env_file: 128 | - env.d/development 129 | - env.d/${REDIS_SERVICE:-redis} 130 | environment: 131 | SERVICE_VARIANT: cms 132 | DJANGO_SETTINGS_MODULE: cms.envs.fun.docker_run 133 | volumes: 134 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/data/media:/edx/var/edxapp/media 135 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/data/store:/edx/app/edxapp/data 136 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/config:/config 137 | # Add lines here to override source code in a Python module e.g.: 138 | #- ./src/edx-ora2/openassessment:/usr/local/lib/python2.7/dist-packages/openassessment 139 | networks: 140 | - default 141 | - lms_outside 142 | depends_on: 143 | - lms 144 | user: ${DOCKER_UID}:${DOCKER_GID} 145 | 146 | cms-dev: 147 | image: "${EDXAPP_IMAGE_NAME:-edxapp}:${EDXAPP_IMAGE_TAG:-master-bare}-dev" 148 | env_file: 149 | - env.d/development 150 | - env.d/${REDIS_SERVICE:-redis} 151 | ports: 152 | - "8082:8000" 153 | volumes: 154 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/src/edx-platform:/edx/app/edxapp/edx-platform 155 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/data/static/development:/edx/app/edxapp/staticfiles 156 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/data/media:/edx/var/edxapp/media 157 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/data/store:/edx/app/edxapp/data 158 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/config:/config 159 | # Add lines here to override source code in a Python module e.g.: 160 | #- ./src/edx-ora2/openassessment:/usr/local/lib/python2.7/dist-packages/openassessment 161 | command: > 162 | python manage.py cms runserver 0.0.0.0:8000 --settings=fun.docker_run_development 163 | depends_on: 164 | - lms-dev 165 | 166 | nginx: 167 | build: 168 | context: ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare} 169 | target: nginx 170 | args: 171 | EDX_RELEASE_REF: ${EDX_RELEASE_REF:-release-2018-08-29-14.14} 172 | EDX_ARCHIVE_URL: ${EDX_ARCHIVE_URL:-https://github.com/edx/edx-platform/archive/release-2018-08-29-14.14.tar.gz} 173 | image: "${EDXAPP_NGINX_IMAGE_NAME:-edxapp-nginx}:${EDXAPP_IMAGE_TAG:-master-bare}" 174 | ports: 175 | - "8073:8073" 176 | - "8083:8083" 177 | networks: 178 | default: 179 | aliases: 180 | - nginx 181 | lms_outside: 182 | aliases: 183 | - edx 184 | volumes: 185 | - ./docker/files/etc/nginx/${NGINX_CONF:-conf.d}:/etc/nginx/conf.d:ro 186 | - ./${FLAVORED_EDX_RELEASE_PATH:-releases/master/0/bare}/data/media:/data/media:ro 187 | depends_on: 188 | - lms 189 | - cms 190 | 191 | dockerize: 192 | image: jwilder/dockerize 193 | 194 | networks: 195 | lms_outside: 196 | driver: bridge 197 | name: "${EDX_NETWORK:-edx-lms-outside}" 198 | -------------------------------------------------------------------------------- /docker/files/etc/nginx/conf.d/cms.conf: -------------------------------------------------------------------------------- 1 | upstream cms-backend { 2 | server cms:8000 fail_timeout=0; 3 | } 4 | 5 | server { 6 | listen 8083; 7 | server_name localhost; 8 | 9 | # Prevent invalid display courseware in IE 10+ with high privacy settings 10 | add_header P3P 'CP="Open edX does not have a P3P policy."'; 11 | 12 | client_max_body_size 100M; 13 | 14 | rewrite ^(.*)/favicon.ico$ /static/images/favicon.ico last; 15 | 16 | # Disables server version feedback on pages and in headers 17 | server_tokens off; 18 | 19 | location @proxy_to_cms_app { 20 | proxy_set_header X-Forwarded-Proto $scheme; 21 | proxy_set_header X-Forwarded-Port $server_port; 22 | proxy_set_header X-Forwarded-For $remote_addr; 23 | 24 | proxy_set_header Host $http_host; 25 | 26 | proxy_redirect off; 27 | proxy_pass http://cms-backend; 28 | } 29 | 30 | location / { 31 | try_files $uri @proxy_to_cms_app; 32 | } 33 | 34 | location ~ ^/static/(?P.*) { 35 | root /edx/app/edxapp/staticfiles; 36 | try_files /$file =404; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /docker/files/etc/nginx/conf.d/lms.conf: -------------------------------------------------------------------------------- 1 | upstream lms-backend { 2 | server lms:8000 fail_timeout=0; 3 | } 4 | 5 | server { 6 | listen 8073; 7 | server_name localhost; 8 | 9 | # Prevent invalid display courseware in IE 10+ with high privacy settings 10 | add_header P3P 'CP="Open edX does not have a P3P policy."'; 11 | 12 | client_max_body_size 4M; 13 | 14 | rewrite ^(.*)/favicon.ico$ /static/images/favicon.ico last; 15 | rewrite ^/richie/(.*)$ http://localhost:8070/$1 permanent; 16 | 17 | # Disables server version feedback on pages and in headers 18 | server_tokens off; 19 | 20 | location @proxy_to_lms_app { 21 | proxy_set_header X-Forwarded-Proto $scheme; 22 | proxy_set_header X-Forwarded-Port $server_port; 23 | proxy_set_header X-Forwarded-For $remote_addr; 24 | 25 | proxy_set_header Host $http_host; 26 | 27 | proxy_redirect off; 28 | proxy_pass http://lms-backend; 29 | } 30 | 31 | location / { 32 | try_files $uri @proxy_to_lms_app; 33 | } 34 | 35 | # /login?next= can be used by 3rd party sites in tags to 36 | # determine whether a user on their site is logged into edX. 37 | # The most common image to use is favicon.ico. 38 | location /login { 39 | if ( $arg_next ~* "favicon.ico" ) { 40 | return 403; 41 | } 42 | try_files $uri @proxy_to_lms_app; 43 | } 44 | 45 | # Need a separate location for the image uploads endpoint to limit upload sizes 46 | location ~ ^/api/profile_images/[^/]*/[^/]*/upload$ { 47 | try_files $uri @proxy_to_lms_app; 48 | client_max_body_size 1049576; 49 | } 50 | 51 | location ~ ^/media/(?P.*) { 52 | root /data/media; 53 | try_files /$file =404; 54 | expires 31536000s; 55 | } 56 | 57 | location ~ ^/static/(?P.*) { 58 | root /edx/app/edxapp/staticfiles; 59 | try_files /$file =404; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /docker/files/etc/nginx/ssl/ssl.conf.tpl: -------------------------------------------------------------------------------- 1 | ssl_certificate /etc/nginx/conf.d/edx.local.dev.pem; 2 | ssl_certificate_key /etc/nginx/conf.d/edx.local.dev.key; 3 | error_page 497 https://$host:$server_port$request_uri; 4 | 5 | -------------------------------------------------------------------------------- /docker/files/usr/local/bin/auth_init: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from os import environ 4 | 5 | import django 6 | 7 | django.setup() 8 | 9 | from django.contrib.auth.models import User 10 | from django.contrib.sites.models import Site 11 | from django.core.exceptions import ImproperlyConfigured 12 | 13 | from student.models import UserProfile 14 | 15 | 16 | # Create a normal user 17 | try: 18 | edx = User.objects.get(username="edx") 19 | except User.DoesNotExist: 20 | edx = User.objects.create(username="edx", email="edx@example.com", is_active=True) 21 | edx.set_password("edx") 22 | edx.save() 23 | UserProfile.objects.create(user=edx) 24 | 25 | # Create a superuser 26 | try: 27 | admin = User.objects.get(username="admin") 28 | except User.DoesNotExist: 29 | admin = User.objects.create( 30 | username="admin", 31 | email="admin@example.com", 32 | is_active=True, 33 | is_superuser=True, 34 | is_staff=True, 35 | ) 36 | admin.set_password("admin") 37 | admin.save() 38 | UserProfile.objects.create(user=admin) 39 | 40 | 41 | # Activate API access 42 | try: 43 | from openedx.core.djangoapps.api_admin.models import ( 44 | ApiAccessConfig, 45 | ApiAccessRequest, 46 | ) 47 | except ImportError: 48 | pass 49 | else: 50 | api_access = ApiAccessConfig.objects.last() 51 | if api_access is None: 52 | ApiAccessConfig.objects.create(enabled=True) 53 | elif api_access.enabled is False: 54 | api_access.enabled = True 55 | api_access.save() 56 | 57 | # Create SSO authorizations 58 | try: 59 | from oauth2_provider.models import Application 60 | 61 | except ImportError: 62 | # Open edX version <= Dogwood 63 | from oauth2_provider.models import TrustedClient 64 | from provider.oauth2.models import Client 65 | from provider.constants import CONFIDENTIAL 66 | 67 | client = { 68 | "client_type": CONFIDENTIAL, 69 | "client_secret": "fakesecret", 70 | "name": "Test SSO", 71 | "redirect_uri": "http://localhost:8070/oauth/complete/edx-oidc/", 72 | "url": "localhost:8070", 73 | "user": admin, 74 | } 75 | 76 | # Create Client with authorization for SSO connection 77 | try: 78 | sso_client = Client.objects.get(client_id="social-id") 79 | except Client.DoesNotExist: 80 | sso_client = Client.objects.create(client_id="social-id", **client) 81 | else: 82 | Client.objects.filter(id=sso_client.id).update(**client) 83 | 84 | TrustedClient.objects.get_or_create(client=sso_client) 85 | 86 | else: 87 | # Open edX version >= Eucalyptus 88 | client = { 89 | "authorization_grant_type": "authorization-code", 90 | "client_type": Application.CLIENT_CONFIDENTIAL, 91 | "client_secret": "fakesecret", 92 | "name": "Test SSO", 93 | "redirect_uris": "http://localhost:8070/oauth/complete/edx-oauth2/", 94 | "skip_authorization": True, 95 | "user": admin, 96 | } 97 | 98 | # Create Application with authorization code for SSO connection 99 | try: 100 | sso_authorization = Application.objects.get(client_id="social-id") 101 | except Application.DoesNotExist: 102 | Application.objects.create(client_id="social-id", **client) 103 | else: 104 | Application.objects.filter(id=sso_authorization.id).update(**client) 105 | -------------------------------------------------------------------------------- /docker/files/usr/local/bin/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Development entrypoint 4 | # 5 | 6 | # Activate user's virtualenv 7 | source /edx/app/edxapp/venv/bin/activate 8 | exec "$@" 9 | -------------------------------------------------------------------------------- /docs/images/edx_oauth2_client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfun/openedx-docker/4d7664c97b59f201230f0b2b48f7cfc255496bf5/docs/images/edx_oauth2_client.png -------------------------------------------------------------------------------- /docs/images/moodle_oauth2_configure_endpoints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfun/openedx-docker/4d7664c97b59f201230f0b2b48f7cfc255496bf5/docs/images/moodle_oauth2_configure_endpoints.png -------------------------------------------------------------------------------- /docs/images/moodle_oauth2_configure_mappings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfun/openedx-docker/4d7664c97b59f201230f0b2b48f7cfc255496bf5/docs/images/moodle_oauth2_configure_mappings.png -------------------------------------------------------------------------------- /docs/images/moodle_oauth2_enable_plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfun/openedx-docker/4d7664c97b59f201230f0b2b48f7cfc255496bf5/docs/images/moodle_oauth2_enable_plugin.png -------------------------------------------------------------------------------- /docs/images/moodle_oauth2_service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfun/openedx-docker/4d7664c97b59f201230f0b2b48f7cfc255496bf5/docs/images/moodle_oauth2_service.png -------------------------------------------------------------------------------- /docs/moodle-oauth-configuration.md: -------------------------------------------------------------------------------- 1 | # Moodle OAuth Configuration 2 | 3 | To integrate Moodle and Open edX, you can use OAuth to allow users to log in to Moodle using their Open edX credentials. 4 | Follow these steps to configure Moodle OAuth with Open edX. 5 | 6 | ## Open edX Configuration 7 | 8 | 1. In the Open edX platform, go to the Django admin panel at `http:///admin`. 9 | 2. Go to http://localhost:8072/admin/oauth2/client/ and click **Add Client**. 10 | 3. Fill the **URI** field with the URL of your Moodle instance. 11 | 12 | `https://sandbox.moodledemo.net` 13 | 4. Fill the **Redirect uri** field with the callback URL of your Moodle instance. 14 | 15 | `https://sandbox.moodledemo.net/admin/oauth2callback.php` 16 | 5. Keep the generated client id and secret for later use. 17 | 6. Set the **client type** to `Confidential`. 18 | 7. Click **Save**. 19 | 20 | ![Open edX admin: OAuth2 client values](images/edx_oauth2_client.png) 21 | 22 | ## Moodle Configuration 23 | 24 | [Detailed Moodle configuration documentation](https://docs.moodle.org/402/en/OAuth_2_services) 25 | 26 | ### Create a new OAuth2 custom service 27 | 28 | 1. In the Moodle admin panel, go to `Site administration > Server > OAuth 2 services`. 29 | 2. Click **Create a new custom service**. 30 | 3. Fill the **Name** field with a name of your choice. 31 | 4. Fill the **Client ID** and **Client secret** fields with the values generated in the Open edX configuration step. 32 | 5. Fill the **Service base URL** field with the URL of your Open edX instance. 33 | 34 | `http://` 35 | 6. Set the **This service will be used** field to `Login page only`. 36 | 7. Fill the **Name displayed on the login page** field with a name of your choice. 37 | 8. Uncheck the **Require email verification** field. 38 | 9. Check the **I understand that disabling email verification can be a security issue.** field. 39 | 10. Click **Save changes**. 40 | 41 | ![Moodle admin: OAuth2 ](images/moodle_oauth2_service.png) 42 | 43 | ### Configure endpoints 44 | 45 | 1. Click on the **Configure endpoints** button. 46 | 47 | ![Moodle admin: navigate to webservice configure endpoints](images/moodle_oauth2_configure_endpoints.png) 48 | 2. Click on **Create new endpoint for issuer "Open EDX"**. 49 | 3. Create the following endpoints: 50 | 51 | | Name | URL | 52 | |------------------------|-------------------------------------------------------| 53 | | authorization_endpoint | `http:///oauth2/authorize/` | 54 | | token_endpoint | `http:///oauth2/access_token/` | 55 | | userinfo_endpoint | `http:///oauth2/user_info/` | 56 | 57 | 58 | ### Configure user field mapping 59 | 60 | 1. Click on the **Configure user field mappings** button. 61 | 62 | ![Moodle admin: navigate to webservice configure user field mappings](images/moodle_oauth2_configure_mappings.png) 63 | 2. Create the following mappings: 64 | 65 | | External field name | Internal field name | 66 | |---------------------|---------------------| 67 | | email | email | 68 | | preferred_username | username | 69 | 70 | 71 | ### Activate OAuth2 login 72 | 73 | 1. In the Moodle admin panel, go to `Site administration > Plugins > Authentication > Manage authentication`. 74 | 2. Make sure that **Prevent account creation when authenticating** is unchecked. 75 | 3. Enable the **OAuth 2** plugin. 76 | 77 | ![Moodle admin: enable OAuth2](images/moodle_oauth2_enable_plugin.png) 78 | 79 | 80 | You can now log in to Moodle using your Open edX credentials. 81 | 82 | -------------------------------------------------------------------------------- /docs/richie-configuration.md: -------------------------------------------------------------------------------- 1 | # Richie Configuration 2 | 3 | ## Enable TLS 4 | 5 | If you want to use this repo with [Richie](https://github.com/openfun/richie), 6 | [you need to enable TLS](https://richie.education/docs/quick-start#purpose) 7 | on your development environment. Following instructions suppose that your development domain 8 | is `edx.local.dev` and you edited your `etc/hosts` accordingly. 9 | 10 | ### How to 11 | 12 | #### 1. Install mkcert ans its Certificate Authority 13 | 14 | First you will need to install mkcert and its Certificate Authority. 15 | [mkcert](https://mkcert.org/) is a little util to ease local certificate generation. 16 | 17 | ##### a. Install `mkcert` on your local machine 18 | 19 | - [Read the doc](https://github.com/FiloSottile/mkcert) 20 | - Linux users who do not want to use linuxbrew : [read this article](https://www.prado.lt/how-to-create-locally-trusted-ssl-certificates-in-local-development-environment-on-linux-with-mkcert). 21 | 22 | ##### b. Install Mkcert Certificate Authority 23 | 24 | `mkcert -install` 25 | 26 | > If you do not want to use mkcert, you can generate [CA and certificate with openssl](https://www.freecodecamp.org/news/how-to-get-https-working-on-your-local-development-environment-in-5-minutes-7af615770eec/). 27 | > You will have to put your certificate and its key in the `docker/files/etc/nginx/ssl` directory 28 | > and name them `edx.local.dev.pem` and `edx.local.dev.key`. 29 | 30 | #### 2. Configure Nginx 31 | 32 | To generate the certificate with mkcert and update Nginx configuration, run: 33 | `bin/setup-ssl` 34 | 35 | > If you do not want to use mkcert, read instructions above to generate OpenEdx certificate then 36 | > run `bin/setup-ssl --no-cert` instead. 37 | 38 | #### 3. Enable Mobile Rest API (Only for Dogwood & Eucalyptus) 39 | If you want to use OpenEdx Dogwood or Eucalyptus releases, you have to enable 40 | the mobile rest API to work with Richie. 41 | 42 | `FEATURES['ENABLE_MOBILE_REST_API'] = True` 43 | 44 | #### 4. Start OpenEdx over SSL 45 | 46 | Finally start apps over SSL with `make run-ssl`. 47 | You can still run apps without SSL using `make run`. 48 | -------------------------------------------------------------------------------- /env.d/development: -------------------------------------------------------------------------------- 1 | # Django 2 | SERVICE_VARIANT=lms 3 | DJANGO_SETTINGS_MODULE=lms.envs.fun.docker_run 4 | ALLOWED_HOSTS=["*"] 5 | 6 | # Database 7 | MYSQL_ROOT_PASSWORD= 8 | MYSQL_ALLOW_EMPTY_PASSWORD=yes 9 | MYSQL_DATABASE=edxapp 10 | MYSQL_USER=edxapp_user 11 | MYSQL_PASSWORD=password 12 | 13 | # Email 14 | EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend 15 | EMAIL_HOST=mailcatcher 16 | EMAIL_PORT=1025 17 | 18 | # Python 19 | PYTHONUNBUFFERED=1 20 | 21 | # Features 22 | FEATURES={"AUTOMATIC_AUTH_FOR_TESTING": true, "RESTRICT_AUTOMATIC_AUTH": false, "ENABLE_OAUTH2_PROVIDER": true, "ENABLE_CORS_HEADERS": true, "ENABLE_CROSS_DOMAIN_CSRF_COOKIE": true} 23 | OAUTH_ENFORCE_SECURE= 24 | OAUTH_ENFORCE_CLIENT_SECURE= 25 | OAUTH_OIDC_ISSUER=http://edx:8073/oauth2 26 | 27 | # CORS 28 | CORS_ALLOW_CREDENTIALS=true 29 | CORS_ALLOW_INSECURE=true 30 | CROSS_DOMAIN_CSRF_COOKIE_DOMAIN=.local.dev 31 | CROSS_DOMAIN_CSRF_COOKIE_NAME=edx_csrf_token 32 | SESSION_COOKIE_NAME=edxcsrftoken 33 | # Do not enable CORS_ORIGIN_ALLOW_ALL for production purpose! 34 | # Instead set CORS_ORIGIN_WHITELIST properly 35 | CORS_ORIGIN_ALLOW_ALL=true 36 | 37 | # API 38 | EDX_API_KEY=FakeEdXAPIKey 39 | 40 | # Richie synchronization 41 | COURSE_HOOKS=[{"url": "https://richie:8070/api/v1.0/course-runs-sync/", "secret": "shared secret", "verify": false}] 42 | -------------------------------------------------------------------------------- /env.d/production: -------------------------------------------------------------------------------- 1 | LMS_BASE=localhost:8073 2 | CMS_BASE=localhost:8083 3 | -------------------------------------------------------------------------------- /env.d/redis: -------------------------------------------------------------------------------- 1 | # Add Redis service environment variables in this file 2 | # 3 | # PLEASE DO NOT REMOVE THIS FILE 4 | # It is required for docker compose environment description 5 | -------------------------------------------------------------------------------- /env.d/redis-sentinel: -------------------------------------------------------------------------------- 1 | # Celery settings 2 | CELERY_BROKER_TRANSPORT=redis-sentinel 3 | CELERY_BROKER_HOST=redis-sentinel 4 | CELERY_BROKER_PORT=26379 5 | BROKER_TRANSPORT_OPTIONS={"sentinels":[["redis-sentinel",26379]],"service_name":"mymaster"} 6 | 7 | # Session in redis 8 | SESSION_REDIS_SENTINEL_LIST=[["redis-sentinel", 26379]] 9 | SESSION_REDIS_SENTINEL_MASTER_ALIAS=mymaster 10 | 11 | # Cache in redis 12 | CACHE_REDIS_BACKEND=django_redis_sentinel.cache.RedisSentinelCache 13 | CACHE_REDIS_CLIENT=django_redis_sentinel.client.SentinelClient 14 | CACHE_REDIS_SENTINEL_SERVICE_NAME=mymaster 15 | CACHE_REDIS_PORT=26379 16 | CACHE_REDIS_HOST=redis-sentinel 17 | -------------------------------------------------------------------------------- /gitlint/gitlint_emoji.py: -------------------------------------------------------------------------------- 1 | """ 2 | Gitlint extra rule to validate that the message title is of the form 3 | "() " 4 | """ 5 | from __future__ import unicode_literals 6 | 7 | import re 8 | 9 | import requests 10 | 11 | from gitlint.rules import CommitMessageTitle, LineRule, RuleViolation 12 | 13 | 14 | class GitmojiTitle(LineRule): 15 | """ 16 | This rule will enforce that each commit title is of the form "() " 17 | where gitmoji is an emoji from the list defined in https://gitmoji.carloscuesta.me and 18 | subject should be all lowercase 19 | """ 20 | 21 | id = "UC1" 22 | name = "title-should-have-gitmoji-and-scope" 23 | target = CommitMessageTitle 24 | 25 | def validate(self, title, _commit): 26 | """ 27 | Download the list possible gitmojis from the project's github repository and check that 28 | title contains one of them. 29 | """ 30 | gitmojis = requests.get( 31 | "https://raw.githubusercontent.com/carloscuesta/gitmoji/master/packages/gitmojis/src/gitmojis.json" 32 | ).json()["gitmojis"] 33 | emojis = [item["emoji"] for item in gitmojis] 34 | pattern = r"^({:s})\(.*\)\s[a-z].*$".format("|".join(emojis)) 35 | if not re.search(pattern, title): 36 | violation_msg = 'Title does not match regex "() "' 37 | return [RuleViolation(self.id, violation_msg, title)] 38 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/activate: -------------------------------------------------------------------------------- 1 | export EDX_RELEASE="dogwood.3" 2 | export FLAVOR="fun" 3 | export EDX_RELEASE_REF="dogwood.3-fun-5.3.3" 4 | export EDX_ARCHIVE_URL="https://github.com/openfun/edx-platform/archive/${EDX_RELEASE_REF}.tar.gz" 5 | export EDX_DEMO_RELEASE_REF="open-release/eucalyptus.1" 6 | export EDX_DEMO_ARCHIVE_URL="file://${PWD}/releases/dogwood/3/fun/demo-course.tar.gz" 7 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/cms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfun/openedx-docker/4d7664c97b59f201230f0b2b48f7cfc255496bf5/releases/dogwood/3/fun/config/cms/__init__.py -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/cms/docker_build_development.py: -------------------------------------------------------------------------------- 1 | # This is a minimal settings file allowing us to run "update_assets" 2 | # in the Dockerfile 3 | from .docker_run_development import * 4 | 5 | DATABASES = {"default": {}} 6 | 7 | XQUEUE_INTERFACE = {"url": None, "django_auth": None} 8 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/cms/docker_build_production.py: -------------------------------------------------------------------------------- 1 | # This is a minimal settings file allowing us to run "update_assets" 2 | # in the Dockerfile 3 | from .docker_run_production import * 4 | 5 | DATABASES = {"default": {}} 6 | 7 | XQUEUE_INTERFACE = {"url": None, "django_auth": None} 8 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/cms/docker_run.py: -------------------------------------------------------------------------------- 1 | # This file is meant to import the environment related settings file 2 | 3 | from docker_run_production import * 4 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/cms/docker_run_development.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `development` environment for the CMS, starting from 2 | # the settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from lms.envs.fun.utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | if "sentry" in LOGGING.get("handlers"): 11 | LOGGING["handlers"]["sentry"]["environment"] = "development" 12 | 13 | DEBUG = True 14 | REQUIRE_DEBUG = True 15 | 16 | EMAIL_BACKEND = config( 17 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 18 | ) 19 | 20 | PIPELINE_ENABLED = False 21 | STATICFILES_STORAGE = "openedx.core.storage.DevelopmentStorage" 22 | 23 | ALLOWED_HOSTS = ["*"] 24 | 25 | FEATURES["AUTOMATIC_AUTH_FOR_TESTING"] = True 26 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/cms/docker_run_preprod.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `preprod` environment for the LMS 2 | # starting from the settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from lms.envs.fun.utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "preprod" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/cms/docker_run_staging.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `staging` environment for the LMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from lms.envs.fun.utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "staging" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/cms/secrets.yml: -------------------------------------------------------------------------------- 1 | ANONYMIZATION_KEY: "" 2 | 3 | EDX_API_KEY: "" 4 | 5 | ECOMMERCE_API_SIGNING_KEY: "" 6 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/cms/settings.yml: -------------------------------------------------------------------------------- 1 | PLATFORM_NAME: FUN 2 | 3 | DEFAULT_FEEDBACK_EMAIL: contact@fun-mooc.fr 4 | DEFAULT_FROM_EMAIL: no-reply@fun-mooc.fr 5 | TECH_SUPPORT_EMAIL: contact@fun-mooc.fr 6 | 7 | ADMINS: 8 | - 9 | - fun-devteam 10 | - fun.dev@fun-mooc.fr 11 | 12 | WIKI_ENABLED: true 13 | 14 | TIME_ZONE: Europe/Paris 15 | 16 | LANGUAGE_CODE: fr 17 | 18 | # These are the languages we allow on FUN platform 19 | # DarkLanguageConfig.released_languages must use the same codes (comma separated) 20 | # Language codes have to match edX's ones (lms.envs.common.LANGUAGES) 21 | LANGUAGES: 22 | - 23 | - fr 24 | - Français 25 | - 26 | - en 27 | - English 28 | - 29 | - de-de 30 | - Deutsch 31 | 32 | PASSWORD_MIN_LENGTH: 8 33 | PASSWORD_MAX_LENGTH: 30 34 | PASSWORD_COMPLEXITY: 35 | UPPER: 1 36 | LOWER: 1 37 | DIGITS: 1 38 | 39 | # Celery 40 | CELERY_RESULT_BACKEND: djcelery.backends.database:DatabaseBackend 41 | 42 | LTI_XBLOCK_CONFIGURATIONS: 43 | # Configuration for Proctor Exam xblock 44 | - is_launch_url_regex: false 45 | hidden_fields: 46 | - display_name 47 | - description 48 | - lti_id 49 | - launch_target 50 | - inline_height 51 | - accept_grades_past_due 52 | - ask_to_send_username 53 | - ask_to_send_email 54 | - custom_parameters 55 | - has_score 56 | - hide_launch 57 | - modal_height 58 | - modal_width 59 | - weight 60 | - button_tex 61 | automatic_resizing: null 62 | inline_ratio: 0.5625 63 | ignore_configuration: true 64 | show_button: false 65 | pattern: .*fun\.proctorexam\.com/lti\?id=(?P[0-9]+) 66 | defaults: 67 | launch_target: new_window 68 | lti_id: proctor_exam 69 | # Configuration for Marsha LTI video service 70 | - display_name: Marsha Video 71 | is_launch_url_regex: true 72 | pattern: .*marsha\.education/lti.* 73 | hidden_fields: accept_grades_past_due 74 | - ask_to_send_username 75 | - ask_to_send_email 76 | - button_text 77 | - custom_parameters 78 | - description 79 | - has_score 80 | - hide_launch 81 | - launch_target 82 | - modal_height 83 | - modal_width 84 | - weight 85 | automatic_resizing: True, 86 | inline_ratio: 0.5625, 87 | defaults: 88 | launch_target: iframe 89 | inline_height: 400 90 | lti_id: marsha 91 | launch_url: https://marsha\.education/lti/videos/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} 92 | # Glowbl 93 | - display_name: Glowbl 94 | is_launch_url_regex: false 95 | hidden_fields: 96 | - accept_grades_past_due 97 | - ask_to_send_email 98 | - custom_parameters 99 | - has_score 100 | - hide_launch 101 | - modal_height 102 | - modal_width 103 | - weight 104 | automatic_resizing: true 105 | inline_ratio: 0.5625 106 | defaults: 107 | ask_to_send_username: true 108 | description: | 109 | 110 |

Accès à la salle de visioconférence Glowbl

111 |

112 | En cliquant sur ce bouton, vous quittez la plateforme fun-mooc.fr 113 |

114 | En accédant à la conférence Glowbl, vous acceptez la transmission de votre nom d’utilisateur à la société Glowbl. 115 |

116 | Attention une intervention de votre part, écrite ou vidéo, peut être enregistrée. 117 |

118 | button_text: Accéder à la conférence Glowbl et accepter la transmission de mon nom d'utilisateur 119 | launch_target: new_window 120 | inline_height: 400 121 | lti_id: glowbl 122 | launch_url: https://account.glowbl.com/auth/provider/lti 123 | # Default LTI consumer 124 | - display_name: LTI consumer 125 | 126 | ######################### CMS settings ####################################### 127 | 128 | FEATURES: 129 | ADVANCED_SECURITY: false 130 | ALLOW_ALL_ADVANCED_COMPONENTS: true # Allow all courses to use advanced components 131 | AUTH_USE_OPENID_PROVIDER: true 132 | AUTOMATIC_AUTH_FOR_TESTING: false 133 | CERTIFICATES_ENABLED: true 134 | CERTIFICATES_HTML_VIEW: true 135 | ENABLE_CONTENT_LIBRARIES: true 136 | ENABLE_COURSEWARE_INDEX: true # index courseware content after every modification in studio 137 | ENABLE_CREATOR_GROUP: true # restrain user who can create course in studio to granted ones in CourseCreator table 138 | ENABLE_DISCUSSION_SERVICE: true 139 | ENABLE_DJANGO_ADMIN_SITE: true 140 | ENABLE_INSTRUCTOR_ANALYTICS: true 141 | ENABLE_MAX_FAILED_LOGIN_ATTEMPTS: false 142 | ENABLE_MKTG_SITE: false 143 | ENABLE_S3_GRADE_DOWNLOADS: true 144 | ENFORCE_PASSWORD_POLICY: true 145 | IS_EDX_DOMAIN: true # used to display Edx Studio logo, see edx-platform/cms/templates/widgets/header.html 146 | SUBDOMAIN_BRANDING: false 147 | SUBDOMAIN_COURSE_LISTINGS: false 148 | USE_CUSTOM_THEME: false 149 | 150 | # MKTG_URLS are absolute urls used when ENABLE_MKTG_SITE is set to True 151 | # As FUN theme is not used in CMS, we can not reverse its static pages like /tos or /privacy 152 | MKTG_URLS: 153 | ROOT: http://www.fun-mooc.fr 154 | TOS: /tos 155 | PRIVACY: /privacy 156 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/lms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfun/openedx-docker/4d7664c97b59f201230f0b2b48f7cfc255496bf5/releases/dogwood/3/fun/config/lms/__init__.py -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/lms/backends.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from ratelimitbackend.backends import RateLimitModelBackend 4 | 5 | 6 | class ProxyRateLimitModelBackend(RateLimitModelBackend): 7 | """A rate limiting Backend that works behind a proxy.""" 8 | 9 | def get_ip(self, request): 10 | """Return the end user address string as set by proxies.""" 11 | return request.META['HTTP_X_FORWARDED_FOR'] 12 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/lms/docker_build_development.py: -------------------------------------------------------------------------------- 1 | # This is a minimal settings file allowing us to run "update_assets" 2 | # in the Dockerfile 3 | from .docker_run_development import * 4 | 5 | DATABASES = {"default": {}} 6 | 7 | XQUEUE_INTERFACE = {"url": None, "django_auth": None} 8 | 9 | CACHES = { 10 | "default": { 11 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 12 | "KEY_PREFIX": "default", 13 | }, 14 | "general": { 15 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 16 | "KEY_PREFIX": "general", 17 | }, 18 | "celery": { 19 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 20 | "KEY_PREFIX": "celery", 21 | }, 22 | "mongo_metadata_inheritance": { 23 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 24 | "KEY_PREFIX": "mongo_metadata_inheritance", 25 | }, 26 | "openassessment_submissions": { 27 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 28 | "KEY_PREFIX": "openassessment_submissions", 29 | }, 30 | "loc_cache": { 31 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 32 | "KEY_PREFIX": "loc_cache", 33 | }, 34 | # Cache backend used by Django 1.8 storage backend while processing static files 35 | "staticfiles": { 36 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 37 | "KEY_PREFIX": "staticfiles", 38 | }, 39 | } 40 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/lms/docker_build_production.py: -------------------------------------------------------------------------------- 1 | # This is a minimal settings file allowing us to run "update_assets" 2 | # in the Dockerfile 3 | from .docker_run_production import * 4 | 5 | DATABASES = {"default": {}} 6 | 7 | XQUEUE_INTERFACE = {"url": None, "django_auth": None} 8 | 9 | CACHES = { 10 | "default": { 11 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 12 | "KEY_PREFIX": "default", 13 | }, 14 | "general": { 15 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 16 | "KEY_PREFIX": "general", 17 | }, 18 | "celery": { 19 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 20 | "KEY_PREFIX": "celery", 21 | }, 22 | "mongo_metadata_inheritance": { 23 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 24 | "KEY_PREFIX": "mongo_metadata_inheritance", 25 | }, 26 | "openassessment_submissions": { 27 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 28 | "KEY_PREFIX": "openassessment_submissions", 29 | }, 30 | "loc_cache": { 31 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 32 | "KEY_PREFIX": "loc_cache", 33 | }, 34 | # Cache backend used by Django 1.8 storage backend while processing static files 35 | "staticfiles": { 36 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 37 | "KEY_PREFIX": "staticfiles", 38 | }, 39 | } 40 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/lms/docker_run.py: -------------------------------------------------------------------------------- 1 | # This file is meant to import the environment related settings file 2 | 3 | from docker_run_production import * 4 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/lms/docker_run_development.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `development` environment for the LMS starting from the 2 | # settings of the `production` environment 3 | 4 | import json 5 | 6 | from docker_run_production import * 7 | from .utils import Configuration 8 | 9 | # Load custom configuration parameters from yaml files 10 | config = Configuration(os.path.dirname(__file__)) 11 | 12 | if "sentry" in LOGGING.get("handlers"): 13 | LOGGING["handlers"]["sentry"]["environment"] = "development" 14 | 15 | DEBUG = True 16 | REQUIRE_DEBUG = True 17 | 18 | EMAIL_BACKEND = config( 19 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 20 | ) 21 | 22 | PIPELINE_ENABLED = False 23 | STATICFILES_STORAGE = "openedx.core.storage.DevelopmentStorage" 24 | 25 | ALLOWED_HOSTS = ["*"] 26 | 27 | FEATURES["AUTOMATIC_AUTH_FOR_TESTING"] = True 28 | 29 | # Simple JWT 30 | SIMPLE_JWT = { 31 | "ALGORITHM": "HS256", 32 | "SIGNING_KEY": config("FONZIE_JWT_SIGNING_KEY", default="ThisIsAnExampleKeyForDevPurposeOnly"), 33 | "USER_ID_FIELD": "username", 34 | "USER_ID_CLAIM": "username", 35 | } 36 | 37 | # ORA2 fileupload 38 | ORA2_FILEUPLOAD_BACKEND = "filesystem" 39 | ORA2_FILEUPLOAD_ROOT = os.path.join(SHARED_ROOT, "openassessment_submissions") 40 | ORA2_FILEUPLOAD_CACHE_ROOT = os.path.join( 41 | SHARED_ROOT, "openassessment_submissions_cache" 42 | ) 43 | 44 | AUTHENTICATION_BACKENDS = config( 45 | "AUTHENTICATION_BACKENDS", 46 | default=["django.contrib.auth.backends.ModelBackend"], 47 | formatter=json.loads 48 | ) 49 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/lms/docker_run_preprod.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `preprod` environment for the LMS 2 | # starting from the settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from .utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "preprod" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/lms/docker_run_staging.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `staging` environment for the LMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from .utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "staging" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/lms/root_urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | OEE urls 3 | We define this file as root url (ROOT_URLCONF) to override and extend 4 | edx-platform's LMS routes 5 | """ 6 | from __future__ import absolute_import, unicode_literals 7 | 8 | from django.conf.urls import include, url 9 | 10 | from fun.lms.urls import urlpatterns # pylint: disable=import-error 11 | 12 | urlpatterns += [ 13 | # Fonzie API urls 14 | url(r"^api/", include("fonzie.urls", namespace="fonzie")), 15 | ] 16 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/lms/settings.yml: -------------------------------------------------------------------------------- 1 | PLATFORM_NAME: FUN 2 | 3 | BUGS_EMAIL: contact@fun-mooc.fr 4 | BULK_EMAIL_DEFAULT_FROM_EMAIL: no-reply@fun-mooc.fr 5 | CONTACT_EMAIL: contact@fun-mooc.fr 6 | DEFAULT_FEEDBACK_EMAIL: contact@fun-mooc.fr 7 | DEFAULT_FROM_EMAIL: no-reply@fun-mooc.fr 8 | PAYMENT_SUPPORT_EMAIL: paiements@fun-mooc.fr 9 | TECH_SUPPORT_EMAIL: contact@fun-mooc.fr 10 | 11 | PLATFORM_FACEBOOK_ACCOUNT: https://www.facebook.com/france.universite.numerique 12 | PLATFORM_TWITTER_ACCOUNT: "@FunMooc" 13 | 14 | # those 2 constants are used in code to describe certificate 15 | CERT_NAME_SHORT: Attestation 16 | CERT_NAME_LONG: Attestation de réussite 17 | 18 | ADMINS: 19 | - 20 | - fun-devteam 21 | - fun.dev@fun-mooc.fr 22 | 23 | WIKI_ENABLED: true 24 | 25 | LMS_SEGMENT_KEY: null # Remove GAnalytics tracking 26 | 27 | TIME_ZONE: Europe/Paris 28 | 29 | LANGUAGE_CODE: fr 30 | 31 | # These are the languages we allow on FUN platform 32 | # DarkLanguageConfig.released_languages must use the same codes (comma separated) 33 | # Language codes have to match edX's ones (lms.envs.common.LANGUAGES) 34 | LANGUAGES: 35 | - 36 | - fr 37 | - Français 38 | - 39 | - en 40 | - English 41 | - 42 | - de-de 43 | - Deutsch 44 | 45 | # Make sure we see the draft on preview... 46 | HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS: 47 | PREVIEW_LMS_BASE: draft-preferred 48 | 49 | PASSWORD_MIN_LENGTH: 8 50 | PASSWORD_MAX_LENGTH: 30 51 | PASSWORD_COMPLEXITY: 52 | UPPER: 1 53 | LOWER: 1 54 | DIGITS: 1 55 | 56 | # Email 57 | EMAIL_HOST: "email-smtp.eu-west-1.amazonaws.com" 58 | EMAIL_PORT: 587 59 | EMAIL_USE_TLS: true 60 | 61 | # Celery 62 | CELERY_RESULT_BACKEND: djcelery.backends.database:DatabaseBackend 63 | 64 | CERT_QUEUE: certificates 65 | 66 | CODE_JAIL: 67 | limits: 68 | REALTIME: 5 69 | VMEM: 50000000 70 | python_bin: null 71 | user: sandbox 72 | 73 | # Courses allowed to use Adways service 74 | ENABLE_ADWAYS_FOR_COURSES: 75 | - course-v1:SciencesPo+05008+session01 76 | - course-v1:SciencesPo+05008ENG+session01 77 | - course-v1:Paris1+16007+session01 78 | - course-v1:lorraine+30003+session03 79 | - course-v1:CNAM+01035+session01 80 | - course-v1:unicaen+48002+session01 81 | - course-v1:umontpellier+08005+session03 82 | - course-v1:lorraine+30003+SPOC_2018_session_1 83 | - course-v1:AgroParisTech+32002+session04 84 | - course-v1:FUN+1000+session1 85 | - course-v1:lorraine+30003+SPOC_1819_session_2 86 | - course-v1:lorraine+30003+SPOC_1920_session_1 87 | - course-v1:lorraine+30003+session04 88 | - course-v1:lorraine+30003+SPOC_1920_session_2 89 | 90 | 91 | LTI_XBLOCK_CONFIGURATIONS: 92 | # Configuration for Proctor Exam xblock 93 | - is_launch_url_regex: false 94 | hidden_fields: 95 | - display_name 96 | - description 97 | - lti_id 98 | - launch_target 99 | - inline_height 100 | - accept_grades_past_due 101 | - ask_to_send_username 102 | - ask_to_send_email 103 | - custom_parameters 104 | - has_score 105 | - hide_launch 106 | - modal_height 107 | - modal_width 108 | - weight 109 | - button_tex 110 | automatic_resizing: null 111 | inline_ratio: 0.5625 112 | ignore_configuration: true 113 | show_button: false 114 | pattern: .*fun\.proctorexam\.com/lti\?id=(?P[0-9]+) 115 | defaults: 116 | launch_target: new_window 117 | lti_id: proctor_exam 118 | # Configuration for Marsha LTI video service 119 | - display_name: Marsha Video 120 | is_launch_url_regex: true 121 | pattern: .*marsha\.education/lti.* 122 | hidden_fields: accept_grades_past_due 123 | - ask_to_send_username 124 | - ask_to_send_email 125 | - button_text 126 | - custom_parameters 127 | - description 128 | - has_score 129 | - hide_launch 130 | - launch_target 131 | - modal_height 132 | - modal_width 133 | - weight 134 | automatic_resizing: True, 135 | inline_ratio: 0.5625, 136 | defaults: 137 | launch_target: iframe 138 | inline_height: 400 139 | lti_id: marsha 140 | launch_url: https://marsha\.education/lti/videos/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} 141 | # Glowbl 142 | - display_name: Glowbl 143 | is_launch_url_regex: false 144 | hidden_fields: 145 | - accept_grades_past_due 146 | - ask_to_send_email 147 | - custom_parameters 148 | - has_score 149 | - hide_launch 150 | - modal_height 151 | - modal_width 152 | - weight 153 | automatic_resizing: true 154 | inline_ratio: 0.5625 155 | defaults: 156 | ask_to_send_username: true 157 | description: | 158 | 159 |

Accès à la salle de visioconférence Glowbl

160 |

161 | En cliquant sur ce bouton, vous quittez la plateforme fun-mooc.fr 162 |

163 | En accédant à la conférence Glowbl, vous acceptez la transmission de votre nom d’utilisateur à la société Glowbl. 164 |

165 | Attention une intervention de votre part, écrite ou vidéo, peut être enregistrée. 166 |

167 | button_text: Accéder à la conférence Glowbl et accepter la transmission de mon nom d'utilisateur 168 | launch_target: new_window 169 | inline_height: 400 170 | lti_id: glowbl 171 | launch_url: https://account.glowbl.com/auth/provider/lti 172 | # Default LTI consumer 173 | - display_name: LTI consumer 174 | 175 | RAVEN_CONFIG: 176 | dsn: "" 177 | 178 | HAYSTACK_CONNECTIONS: 179 | default: 180 | ENGINE: courses.search_indexes.ConfigurableElasticSearchEngine 181 | URL: http://localhost:9200/ 182 | INDEX_NAME: haystack 183 | 184 | MKTG_URL_LINK_MAP: 185 | ABOUT: about 186 | HONOR: honor 187 | HOW-IT-WORKS: how-it-works 188 | TOS: tos 189 | FAQ: None 190 | PRIVACY: privacy 191 | CONTACT: None 192 | UNSUPPORTED-BROWSER: unsupported-browser 193 | LICENSES: licenses 194 | LEGAL: legal 195 | COPYRIGHTS: None 196 | ROOT: root 197 | COURSES: fun-courses:index 198 | 199 | FEATURES: 200 | ACCESS_REQUIRE_STAFF_FOR_COURSE: true # hide spocs from course list 201 | ALLOW_COURSE_STAFF_GRADE_DOWNLOADS: true 202 | ALWAYS_REDIRECT_HOMEPAGE_TO_DASHBOARD_FOR_AUTHENTICATED_USER: false # loged user root is home page 203 | AUTH_USE_OPENID_PROVIDER: true 204 | CERTIFICATES_ENABLED: true 205 | CERTIFICATES_HTML_VIEW: true 206 | ENABLE_CONTENT_LIBRARIES: true 207 | ENABLE_CORS_HEADERS: true 208 | ENABLE_CREDIT_API: true 209 | ENABLE_CREDIT_ELIGIBILITY: true 210 | ENABLE_CROSS_DOMAIN_CSRF_COOKIE: true 211 | ENABLE_DASHBOARD_SEARCH: true 212 | ENABLE_DISCUSSION_SERVICE: true 213 | ENABLE_DJANGO_ADMIN_SITE: true 214 | ENABLE_MOBILE_REST_API: true 215 | ENABLE_OAUTH2_PROVIDER: true 216 | ENABLE_PAYMENT_FAKE: true 217 | ENABLE_S3_GRADE_DOWNLOADS: true 218 | ENFORCE_PASSWORD_POLICY: true 219 | PREVIEW_LMS_BASE: preview.fun-mooc.fr 220 | ADVANCED_SECURITY: false 221 | AUTOMATIC_AUTH_FOR_TESTING: false 222 | ENABLE_COMBINED_LOGIN_REGISTRATION: false 223 | ENABLE_COURSE_DISCOVERY: false 224 | ENABLE_COURSEWARE_INDEX: false 225 | ENABLE_INSTRUCTOR_ANALYTICS: false 226 | ENABLE_MAX_FAILED_LOGIN_ATTEMPTS: false 227 | ENABLE_SYSADMIN_DASHBOARD: false 228 | REQUIRE_COURSE_EMAIL_AUTH: false 229 | SUBDOMAIN_BRANDING: false 230 | SUBDOMAIN_COURSE_LISTINGS: false 231 | 232 | REGISTRATION_EXTRA_FIELDS: 233 | level_of_education: optional 234 | gender: optional 235 | year_of_birth: optional 236 | mailing_address: optional 237 | goals: optional 238 | honor_code: required 239 | city: required 240 | country: required 241 | 242 | GRADES_DOWNLOAD: 243 | STORAGE_TYPE: localfs 244 | BUCKET: edx-grades 245 | ROOT_PATH: /edx/var/edxapp/grades 246 | 247 | ECOMMERCE_API_URL: http://localhost:8080/api/v2/ 248 | ECOMMERCE_PUBLIC_URL_ROOT: http://localhost:8080/ 249 | ECOMMERCE_SERVICE_WORKER_USERNAME: ecommerce_worker 250 | ECOMMERCE_NOTIFICATION_URL: http://localhost:8080/payment/paybox/notify/ 251 | 252 | JWT_ISSUER: http://localhost:8000/oauth2 253 | JWT_EXPIRATION: 30 254 | 255 | OAUTH_ENFORCE_SECURE: false 256 | 257 | CORS_ALLOW_CREDENTIALS: true 258 | CORS_ALLOW_INSECURE: true 259 | CORS_ORIGIN_ALLOW_ALL: true 260 | CROSS_DOMAIN_CSRF_COOKIE_NAME: edx_csrf_token 261 | CROSS_DOMAIN_CSRF_COOKIE_DOMAIN: .local.dev 262 | 263 | PLATFORM_RICHIE_URL: http://richie.local.dev:8070 264 | 265 | VERIFIED_COHORTS: [] 266 | 267 | FONZIE_JWT_SIGNING_KEY: ThisIsAnExampleKeyForDevPurposeOnly 268 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/lms/storage.py: -------------------------------------------------------------------------------- 1 | """Django static file storage backend for OpenEdX.""" 2 | from django.conf import settings 3 | 4 | from pipeline.storage import PipelineCachedStorage 5 | 6 | from openedx.core.storage import ProductionStorage 7 | 8 | 9 | class CDNMixin(object): 10 | """Mixin to activate CDN urls on a static files storage backend.""" 11 | 12 | def url(self, name, force=False): 13 | """Prepend static files path by the CDN base url when configured in settings.""" 14 | url = super(CDNMixin, self).url(name, force=force) 15 | 16 | cdn_base_url = getattr(settings, "CDN_BASE_URL", None) 17 | if cdn_base_url: 18 | url = "{:s}{:s}".format(cdn_base_url, url) 19 | 20 | return url 21 | 22 | 23 | class CDNProductionStorage(CDNMixin, ProductionStorage): 24 | """Open edX LMS production static files storage backend that can be placed behing a CDN.""" 25 | 26 | 27 | class CDNPipelineCachedStorage(CDNMixin, PipelineCachedStorage): 28 | """Open edX Studio production static files storage backend that can be placed behing a CDN.""" 29 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/config/lms/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import yaml 4 | import os 5 | 6 | from django.core.exceptions import ImproperlyConfigured 7 | 8 | 9 | class Configuration(dict): 10 | """ 11 | Try getting a setting from the settings.yml or secrets.yml files placed in 12 | the directory passed when initializing the configuration instance. 13 | """ 14 | 15 | def __init__(self, dir=None, *args, **kwargs): 16 | """ 17 | Initialize with the path to the directory in which the configuration is 18 | to be found. 19 | """ 20 | super(Configuration, self).__init__(*args, **kwargs) 21 | 22 | if dir is None: 23 | self.settings = {} 24 | 25 | else: 26 | # Load the content of a `settings.yml` file placed in the current 27 | # directory if any. This file is where customizable settings are stored 28 | # for a given environment. 29 | try: 30 | with open(os.path.join(dir, "settings.yml")) as f: 31 | settings = yaml.load(f.read()) or {} 32 | except IOError: 33 | settings = {} 34 | # Load the content of a `secrets.yml` file placed in the current 35 | # directory if any. This file is where sensitive credentials are stored 36 | # for a given environment. 37 | try: 38 | with open(os.path.join(dir, "secrets.yml")) as f: 39 | credentials = yaml.load(f.read()) or {} 40 | except IOError: 41 | credentials = {} 42 | 43 | settings.update(credentials) 44 | self.settings = settings 45 | 46 | def __call__(self, var_name, formatter=str, *args, **kwargs): 47 | """ 48 | The config returns in order of priority: 49 | 50 | - the value set in the secrets.yml file, 51 | - the value set in the settings.yml file, 52 | - the value set as environment variable 53 | - the value passed as default. 54 | 55 | If the value is passed as a string, a type is forced via the function passed in 56 | the "formatter" kwarg. 57 | 58 | Raise an "ImproperlyConfigured" error if the name is not found, except 59 | if the `default` key is given in kwargs (using kwargs allows to pass a 60 | default to None, which is different from not passing any default): 61 | 62 | $ config = Configuration('path/to/config/directory') 63 | $ config('foo') # raise ImproperlyConfigured error if `foo` is not defined 64 | $ config('foo', default='bar') # return 'bar' if `foo` is not defined 65 | $ config('foo', default=None) # return `None` if `foo` is not defined 66 | """ 67 | try: 68 | value = self.settings[var_name] 69 | except KeyError: 70 | try: 71 | value = formatter(os.environ[var_name]) 72 | except KeyError: 73 | if "default" in kwargs: 74 | value = kwargs["default"] 75 | else: 76 | raise ImproperlyConfigured( 77 | 'Please set the "{:s}" variable in a settings.yml file, a secrets.yml ' 78 | "file or an environment variable.".format(var_name) 79 | ) 80 | # If a formatter is specified, force the value but only if it was passed as a string 81 | if isinstance(value, basestring): 82 | value = formatter(value.encode("utf-8")) 83 | 84 | return value 85 | 86 | def get(self, name, *args, **kwargs): 87 | """ 88 | edX is loading the content of 2 json files to settings.ENV_TOKEN and settings.AUTH_TOKEN 89 | They have started calling these attributes anywhere in the code base, so we must make 90 | sure that the following call works (and the same for AUTH_TOKEN): 91 | 92 | settings.ENV_TOKEN.get('ANY_SETTING_NAME') 93 | 94 | That's what this method will do after we add this to our settings: 95 | ``` 96 | config = Configuration('path/to/my/settings/directory.yml') 97 | ENV_TOKEN = config 98 | AUTH_TOKEN = config 99 | ``` 100 | """ 101 | try: 102 | default = args[0] 103 | except IndexError: 104 | # As a first approach, all defaults that are not provided by Open edX are set to None. 105 | # If this creates a problem, we can either: 106 | # - make sure we provide a value for this setting in our yaml files, 107 | # - make a PR to Open edX to provide a better default for this setting. 108 | default = None 109 | return self(name, default=default) 110 | 111 | 112 | def prefer_fun_video(identifier, entry_points): 113 | """ 114 | This function will be affected to XBLOCK_SELECT_FUNCTION which is used to 115 | filter python package entrypoints each time an xblock is instanciated. 116 | It replaces `video` xblocks python class by `libcast_xblock`'s one. 117 | """ 118 | from django.conf import settings 119 | from xmodule.modulestore import prefer_xmodules 120 | 121 | if identifier == "video": 122 | import pkg_resources 123 | from xblock.core import XBlock 124 | 125 | # These entry points are listed in the setup.py of the libcast module 126 | # Inspired by the XBlock.load_class method 127 | entry_points = list( 128 | pkg_resources.iter_entry_points(XBlock.entry_point, name="libcast_xblock") 129 | ) 130 | return prefer_xmodules(identifier, entry_points) 131 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/demo-course.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfun/openedx-docker/4d7664c97b59f201230f0b2b48f7cfc255496bf5/releases/dogwood/3/fun/demo-course.tar.gz -------------------------------------------------------------------------------- /releases/dogwood/3/fun/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Development entrypoint 4 | # 5 | 6 | # Activate user's virtualenv 7 | source /edx/app/edxapp/venv/bin/activate 8 | 9 | # Override default root_urls for the lms app 10 | ln -sf /config/lms/root_urls.py /edx/app/edxapp/edx-platform/lms/ 11 | 12 | exec "$@" 13 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/patches/edx-platform_dogwood.3-fun_XBlock.patch: -------------------------------------------------------------------------------- 1 | diff --git a/requirements/edx/github.txt b/requirements/edx/github.txt 2 | --- a/requirements/edx/github.txt 3 | +++ b/requirements/edx/github.txt 4 | @@ -71,7 +71,7 @@ 5 | git+https://github.com/edx/lettuce.git@0.2.20.002#egg=lettuce==0.2.20.002 6 | 7 | # Our libraries: 8 | -git+https://github.com/edx/XBlock.git@xblock-0.4.5#egg=XBlock==0.4.5 9 | +git+https://github.com/openedx/XBlock.git@xblock-0.4.5#egg=XBlock==0.4.5 10 | -e git+https://github.com/edx/codejail.git@6b17c33a89bef0ac510926b1d7fea2748b73aadd#egg=codejail 11 | -e git+https://github.com/edx/js-test-tool.git@v0.1.6#egg=js_test_tool 12 | -e git+https://github.com/edx/event-tracking.git@0.2.1#egg=event-tracking==0.2.1 13 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/patches/edx-platform_dogwood.3-fun_moto.patch: -------------------------------------------------------------------------------- 1 | diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt 2 | index 19c655d79e..457d719e30 100644 3 | --- a/requirements/edx/base.txt 4 | +++ b/requirements/edx/base.txt 5 | @@ -140,7 +140,7 @@ flaky==2.4.0 6 | freezegun==0.1.11 7 | mock-django==0.6.9 8 | mock==1.0.1 9 | -moto==0.3.1 10 | +#moto==0.3.1 version not available anymore but we don't need to run the tests anyway 11 | nose==1.3.7 12 | nose-exclude 13 | nose-ignore-docstring 14 | 15 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/patches/edx-platform_dogwood.3-fun_strftime-1900.patch: -------------------------------------------------------------------------------- 1 | diff --git a/common/lib/xmodule/xmodule/fields.py b/common/lib/xmodule/xmodule/fields.py 2 | index a3c6aa19d7..2b73d5b63f 100644 3 | --- a/common/lib/xmodule/xmodule/fields.py 4 | +++ b/common/lib/xmodule/xmodule/fields.py 5 | @@ -73,6 +73,10 @@ class Date(JSONField): 6 | return time.strftime('%Y-%m-%dT%H:%M:%SZ', value) 7 | elif isinstance(value, datetime.datetime): 8 | if value.tzinfo is None or value.utcoffset().total_seconds() == 0: 9 | + if value.year < 1900: 10 | + # strftime doesn't work for pre-1900 dates, so use 11 | + # isoformat instead 12 | + return value.isoformat() 13 | # isoformat adds +00:00 rather than Z 14 | return value.strftime('%Y-%m-%dT%H:%M:%SZ') 15 | else: 16 | 17 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/pip.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | trusted-host = pypi.python.org 3 | pypi.org 4 | files.pythonhosted.org 5 | -------------------------------------------------------------------------------- /releases/dogwood/3/fun/requirements.txt: -------------------------------------------------------------------------------- 1 | # FUN dependencies 2 | --extra-index-url https://pypi.fury.io/openfun/ 3 | 4 | # ==== core ==== 5 | edx-gea==0.2.0 6 | fonzie==0.7.0 7 | fun-apps==5.21.0 8 | 9 | # ==== xblocks ==== 10 | configurable_lti_consumer-xblock==1.4.1 11 | glowbl-xblock==0.2.1 12 | ipython-xblock==0.2.0 13 | libcast-xblock==0.5.0 14 | password-container-xblock==0.3.0 15 | proctoru-xblock==1.2.0 16 | xblock-proctor-exam==1.0.0 17 | xblock-utils2==0.3.0 18 | 19 | # ==== third-party apps ==== 20 | celery-redis-sentinel==0.3.0 21 | # django-classy-tags 2.0.0 dropped support for python 2.x 22 | django-classy-tags==0.8.0 23 | # django-django-redis-sentinel-redux is not compatible with 24 | # django-redis > 4.5.0 25 | django-redis==4.5.0 26 | django-redis-sentinel-redux==0.2.0 27 | django-redis-sessions==0.6.1 28 | djangorestframework-simplejwt==3.3 29 | # Add Gelf support for logging 30 | djehouty==0.1.5 31 | raven==6.9.0 32 | redis==2.10.6 33 | # Upgrade requests and urllib3 to prevent SSL certificate validation failure 34 | # (we should use pyOpenSSL instead of the local openssl library). For reference, see: 35 | # https://urllib3.readthedocs.io/en/latest/user-guide.html#ssl-py2 36 | requests==2.22.0 37 | urllib3[secure]==1.25.7 38 | 39 | # The version of gunicorn shipped with Open edX is too old. We want to use a 40 | # recent version to be able to configure threads with gthread worker class. 41 | gunicorn==19.9.0 42 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/activate: -------------------------------------------------------------------------------- 1 | export EDX_RELEASE="eucalyptus.3" 2 | export FLAVOR="wb" 3 | export EDX_RELEASE_REF="eucalyptus.3-wb" 4 | export EDX_ARCHIVE_URL="https://github.com/openfun/edx-platform/archive/${EDX_RELEASE_REF}.tar.gz" 5 | export EDX_DEMO_RELEASE_REF="open-release/eucalyptus.3" 6 | export EDX_DEMO_ARCHIVE_URL="file://${PWD}/releases/eucalyptus/3/wb/demo-course.tar.gz" 7 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/config/cms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfun/openedx-docker/4d7664c97b59f201230f0b2b48f7cfc255496bf5/releases/eucalyptus/3/wb/config/cms/__init__.py -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/config/cms/docker_build_development.py: -------------------------------------------------------------------------------- 1 | # This is a minimal settings file allowing us to run "update_assets" 2 | # in the Dockerfile 3 | from .docker_run_development import * 4 | 5 | DATABASES = {"default": {}} 6 | 7 | XQUEUE_INTERFACE = {"url": None, "django_auth": None} 8 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/config/cms/docker_build_production.py: -------------------------------------------------------------------------------- 1 | # This is a minimal settings file allowing us to run "update_assets" 2 | # in the Dockerfile 3 | from .docker_run_production import * 4 | 5 | DATABASES = {"default": {}} 6 | 7 | XQUEUE_INTERFACE = {"url": None, "django_auth": None} 8 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/config/cms/docker_run.py: -------------------------------------------------------------------------------- 1 | # This file is meant to import the environment related settings file 2 | 3 | from docker_run_production import * 4 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/config/cms/docker_run_development.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `development` environment for the CMS, starting from 2 | # the settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from lms.envs.fun.utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | if "sentry" in LOGGING.get("handlers"): 11 | LOGGING["handlers"]["sentry"]["environment"] = "development" 12 | 13 | DEBUG = True 14 | REQUIRE_DEBUG = True 15 | 16 | EMAIL_BACKEND = config( 17 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 18 | ) 19 | 20 | 21 | PIPELINE_ENABLED = False 22 | STATICFILES_STORAGE = "openedx.core.storage.DevelopmentStorage" 23 | 24 | ALLOWED_HOSTS = ["*"] 25 | FEATURES["AUTOMATIC_AUTH_FOR_TESTING"] = True 26 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/config/cms/docker_run_preprod.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `preprod` environment for the LMS 2 | # starting from the settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from lms.envs.fun.utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "preprod" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/config/cms/docker_run_staging.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `staging` environment for the LMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from lms.envs.fun.utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "staging" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/config/lms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfun/openedx-docker/4d7664c97b59f201230f0b2b48f7cfc255496bf5/releases/eucalyptus/3/wb/config/lms/__init__.py -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/config/lms/backends.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from ratelimitbackend.backends import RateLimitModelBackend 4 | 5 | 6 | class ProxyRateLimitModelBackend(RateLimitModelBackend): 7 | """A rate limiting Backend that works behind a proxy.""" 8 | 9 | def get_ip(self, request): 10 | """Return the end user address string as set by proxies.""" 11 | return request.META['HTTP_X_FORWARDED_FOR'] 12 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/config/lms/docker_build_development.py: -------------------------------------------------------------------------------- 1 | # This is a minimal settings file allowing us to run "update_assets" 2 | # in the Dockerfile 3 | from .docker_run_development import * 4 | 5 | DATABASES = {"default": {}} 6 | 7 | XQUEUE_INTERFACE = {"url": None, "django_auth": None} 8 | 9 | CACHES = { 10 | "default": { 11 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 12 | "KEY_PREFIX": "default", 13 | }, 14 | "general": { 15 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 16 | "KEY_PREFIX": "general", 17 | }, 18 | "celery": { 19 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 20 | "KEY_PREFIX": "celery", 21 | }, 22 | "mongo_metadata_inheritance": { 23 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 24 | "KEY_PREFIX": "mongo_metadata_inheritance", 25 | }, 26 | "openassessment_submissions": { 27 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 28 | "KEY_PREFIX": "openassessment_submissions", 29 | }, 30 | "loc_cache": { 31 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 32 | "KEY_PREFIX": "loc_cache", 33 | }, 34 | # Cache backend used by Django 1.8 storage backend while processing static files 35 | "staticfiles": { 36 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 37 | "KEY_PREFIX": "staticfiles", 38 | }, 39 | } 40 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/config/lms/docker_build_production.py: -------------------------------------------------------------------------------- 1 | # This is a minimal settings file allowing us to run "update_assets" 2 | # in the Dockerfile 3 | from .docker_run_production import * 4 | 5 | DATABASES = {"default": {}} 6 | 7 | XQUEUE_INTERFACE = {"url": None, "django_auth": None} 8 | 9 | CACHES = { 10 | "default": { 11 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 12 | "KEY_PREFIX": "default", 13 | }, 14 | "general": { 15 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 16 | "KEY_PREFIX": "general", 17 | }, 18 | "celery": { 19 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 20 | "KEY_PREFIX": "celery", 21 | }, 22 | "mongo_metadata_inheritance": { 23 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 24 | "KEY_PREFIX": "mongo_metadata_inheritance", 25 | }, 26 | "openassessment_submissions": { 27 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 28 | "KEY_PREFIX": "openassessment_submissions", 29 | }, 30 | "loc_cache": { 31 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 32 | "KEY_PREFIX": "loc_cache", 33 | }, 34 | # Cache backend used by Django 1.8 storage backend while processing static files 35 | "staticfiles": { 36 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 37 | "KEY_PREFIX": "staticfiles", 38 | }, 39 | } 40 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/config/lms/docker_run.py: -------------------------------------------------------------------------------- 1 | # This file is meant to import the environment related settings file 2 | 3 | from docker_run_production import * 4 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/config/lms/docker_run_development.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `development` environment for the LMS starting from the 2 | # settings of the `production` environment 3 | 4 | import json 5 | 6 | from docker_run_production import * 7 | from .utils import Configuration 8 | 9 | # Load custom configuration parameters from yaml files 10 | config = Configuration(os.path.dirname(__file__)) 11 | 12 | if "sentry" in LOGGING.get("handlers"): 13 | LOGGING["handlers"]["sentry"]["environment"] = "development" 14 | 15 | DEBUG = True 16 | REQUIRE_DEBUG = True 17 | 18 | EMAIL_BACKEND = config( 19 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 20 | ) 21 | 22 | PIPELINE_ENABLED = False 23 | STATICFILES_STORAGE = "openedx.core.storage.DevelopmentStorage" 24 | 25 | ALLOWED_HOSTS = ["*"] 26 | FEATURES["AUTOMATIC_AUTH_FOR_TESTING"] = True 27 | 28 | AUTHENTICATION_BACKENDS = config( 29 | "AUTHENTICATION_BACKENDS", 30 | default=["django.contrib.auth.backends.ModelBackend"], 31 | formatter=json.loads 32 | ) 33 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/config/lms/docker_run_preprod.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `preprod` environment for the LMS 2 | # starting from the settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from .utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "preprod" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/config/lms/docker_run_staging.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `staging` environment for the LMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from .utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "staging" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/config/lms/storage.py: -------------------------------------------------------------------------------- 1 | """Django static file storage backend for OpenEdX.""" 2 | from django.conf import settings 3 | 4 | from openedx.core.storage import ProductionStorage 5 | 6 | 7 | class CDNProductionStorage(ProductionStorage): 8 | """Open edX production static files storage backend that can be placed behing a CDN.""" 9 | 10 | def url(self, name, force=False): 11 | """Prepend static files path by the CDN base url when configured in settings.""" 12 | url = super(CDNProductionStorage, self).url(name, force=force) 13 | 14 | cdn_base_url = getattr(settings, "CDN_BASE_URL", None) 15 | if cdn_base_url: 16 | url = "{:s}{:s}".format(cdn_base_url, url) 17 | 18 | return url 19 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/config/lms/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import yaml 3 | import os 4 | 5 | from django.core.exceptions import ImproperlyConfigured 6 | 7 | 8 | class Configuration(dict): 9 | """ 10 | Try getting a setting from the settings.yml or secrets.yml files placed in 11 | the directory passed when initializing the configuration instance. 12 | """ 13 | 14 | def __init__(self, dir=None, *args, **kwargs): 15 | """ 16 | Initialize with the path to the directory in which the configuration is 17 | to be found. 18 | """ 19 | super(Configuration, self).__init__(*args, **kwargs) 20 | if dir is None: 21 | self.settings = {} 22 | else: 23 | # Load the content of a `settings.yml` file placed in the current 24 | # directory if any. This file is where customizable settings are stored 25 | # for a given environment. 26 | try: 27 | with open(os.path.join(dir, "settings.yml")) as f: 28 | settings = yaml.load(f.read()) or {} 29 | except IOError: 30 | settings = {} 31 | # Load the content of a `secrets.yml` file placed in the current 32 | # directory if any. This file is where sensitive credentials are stored 33 | # for a given environment. 34 | try: 35 | with open(os.path.join(dir, "secrets.yml")) as f: 36 | credentials = yaml.load(f.read()) or {} 37 | except IOError: 38 | credentials = {} 39 | settings.update(credentials) 40 | self.settings = settings 41 | 42 | def __call__(self, var_name, formatter=str, *args, **kwargs): 43 | """ 44 | The config returns in order of priority: 45 | 46 | - the value set in the secrets.yml file, 47 | - the value set in the settings.yml file, 48 | - the value set as environment variable 49 | - the value passed as default. 50 | 51 | If the value is passed as a string, a type is forced via the function passed in 52 | the "formatter" kwarg. 53 | 54 | Raise an "ImproperlyConfigured" error if the name is not found, except 55 | if the `default` key is given in kwargs (using kwargs allows to pass a 56 | default to None, which is different from not passing any default): 57 | 58 | $ config = Configuration('path/to/config/directory') 59 | $ config('foo') # raise ImproperlyConfigured error if `foo` is not defined 60 | $ config('foo', default='bar') # return 'bar' if `foo` is not defined 61 | $ config('foo', default=None) # return `None` if `foo` is not defined 62 | """ 63 | try: 64 | value = self.settings[var_name] 65 | except KeyError: 66 | try: 67 | value = formatter(os.environ[var_name]) 68 | except KeyError: 69 | if "default" in kwargs: 70 | value = kwargs["default"] 71 | else: 72 | raise ImproperlyConfigured( 73 | 'Please set the "{:s}" variable in a settings.yml file, a secrets.yml ' 74 | "file or an environment variable.".format(var_name) 75 | ) 76 | 77 | # If a formatter is specified, force the value but only if it was passed as a string 78 | if isinstance(value, basestring): 79 | value = formatter(value.encode("utf-8")) 80 | return value 81 | 82 | def get(self, name, *args, **kwargs): 83 | """ 84 | edX is loading the content of 2 json files to settings.ENV_TOKEN and settings.AUTH_TOKEN 85 | They have started calling these attributes anywhere in the code base, so we must make 86 | sure that the following call works (and the same for AUTH_TOKEN): 87 | 88 | settings.ENV_TOKEN.get('ANY_SETTING_NAME') 89 | 90 | That's what this method will do after we add this to our settings: 91 | ``` 92 | config = Configuration('path/to/my/settings/directory.yml') 93 | ENV_TOKEN = config 94 | AUTH_TOKEN = config 95 | ``` 96 | """ 97 | try: 98 | default = args[0] 99 | except IndexError: 100 | # As a first approach, all defaults that are not provided by Open edX are set to None. 101 | # If this creates a problem, we can either: 102 | # - make sure we provide a value for this setting in our yaml files, 103 | # - make a PR to Open edX to provide a better default for this setting. 104 | default = None 105 | return self(name, default=default) 106 | 107 | 108 | def prefer_fun_video(identifier, entry_points): 109 | """ 110 | This function will be affected to XBLOCK_SELECT_FUNCTION which is used to 111 | filter python package entrypoints each time an xblock is instanciated. 112 | It replaces `video` xblocks python class by `libcast_xblock`'s one. 113 | """ 114 | from django.conf import settings 115 | from xmodule.modulestore import prefer_xmodules 116 | 117 | if identifier == "video": 118 | import pkg_resources 119 | from xblock.core import XBlock 120 | 121 | # These entry points are listed in the setup.py of the libcast module 122 | # Inspired by the XBlock.load_class method 123 | entry_points = list( 124 | pkg_resources.iter_entry_points(XBlock.entry_point, name="libcast_xblock") 125 | ) 126 | return prefer_xmodules(identifier, entry_points) 127 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/demo-course.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfun/openedx-docker/4d7664c97b59f201230f0b2b48f7cfc255496bf5/releases/eucalyptus/3/wb/demo-course.tar.gz -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Development entrypoint 4 | # 5 | 6 | # Activate user's virtualenv 7 | source /edx/app/edxapp/venv/bin/activate 8 | exec "$@" 9 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/patches/edx-platform_eucalyptus.3-remove_faulthandler.patch: -------------------------------------------------------------------------------- 1 | diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt 2 | index 9bc8908d72..5b374a18e2 100644 3 | --- a/requirements/edx/base.txt 4 | +++ b/requirements/edx/base.txt 5 | @@ -169,7 +169,6 @@ selenium==2.53.1 6 | splinter==0.5.4 7 | testtools==0.9.34 8 | testfixtures==4.5.0 9 | -nose-faulthandler==0.1 10 | 11 | # Used for Segment analytics 12 | analytics-python==1.1.0 13 | -------------------------------------------------------------------------------- /releases/eucalyptus/3/wb/requirements.txt: -------------------------------------------------------------------------------- 1 | # FUN dependencies 2 | --extra-index-url https://pypi.fury.io/openfun/ 3 | 4 | # ==== core ==== 5 | configurable-lti-consumer-xblock==1.4.1 6 | edx-gea==0.2.0 7 | fun-apps==2.6.0+wb 8 | ipython-xblock==0.2.0 9 | libcast-xblock==0.6.1 10 | password-container-xblock==0.3.0 11 | xblock-proctor-exam==1.0.0 12 | xblock-utils2==0.3.0 13 | 14 | # ==== third-party apps ==== 15 | celery-redis-sentinel==0.3.0 16 | # django-classy-tags 2.0.0 dropped support for python 2.x 17 | django-classy-tags==0.8.0 18 | # django-django-redis-sentinel-redux is not compatible with 19 | # django-redis > 4.5.0 20 | django-redis==4.5.0 21 | django-redis-sentinel-redux==0.2.0 22 | django-redis-sessions==0.6.1 23 | raven==6.9.0 24 | redis==2.10.6 25 | gunicorn==19.9.0 26 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/Dockerfile: -------------------------------------------------------------------------------- 1 | # EDX-PLATFORM multi-stage docker build 2 | 3 | # Change release to build, by providing the EDX_RELEASE_REF build argument to 4 | # your build command: 5 | # 6 | # $ docker build \ 7 | # --build-arg EDX_RELEASE_REF="hawthorn.1" \ 8 | # -t edxapp:hawthorn.1 \ 9 | # . 10 | ARG DOCKER_UID=1000 11 | ARG DOCKER_GID=1000 12 | ARG EDX_RELEASE_REF=hawthorn.1 13 | ARG EDXAPP_STATIC_ROOT=/edx/app/edxapp/staticfiles 14 | ARG NGINX_IMAGE_NAME=nginxinc/nginx-unprivileged 15 | ARG NGINX_IMAGE_TAG=1.20 16 | 17 | # === BASE === 18 | FROM ubuntu:16.04 as base 19 | 20 | # Configure locales & timezone 21 | RUN apt-get update && \ 22 | apt-get install -y \ 23 | gettext \ 24 | locales \ 25 | tzdata && \ 26 | rm -rf /var/lib/apt/lists/* 27 | RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \ 28 | locale-gen 29 | ENV LANG en_US.UTF-8 30 | ENV LANGUAGE en_US:en 31 | ENV LC_ALL en_US.UTF-8 32 | 33 | # === DOWNLOAD === 34 | FROM base as downloads 35 | 36 | WORKDIR /downloads 37 | 38 | # Install curl 39 | RUN apt-get update && \ 40 | apt-get install -y curl 41 | 42 | # Download pip installer for python 2.7 43 | RUN curl -sLo get-pip.py https://bootstrap.pypa.io/pip/2.7/get-pip.py 44 | 45 | # Download edxapp release 46 | # Get default EDX_RELEASE_REF value (defined on top) 47 | ARG EDX_RELEASE_REF 48 | RUN curl -sLo edxapp.tgz https://github.com/openedx/edx-platform/archive/open-release/$EDX_RELEASE_REF.tar.gz && \ 49 | tar xzf edxapp.tgz 50 | 51 | 52 | # === EDXAPP === 53 | FROM base as edxapp 54 | 55 | # Install apt https support (required to use node sources repository) 56 | RUN apt-get update && \ 57 | apt-get upgrade -y && \ 58 | apt-get install -y \ 59 | apt-transport-https 60 | 61 | # Add a recent release of nodejs to apt sources (ubuntu package for precise is 62 | # broken) 63 | RUN echo "deb https://deb.nodesource.com/node_10.x trusty main" \ 64 | > /etc/apt/sources.list.d/nodesource.list && \ 65 | curl -s 'https://deb.nodesource.com/gpgkey/nodesource.gpg.key' | apt-key add - 66 | 67 | # Install base system dependencies 68 | RUN apt-get update && \ 69 | apt-get install -y \ 70 | nodejs \ 71 | python && \ 72 | rm -rf /var/lib/apt/lists/* 73 | 74 | WORKDIR /edx/app/edxapp/edx-platform 75 | 76 | # Get default EDX_RELEASE_REF value (defined on top) 77 | ARG EDX_RELEASE_REF 78 | COPY --from=downloads "/downloads/edx-platform-open-release-${EDX_RELEASE_REF}" . 79 | 80 | COPY ./requirements.txt /edx/app/edxapp/edx-platform/requirements/edx/extend.txt 81 | 82 | # We copy default configuration files to "/config" and we point to them via 83 | # symlinks. That allows to easily override default configurations by mounting a 84 | # docker volume. 85 | COPY ./config /config 86 | RUN ln -sf /config/lms /edx/app/edxapp/edx-platform/lms/envs/fun && \ 87 | ln -sf /config/cms /edx/app/edxapp/edx-platform/cms/envs/fun && \ 88 | ln -sf /config/lms/root_urls.py /edx/app/edxapp/edx-platform/lms/ && \ 89 | ln -sf /config/cms/root_urls.py /edx/app/edxapp/edx-platform/cms/ 90 | 91 | # Add node_modules/.bin to the PATH so that paver-related commands can execute 92 | # node scripts 93 | ENV PATH="/edx/app/edxapp/edx-platform/node_modules/.bin:${PATH}" 94 | 95 | # === BUILDER === 96 | FROM edxapp as builder 97 | 98 | WORKDIR /builder 99 | 100 | # Install builder system dependencies 101 | RUN apt-get update && \ 102 | apt-get upgrade -y && \ 103 | apt-get install -y \ 104 | build-essential \ 105 | gettext \ 106 | git \ 107 | graphviz-dev \ 108 | libgeos-dev \ 109 | libmysqlclient-dev \ 110 | libxml2-dev \ 111 | libxmlsec1-dev \ 112 | python-dev \ 113 | rdfind && \ 114 | rm -rf /var/lib/apt/lists/* 115 | 116 | # Install the latest pip release 117 | COPY --from=downloads /downloads/get-pip.py ./get-pip.py 118 | RUN python get-pip.py 119 | 120 | WORKDIR /edx/app/edxapp/edx-platform 121 | 122 | # Patches 123 | COPY patches/* /tmp/ 124 | 125 | # Patches pre-install 126 | # Patch requirements to install py2neo==3.1.2 from github as this version has been removed from pypi.org 127 | RUN patch -p1 < /tmp/edx-platform_hawthorn.1-oee_requirements-py2neo.patch 128 | 129 | # Patch to fix installation of Python modules 130 | RUN patch -p1 < /tmp/edx-platform_hawthorn.1-oee_moto.patch 131 | 132 | # Install python dependencies 133 | RUN pip install --src /usr/local/src -r requirements/edx/base.txt && \ 134 | pip install -r requirements/edx/extend.txt 135 | 136 | # Patch the CMS to activate our customizable LTI Xblock 137 | RUN patch -p1 < /tmp/edx-platform_hawthorn.1-oee.patch 138 | # Patch ORA2 to hide empty file links 139 | RUN patch -d /usr/local/lib/python2.7/dist-packages/ -p1 < /tmp/edx-ora2_hawthorn.1-oee.patch 140 | 141 | # Install Javascript requirements 142 | RUN npm install 143 | 144 | # Update assets skipping collectstatic (it should be done during deployment) 145 | RUN NO_PREREQ_INSTALL=1 \ 146 | paver update_assets --settings=fun.docker_build_production --skip-collect 147 | 148 | # === STATIC LINKS COLLECTOR === 149 | FROM builder as links_collector 150 | 151 | ARG EDXAPP_STATIC_ROOT 152 | 153 | RUN python manage.py lms collectstatic --link --noinput --settings=fun.docker_build_production && \ 154 | python manage.py cms collectstatic --link --noinput --settings=fun.docker_build_production 155 | 156 | # Replace duplicated file by a symlink to decrease the overall size of the 157 | # final image 158 | RUN rdfind -makesymlinks true -followsymlinks true ${EDXAPP_STATIC_ROOT} 159 | 160 | # === STATIC FILES COLLECTOR === 161 | FROM builder as files_collector 162 | 163 | ARG EDXAPP_STATIC_ROOT 164 | 165 | RUN python manage.py lms collectstatic --noinput --settings=fun.docker_build_production && \ 166 | python manage.py cms collectstatic --noinput --settings=fun.docker_build_production 167 | 168 | # Replace duplicated file by a symlink to decrease the overall size of the 169 | # final image 170 | RUN rdfind -makesymlinks true ${EDXAPP_STATIC_ROOT} 171 | 172 | # === DEVELOPMENT === 173 | FROM builder as development 174 | 175 | ARG DOCKER_UID 176 | ARG DOCKER_GID 177 | ARG EDX_RELEASE_REF 178 | 179 | # Install system dependencies 180 | RUN apt-get update && \ 181 | apt-get upgrade -y && \ 182 | apt-get install -y \ 183 | libsqlite3-dev \ 184 | mongodb && \ 185 | rm -rf /var/lib/apt/lists/* 186 | 187 | RUN groupadd --gid ${DOCKER_GID} edx || \ 188 | echo "Group with ID ${DOCKER_GID} already exists." && \ 189 | useradd \ 190 | --create-home \ 191 | --home-dir /home/edx \ 192 | --uid ${DOCKER_UID} \ 193 | --gid ${DOCKER_GID} \ 194 | edx || \ 195 | echo "Skip user creation (user with ID ${DOCKER_UID} already exists?)" 196 | 197 | # To prevent permission issues related to the non-privileged user running in 198 | # development, we will install development dependencies in a python virtual 199 | # environment belonging to that user 200 | RUN pip install virtualenv 201 | 202 | # Create the virtualenv directory where we will install python development 203 | # dependencies 204 | RUN mkdir -p /edx/app/edxapp/venv && \ 205 | chown -R ${DOCKER_UID}:${DOCKER_GID} /edx/app/edxapp/venv 206 | 207 | # Change edxapp directory owner to allow the development image docker user to 208 | # perform installations from edxapp sources (yeah, I know...) 209 | RUN chown -R ${DOCKER_UID}:${DOCKER_GID} /edx/app/edxapp 210 | 211 | # Copy the entrypoint that will activate the virtualenv 212 | COPY ./entrypoint.sh /usr/local/bin/entrypoint.sh 213 | 214 | # Switch to an un-privileged user matching the host user to prevent permission 215 | # issues with volumes (host folders) 216 | USER ${DOCKER_UID}:${DOCKER_GID} 217 | 218 | # Create the virtualenv with a non-privileged user 219 | RUN virtualenv -p python2.7 --system-site-packages /edx/app/edxapp/venv 220 | 221 | # Install development dependencies in a virtualenv 222 | RUN bash -c "source /edx/app/edxapp/venv/bin/activate && \ 223 | pip install --no-cache-dir -r requirements/edx/testing.txt && \ 224 | pip install --no-cache-dir -r requirements/edx/development.txt" 225 | 226 | ENTRYPOINT [ "/usr/local/bin/entrypoint.sh" ] 227 | 228 | 229 | # === PRODUCTION === 230 | FROM edxapp as production 231 | 232 | ARG EDXAPP_STATIC_ROOT 233 | 234 | # Install runner system dependencies 235 | RUN apt-get update && \ 236 | apt-get upgrade -y && \ 237 | apt-get install -y \ 238 | libgeos-dev \ 239 | libmysqlclient20 \ 240 | libxml2 \ 241 | libxmlsec1-dev \ 242 | lynx \ 243 | tzdata && \ 244 | rm -rf /var/lib/apt/lists/* 245 | 246 | # Copy installed dependencies 247 | COPY --from=builder /usr/local /usr/local 248 | 249 | # Copy modified sources (sic!) 250 | COPY --from=builder /edx/app/edxapp/edx-platform /edx/app/edxapp/edx-platform 251 | 252 | # Copy static files 253 | COPY --from=links_collector ${EDXAPP_STATIC_ROOT} ${EDXAPP_STATIC_ROOT} 254 | 255 | # Now that dependencies are installed and configuration has been set, the above 256 | # statements will run with a un-privileged user. 257 | USER 10000 258 | 259 | # To start the CMS, inject the SERVICE_VARIANT=cms environment variable 260 | # (defaults to "lms") 261 | ENV SERVICE_VARIANT=lms 262 | 263 | # Gunicorn configuration 264 | # 265 | # We want to be able to easily increase gunicorn in hawtorn if needed 266 | ENV GUNICORN_TIMEOUT 300 267 | 268 | # In docker we must increase the number of workers and threads created 269 | # by gunicorn. 270 | # This blogpost explains why and how to do that https://pythonspeed.com/articles/gunicorn-in-docker/ 271 | ENV GUNICORN_WORKERS 3 272 | ENV GUNICORN_THREADS 6 273 | 274 | # Use Gunicorn in production as web server 275 | CMD DJANGO_SETTINGS_MODULE=${SERVICE_VARIANT}.envs.fun.docker_run \ 276 | gunicorn \ 277 | --name=${SERVICE_VARIANT} \ 278 | --bind=0.0.0.0:8000 \ 279 | --max-requests=1000 \ 280 | --timeout=${GUNICORN_TIMEOUT} \ 281 | --workers=${GUNICORN_WORKERS} \ 282 | --threads=${GUNICORN_THREADS} \ 283 | ${SERVICE_VARIANT}.wsgi:application 284 | 285 | # === NGINX === 286 | FROM ${NGINX_IMAGE_NAME}:${NGINX_IMAGE_TAG} as nginx 287 | 288 | ARG EDXAPP_STATIC_ROOT 289 | 290 | # Switch back to the root user to include static files in the container 291 | USER root:root 292 | 293 | RUN mkdir -p ${EDXAPP_STATIC_ROOT} 294 | 295 | COPY --from=files_collector ${EDXAPP_STATIC_ROOT} ${EDXAPP_STATIC_ROOT} 296 | 297 | # Now that everything is included, run the container with a un-privileged user 298 | USER 10000 299 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/activate: -------------------------------------------------------------------------------- 1 | export EDX_RELEASE="hawthorn.1" 2 | export FLAVOR="oee" 3 | export EDX_RELEASE_REF="hawthorn.1" 4 | export EDX_DEMO_RELEASE_REF="open-release/hawthorn.1" 5 | unset EDX_ARCHIVE_URL 6 | unset EDX_DEMO_ARCHIVE_URL 7 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/cms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfun/openedx-docker/4d7664c97b59f201230f0b2b48f7cfc255496bf5/releases/hawthorn/1/oee/config/cms/__init__.py -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/cms/docker_build_development.py: -------------------------------------------------------------------------------- 1 | # This is a minimal settings file allowing us to run "update_assets" 2 | # in the Dockerfile for the development image of the edxapp CMS 3 | 4 | from .docker_build_production import * 5 | 6 | DEBUG = True 7 | REQUIRE_DEBUG = True 8 | 9 | WEBPACK_CONFIG_PATH = "webpack.dev.config.js" 10 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/cms/docker_build_production.py: -------------------------------------------------------------------------------- 1 | # This is a minimal settings file allowing us to run "update_assets" 2 | # in the Dockerfile for the production image of the edxapp CMS 3 | 4 | from openedx.core.lib.derived import derive_settings 5 | 6 | from lms.envs.fun.utils import Configuration 7 | from path import Path as path 8 | 9 | from .docker_run_production import * 10 | 11 | # Load custom configuration parameters 12 | config = Configuration() 13 | 14 | DATABASES = {"default": {}} 15 | 16 | XQUEUE_INTERFACE = {"url": None, "django_auth": None} 17 | 18 | # We need to override STATIC_ROOT because for CMS, edX appends the value of 19 | # "EDX_PLATFORM_REVISION" to it by default and we don't want to use this. 20 | # We should use Django's ManifestStaticFilesStorage for this purpose. 21 | STATIC_URL = "/static/studio/" 22 | STATIC_ROOT = path("/edx/app/edxapp/staticfiles/studio") 23 | 24 | # Allow setting a custom theme 25 | DEFAULT_SITE_THEME = config("DEFAULT_SITE_THEME", default=None) 26 | 27 | ########################## Derive Any Derived Settings ####################### 28 | 29 | derive_settings(__name__) 30 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/cms/docker_run.py: -------------------------------------------------------------------------------- 1 | # This file is meant to import the environment related settings file 2 | 3 | from docker_run_production import * 4 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/cms/docker_run_ci.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `CI` environment for the CMS, starting from 2 | # the settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | 6 | 7 | DEBUG = True 8 | 9 | EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" 10 | 11 | PIPELINE_ENABLED = False 12 | STATICFILES_STORAGE = "openedx.core.storage.DevelopmentStorage" 13 | 14 | ALLOWED_HOSTS = ["*"] 15 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/cms/docker_run_development.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `development` environment for the CMS, starting from 2 | # the settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from lms.envs.fun.utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | if "sentry" in LOGGING.get("handlers"): 11 | LOGGING["handlers"]["sentry"]["environment"] = "development" 12 | 13 | DEBUG = True 14 | REQUIRE_DEBUG = True 15 | 16 | EMAIL_BACKEND = config( 17 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 18 | ) 19 | 20 | PIPELINE_ENABLED = False 21 | STATICFILES_STORAGE = "openedx.core.storage.DevelopmentStorage" 22 | 23 | ALLOWED_HOSTS = ["*"] 24 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/cms/docker_run_feature.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `feature` environment for the CMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from lms.envs.fun.utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "feature" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/cms/docker_run_preprod.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `preprod` environment for the CMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from lms.envs.fun.utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "preprod" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/cms/docker_run_staging.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `staging` environment for the CMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from lms.envs.fun.utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "staging" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/cms/root_urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | OEE urls 3 | We define this file as root url (ROOT_URLCONF) to override and extend 4 | edx-platform's CMS routes 5 | """ 6 | from __future__ import absolute_import, unicode_literals 7 | 8 | from django.conf.urls import include, url 9 | 10 | from cms.urls import urlpatterns # pylint: disable=import-error 11 | 12 | 13 | urlpatterns += [ 14 | url(r'^openassessment/fileupload/', include('openassessment.fileupload.urls')), 15 | ] 16 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/lms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfun/openedx-docker/4d7664c97b59f201230f0b2b48f7cfc255496bf5/releases/hawthorn/1/oee/config/lms/__init__.py -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/lms/backends.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from ratelimitbackend.backends import RateLimitModelBackend 4 | 5 | 6 | class ProxyRateLimitModelBackend(RateLimitModelBackend): 7 | """A rate limiting Backend that works behind a proxy.""" 8 | 9 | def get_ip(self, request): 10 | """Return the end user address string as set by proxies.""" 11 | return request.META['HTTP_X_FORWARDED_FOR'] 12 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/lms/docker_build_development.py: -------------------------------------------------------------------------------- 1 | # This is a minimal settings file allowing us to run "update_assets" 2 | # in the Dockerfile for the development image of the edxapp LMS 3 | 4 | from .docker_build_production import * 5 | 6 | DEBUG = True 7 | REQUIRE_DEBUG = True 8 | 9 | WEBPACK_CONFIG_PATH = "webpack.dev.config.js" 10 | 11 | CACHES = { 12 | "default": { 13 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 14 | "KEY_PREFIX": "default", 15 | }, 16 | "general": { 17 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 18 | "KEY_PREFIX": "general", 19 | }, 20 | "celery": { 21 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 22 | "KEY_PREFIX": "celery", 23 | }, 24 | "mongo_metadata_inheritance": { 25 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 26 | "KEY_PREFIX": "mongo_metadata_inheritance", 27 | }, 28 | "openassessment_submissions": { 29 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 30 | "KEY_PREFIX": "openassessment_submissions", 31 | }, 32 | "loc_cache": { 33 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 34 | "KEY_PREFIX": "loc_cache", 35 | }, 36 | # Cache backend used by Django 1.8 storage backend while processing static files 37 | "staticfiles": { 38 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 39 | "KEY_PREFIX": "staticfiles", 40 | }, 41 | } 42 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/lms/docker_build_production.py: -------------------------------------------------------------------------------- 1 | # This is a minimal settings file allowing us to run "update_assets" 2 | # in the Dockerfile for the production image of the edxapp LMS 3 | 4 | from openedx.core.lib.derived import derive_settings 5 | from path import Path as path 6 | 7 | from .docker_run_production import * 8 | from .utils import Configuration 9 | 10 | # Load custom configuration parameters 11 | config = Configuration() 12 | 13 | DATABASES = {"default": {}} 14 | 15 | XQUEUE_INTERFACE = {"url": None, "django_auth": None} 16 | 17 | STATIC_ROOT = path("/edx/app/edxapp/staticfiles") 18 | 19 | # Allow setting a custom theme 20 | DEFAULT_SITE_THEME = config("DEFAULT_SITE_THEME", default=None) 21 | 22 | CACHES = { 23 | "default": { 24 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 25 | "KEY_PREFIX": "default", 26 | }, 27 | "general": { 28 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 29 | "KEY_PREFIX": "general", 30 | }, 31 | "celery": { 32 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 33 | "KEY_PREFIX": "celery", 34 | }, 35 | "mongo_metadata_inheritance": { 36 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 37 | "KEY_PREFIX": "mongo_metadata_inheritance", 38 | }, 39 | "openassessment_submissions": { 40 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 41 | "KEY_PREFIX": "openassessment_submissions", 42 | }, 43 | "loc_cache": { 44 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 45 | "KEY_PREFIX": "loc_cache", 46 | }, 47 | # Cache backend used by Django 1.8 storage backend while processing static files 48 | "staticfiles": { 49 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 50 | "KEY_PREFIX": "staticfiles", 51 | }, 52 | } 53 | 54 | 55 | ########################## Derive Any Derived Settings ####################### 56 | 57 | derive_settings(__name__) 58 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/lms/docker_run.py: -------------------------------------------------------------------------------- 1 | # This file is meant to import the environment related settings file 2 | 3 | from docker_run_production import * 4 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/lms/docker_run_ci.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `CI` environment for the LMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | 6 | 7 | DEBUG = True 8 | 9 | EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" 10 | 11 | PIPELINE_ENABLED = False 12 | STATICFILES_STORAGE = "openedx.core.storage.DevelopmentStorage" 13 | 14 | ALLOWED_HOSTS = ["*"] 15 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/lms/docker_run_development.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `development` environment for the LMS starting from the 2 | # settings of the `production` environment 3 | 4 | import json 5 | 6 | from docker_run_production import * 7 | from .utils import Configuration 8 | 9 | # Load custom configuration parameters from yaml files 10 | config = Configuration(os.path.dirname(__file__)) 11 | 12 | if "sentry" in LOGGING.get("handlers"): 13 | LOGGING["handlers"]["sentry"]["environment"] = "development" 14 | 15 | DEBUG = True 16 | REQUIRE_DEBUG = True 17 | 18 | EMAIL_BACKEND = config( 19 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 20 | ) 21 | 22 | PIPELINE_ENABLED = False 23 | STATICFILES_STORAGE = "openedx.core.storage.DevelopmentStorage" 24 | 25 | ALLOWED_HOSTS = ["*"] 26 | 27 | WEBPACK_CONFIG_PATH = "webpack.dev.config.js" 28 | 29 | AUTHENTICATION_BACKENDS = config( 30 | "AUTHENTICATION_BACKENDS", 31 | default=["django.contrib.auth.backends.ModelBackend"], 32 | formatter=json.loads 33 | ) 34 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/lms/docker_run_feature.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `feature` environment for the LMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from .utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "feature" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/lms/docker_run_preprod.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `preprod` environment for the LMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from .utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "preprod" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/lms/docker_run_staging.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `staging` environment for the LMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from .utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "staging" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/lms/root_urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | OEE urls 3 | We define this file as root url (ROOT_URLCONF) to override and extend 4 | edx-platform's LMS routes 5 | """ 6 | from __future__ import absolute_import, unicode_literals 7 | 8 | from django.conf.urls import include, url 9 | 10 | from lms.urls import urlpatterns # pylint: disable=import-error 11 | 12 | 13 | urlpatterns += [ 14 | # Fonzie API urls 15 | url(r"^api/", include("fonzie.urls", namespace="fonzie")), 16 | ] 17 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/lms/storage.py: -------------------------------------------------------------------------------- 1 | """Django static file storage backend for OpenEdX.""" 2 | from django.conf import settings 3 | 4 | from openedx.core.storage import ProductionStorage 5 | 6 | 7 | class CDNProductionStorage(ProductionStorage): 8 | """Open edX production static files storage backend that can be placed behing a CDN.""" 9 | 10 | def url(self, name, force=False): 11 | """Prepend static files path by the CDN base url when configured in settings.""" 12 | url = super(CDNProductionStorage, self).url(name, force=force) 13 | 14 | cdn_base_url = getattr(settings, "CDN_BASE_URL", None) 15 | if cdn_base_url: 16 | url = "{:s}{:s}".format(cdn_base_url, url) 17 | 18 | return url 19 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/config/lms/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import yaml 4 | import os 5 | 6 | from django.core.exceptions import ImproperlyConfigured 7 | 8 | 9 | class Configuration(dict): 10 | """ 11 | Try getting a setting from the settings.yml or secrets.yml files placed in 12 | the directory passed when initializing the configuration instance. 13 | """ 14 | 15 | def __init__(self, dir=None, *args, **kwargs): 16 | """ 17 | Initialize with the path to the directory in which the configuration is 18 | to be found. 19 | """ 20 | super(Configuration, self).__init__(*args, **kwargs) 21 | 22 | if dir is None: 23 | self.settings = {} 24 | 25 | else: 26 | # Load the content of a `settings.yml` file placed in the current 27 | # directory if any. This file is where customizable settings are stored 28 | # for a given environment. 29 | try: 30 | with open(os.path.join(dir, "settings.yml")) as f: 31 | settings = yaml.load(f.read()) or {} 32 | except IOError: 33 | settings = {} 34 | 35 | # Load the content of a `secrets.yml` file placed in the current 36 | # directory if any. This file is where sensitive credentials are stored 37 | # for a given environment. 38 | try: 39 | with open(os.path.join(dir, "secrets.yml")) as f: 40 | credentials = yaml.load(f.read()) or {} 41 | except IOError: 42 | credentials = {} 43 | 44 | settings.update(credentials) 45 | self.settings = settings 46 | 47 | def __call__(self, var_name, formatter=str, *args, **kwargs): 48 | """ 49 | The config returns in order of priority: 50 | 51 | - the value set in the secrets.yml file, 52 | - the value set in the settings.yml file, 53 | - the value set as environment variable 54 | - the value passed as default. 55 | 56 | If the value is passed as a string, a type is forced via the function passed in 57 | the "formatter" kwarg. 58 | 59 | Raise an "ImproperlyConfigured" error if the name is not found, except 60 | if the `default` key is given in kwargs (using kwargs allows to pass a 61 | default to None, which is different from not passing any default): 62 | 63 | $ config = Configuration('path/to/config/directory') 64 | $ config('foo') # raise ImproperlyConfigured error if `foo` is not defined 65 | $ config('foo', default='bar') # return 'bar' if `foo` is not defined 66 | $ config('foo', default=None) # return `None` if `foo` is not defined 67 | """ 68 | try: 69 | value = self.settings[var_name] 70 | except KeyError: 71 | try: 72 | value = formatter(os.environ[var_name]) 73 | except KeyError: 74 | if "default" in kwargs: 75 | value = kwargs["default"] 76 | else: 77 | raise ImproperlyConfigured( 78 | 'Please set the "{:s}" variable in a settings.yml file, a secrets.yml ' 79 | "file or an environment variable.".format(var_name) 80 | ) 81 | # If a formatter is specified, force the value but only if it was passed as a string 82 | if isinstance(value, basestring): 83 | value = formatter(value.encode("utf-8")) 84 | 85 | return value 86 | 87 | def get(self, name, *args, **kwargs): 88 | """ 89 | edX is loading the content of 2 json files to settings.ENV_TOKEN and settings.AUTH_TOKEN 90 | They have started calling these attributes anywhere in the code base, so we must make 91 | sure that the following call works (and the same for AUTH_TOKEN): 92 | 93 | settings.ENV_TOKEN.get('ANY_SETTING_NAME') 94 | 95 | That's what this method will do after we add this to our settings: 96 | ``` 97 | config = Configuration('path/to/my/settings/directory.yml') 98 | ENV_TOKEN = config 99 | AUTH_TOKEN = config 100 | ``` 101 | """ 102 | try: 103 | default = args[0] 104 | except IndexError: 105 | # As a first approach, all defaults that are not provided by Open edX are set to None. 106 | # If this creates a problem, we can either: 107 | # - make sure we provide a value for this setting in our yaml files, 108 | # - make a PR to Open edX to provide a better default for this setting. 109 | default = None 110 | return self(name, default=default) 111 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Development entrypoint 4 | # 5 | 6 | # Activate user's virtualenv 7 | source /edx/app/edxapp/venv/bin/activate 8 | 9 | # Override default root_urls 10 | ln -sf /config/lms/root_urls.py /edx/app/edxapp/edx-platform/lms/ 11 | ln -sf /config/cms/root_urls.py /edx/app/edxapp/edx-platform/cms/ 12 | 13 | exec "$@" 14 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/patches/edx-ora2_hawthorn.1-oee.patch: -------------------------------------------------------------------------------- 1 | diff --git a/openassessment/templates/openassessmentblock/oa_uploaded_file.html b/openassessment/templates/openassessmentblock/oa_uploaded_file.html 2 | index 214fa75b..b07f3b0c 100644 3 | --- a/openassessment/templates/openassessmentblock/oa_uploaded_file.html 4 | +++ b/openassessment/templates/openassessmentblock/oa_uploaded_file.html 5 | @@ -13,6 +13,7 @@ 6 |
7 |
8 | {% for file_url, file_description in file_urls %} 9 | + {% if file_description %} 10 |
11 | {% if file_upload_type == "image" %} 12 | {% if file_description %} 13 | @@ -30,6 +31,7 @@ 14 | 15 | {% endif %} 16 |
17 | + {% endif %} 18 | {% endfor %} 19 |
20 | {% if show_warning %} 21 | 22 | 23 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/patches/edx-platform_hawthorn.1-oee.patch: -------------------------------------------------------------------------------- 1 | diff --git a/cms/djangoapps/contentstore/views/component.py b/cms/djangoapps/contentstore/views/component.py 2 | index f65f281151..af47c239de 100644 3 | --- a/cms/djangoapps/contentstore/views/component.py 4 | +++ b/cms/djangoapps/contentstore/views/component.py 5 | @@ -27,6 +27,15 @@ from xblock_django.models import XBlockStudioConfigurationFlag 6 | from xmodule.modulestore.django import modulestore 7 | from xmodule.modulestore.exceptions import ItemNotFoundError 8 | 9 | +# Try to import the configurable LTI consumer xblock that overrides Open edX 10 | +# default LTI xblock. It has no effect if this alternative xblock is not 11 | +# installed. 12 | +try: 13 | + from configurable_lti_consumer import add_dynamic_components 14 | +except ImportError: 15 | + add_dynamic_components = None 16 | + 17 | + 18 | __all__ = [ 19 | 'container_handler', 20 | 'component_handler' 21 | @@ -359,6 +368,17 @@ def get_component_templates(courselike, library=False): 22 | "support_legend": create_support_legend_dict() 23 | } 24 | advanced_component_types = _advanced_component_types(allow_unsupported) 25 | + 26 | + # Read LTI_XBLOCK_CONFIGURATIONS setting to add links to the `Advanced` component button 27 | + if add_dynamic_components: 28 | + add_dynamic_components( 29 | + getattr(settings, "LTI_XBLOCK_CONFIGURATIONS"), 30 | + advanced_component_templates, 31 | + categories, 32 | + create_template_dict, 33 | + course_advanced_keys 34 | + ) 35 | + 36 | # Set component types according to course policy file 37 | if isinstance(course_advanced_keys, list): 38 | for category in course_advanced_keys: 39 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/patches/edx-platform_hawthorn.1-oee_moto.patch: -------------------------------------------------------------------------------- 1 | diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt 2 | --- a/requirements/edx/testing.txt 3 | +++ b/requirements/edx/testing.txt 4 | @@ -203,7 +203,7 @@ 5 | mock==1.0.1 6 | mongoengine==0.10.0 7 | more-itertools==4.2.0 # via pytest 8 | -moto==0.3.1 9 | +# moto==0.3.1 version not available anymore but we don't need to run the tests anyway 10 | mysql-python==1.2.5 11 | needle==0.5.0 # via bok-choy 12 | networkx==1.7 13 | 14 | diff --git a/requirements/edx/testing.in b/requirements/edx/testing.in 15 | --- a/requirements/edx/testing.in 16 | +++ b/requirements/edx/testing.in 17 | @@ -26,7 +26,7 @@ 18 | freezegun # Allows tests to mock the output of assorted datetime module functions 19 | httpretty # Library for mocking HTTP requests, used in many tests 20 | isort # For checking and fixing the order of imports 21 | -moto==0.3.1 # Lets tests mock AWS access via the boto library 22 | +# moto==0.3.1 # Version not available anymore but we don't need to run the tests anyway 23 | nose # Former test runner, we're still using some utility functions from it 24 | pa11ycrawler # Python crawler (using Scrapy) that uses Pa11y to check accessibility of pages as it crawls 25 | pycodestyle # Checker for compliance with the Python style guide (PEP 8) 26 | 27 | diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt 28 | --- a/requirements/edx/development.txt 29 | +++ b/requirements/edx/development.txt 30 | @@ -212,7 +212,7 @@ 31 | modernize==0.6.1 32 | mongoengine==0.10.0 33 | more-itertools==4.2.0 34 | -moto==0.3.1 35 | +# moto==0.3.1 version not available anymore but we don't need to run the tests anyway 36 | mysql-python==1.2.5 37 | needle==0.5.0 38 | networkx==1.7 39 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/patches/edx-platform_hawthorn.1-oee_requirements-py2neo.patch: -------------------------------------------------------------------------------- 1 | diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt 2 | index f90b796..b5a7d62 100644 3 | --- a/requirements/edx/base.txt 4 | +++ b/requirements/edx/base.txt 5 | @@ -183,7 +183,8 @@ piexif==1.0.2 6 | pillow==3.4.0 7 | polib==1.1.0 # via edx-i18n-tools 8 | psutil==1.2.1 9 | -py2neo==3.1.2 10 | +# Old versions of py2neo have been moved into a package called py2neo-history 11 | +py2neo-history==3.1.2 12 | pycontracts==1.7.1 13 | pycountry==1.20 14 | pycparser==2.18 15 | 16 | diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt 17 | index 4d69e60..c4662b0 100644 18 | --- a/requirements/edx/development.txt 19 | +++ b/requirements/edx/development.txt 20 | @@ -238,7 +238,8 @@ pip-tools==2.0.2 21 | pluggy==0.6.0 22 | polib==1.1.0 23 | psutil==1.2.1 24 | -py2neo==3.1.2 25 | +# Old versions of py2neo have been moved into a package called py2neo-history 26 | +py2neo-history==3.1.2 27 | py==1.5.4 28 | pyasn1-modules==0.2.2 29 | pyasn1==0.4.3 30 | 31 | 32 | diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt 33 | index 23dd44a..44ed71e 100644 34 | --- a/requirements/edx/testing.txt 35 | +++ b/requirements/edx/testing.txt 36 | @@ -227,7 +227,8 @@ pillow==3.4.0 37 | pluggy==0.6.0 # via pytest, tox 38 | polib==1.1.0 39 | psutil==1.2.1 40 | -py2neo==3.1.2 41 | +# Old versions of py2neo have been moved into a package called py2neo-history 42 | +py2neo-history==3.1.2 43 | py==1.5.4 # via pytest, tox 44 | pyasn1-modules==0.2.2 # via service-identity 45 | pyasn1==0.4.3 # via pyasn1-modules, service-identity 46 | -------------------------------------------------------------------------------- /releases/hawthorn/1/oee/requirements.txt: -------------------------------------------------------------------------------- 1 | # Open edX extended: dependencies 2 | 3 | # ==== core ==== 4 | fonzie==0.2.1 5 | 6 | # ==== xblocks ==== 7 | configurable_lti_consumer-xblock==1.4.1 8 | 9 | # pin ora2 to a version that works with the filesystem 10 | git+https://github.com/edx/edx-ora2.git@2.2.7#egg=ora2==2.2.7 11 | 12 | # ==== third-party apps ==== 13 | celery-redis-sentinel==0.3.0 14 | # django-django-redis-sentinel-redux is not compatible with 15 | # django-redis > 4.5.0 16 | django-redis==4.5.0 17 | django-redis-sentinel-redux==0.2.0 18 | django-redis-sessions==0.6.1 19 | raven==6.9.0 20 | redis==2.10.6 21 | gunicorn==19.9.0 22 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic 7 | Versioning](https://semver.org/spec/v2.0.0.html) for each flavored OpenEdx 8 | release. 9 | 10 | ## [Unreleased] 11 | 12 | ## [ironwood.2-oee-1.1.1] - 2022-04-14 13 | 14 | ### Fixed 15 | 16 | - Upgrade configurable_lti_consumer-xblock to version 1.4.1 17 | to allow screen sharing 18 | 19 | ## [ironwood.2-oee-1.1.0] - 2022-04-13 20 | 21 | ### Changed 22 | 23 | - Use Nginx Inc's unprivileged image instead of our custom image for OpenShift 24 | - Upgrade configurable_lti_consumer-xblock to version 1.4.0 25 | 26 | ## [ironwood.2-oee-1.0.5] - 2021-09-28 27 | 28 | ### Fixed 29 | 30 | - Set `SESSION_COOKIE_SECURE` to True by default 31 | - Fix build by installing py2neo 3.1.2 from its github repository 32 | 33 | ## [ironwood.2-oee-1.0.4] - 2021-03-04 34 | 35 | ### Fixed 36 | 37 | - Fix ORA2 urls that were breaking assets 38 | - Fix pip install for python 2.7 39 | 40 | ## [ironwood.2-oee-1.0.3] - 2020-11-20 41 | 42 | ### Fixed 43 | 44 | - Hide broken file links on ORA2 upload widget 45 | 46 | ## [ironwood.2-oee-1.0.2] - 2020-11-09 47 | 48 | ### Fixed 49 | 50 | - Make ORA2 work with the filesystem backend 51 | 52 | ## [ironwood.2-oee-1.0.1] - 2020-09-16 53 | 54 | ### Changed 55 | 56 | - Update default password policy 57 | 58 | ## [ironwood.2-oee-1.0.0] - 2020-09-10 59 | 60 | - First release of an `ironwood.2-oee` Docker image. 61 | 62 | [unreleased]: https://github.com/openfun/openedx-docker/compare/ironwood.2-oee-1.1.1...HEAD 63 | [ironwood.2-oee-1.1.1]: https://github.com/openfun/openedx-docker/compare/ironwood.2-oee-1.1.0...ironwood.2-oee-1.1.1 64 | [ironwood.2-oee-1.1.0]: https://github.com/openfun/openedx-docker/compare/ironwood.2-oee-1.0.5...ironwood.2-oee-1.1.0 65 | [ironwood.2-oee-1.0.5]: https://github.com/openfun/openedx-docker/compare/ironwood.2-oee-1.0.4...ironwood.2-oee-1.0.5 66 | [ironwood.2-oee-1.0.4]: https://github.com/openfun/openedx-docker/compare/ironwood.2-oee-1.0.3...ironwood.2-oee-1.0.4 67 | [ironwood.2-oee-1.0.3]: https://github.com/openfun/openedx-docker/compare/ironwood.2-oee-1.0.2...ironwood.2-oee-1.0.3 68 | [ironwood.2-oee-1.0.2]: https://github.com/openfun/openedx-docker/compare/ironwood.2-oee-1.0.1...ironwood.2-oee-1.0.2 69 | [ironwood.2-oee-1.0.1]: https://github.com/openfun/openedx-docker/compare/ironwood.2-oee-1.0.0...ironwood.2-oee-1.0.1 70 | [ironwood.2-oee-1.0.0]: https://github.com/openfun/openedx-docker/releases/tag/ironwood.2-oee-1.0.0 71 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/Dockerfile: -------------------------------------------------------------------------------- 1 | # EDX-PLATFORM multi-stage docker build 2 | 3 | # Change release to build, by providing the EDX_RELEASE_REF build argument to 4 | # your build command: 5 | # 6 | # $ docker build \ 7 | # --build-arg EDX_RELEASE_REF="ironwood.2" \ 8 | # -t edxapp:ironwood.2 \ 9 | # . 10 | ARG DOCKER_UID=1000 11 | ARG DOCKER_GID=1000 12 | ARG EDX_RELEASE_REF=ironwood.2 13 | ARG EDXAPP_STATIC_ROOT=/edx/app/edxapp/staticfiles 14 | ARG NGINX_IMAGE_NAME=nginxinc/nginx-unprivileged 15 | ARG NGINX_IMAGE_TAG=1.20 16 | 17 | # === BASE === 18 | FROM ubuntu:16.04 as base 19 | 20 | # Configure locales & timezone 21 | RUN apt-get update && \ 22 | apt-get install -y \ 23 | gettext \ 24 | locales \ 25 | tzdata && \ 26 | rm -rf /var/lib/apt/lists/* 27 | RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \ 28 | locale-gen 29 | ENV LANG en_US.UTF-8 30 | ENV LANGUAGE en_US:en 31 | ENV LC_ALL en_US.UTF-8 32 | 33 | # === DOWNLOAD === 34 | FROM base as downloads 35 | 36 | WORKDIR /downloads 37 | 38 | # Install curl 39 | RUN apt-get update && \ 40 | apt-get install -y curl 41 | 42 | # Download pip installer for python 2.7 43 | RUN curl -sLo get-pip.py https://bootstrap.pypa.io/pip/2.7/get-pip.py 44 | 45 | # Download edxapp release 46 | # Get default EDX_RELEASE_REF value (defined on top) 47 | ARG EDX_RELEASE_REF 48 | RUN curl -sLo edxapp.tgz https://github.com/openedx/edx-platform/archive/open-release/$EDX_RELEASE_REF.tar.gz && \ 49 | tar xzf edxapp.tgz 50 | 51 | 52 | # === EDXAPP === 53 | FROM base as edxapp 54 | 55 | # Install apt https support (required to use node sources repository) 56 | RUN apt-get update && \ 57 | apt-get upgrade -y && \ 58 | apt-get install -y \ 59 | apt-transport-https 60 | 61 | # Add a recent release of nodejs to apt sources (ubuntu package for precise is 62 | # broken) 63 | RUN echo "deb https://deb.nodesource.com/node_10.x trusty main" \ 64 | > /etc/apt/sources.list.d/nodesource.list && \ 65 | curl -s 'https://deb.nodesource.com/gpgkey/nodesource.gpg.key' | apt-key add - 66 | 67 | # Install base system dependencies 68 | RUN apt-get update && \ 69 | apt-get install -y \ 70 | nodejs \ 71 | python && \ 72 | rm -rf /var/lib/apt/lists/* 73 | 74 | WORKDIR /edx/app/edxapp/edx-platform 75 | 76 | # Get default EDX_RELEASE_REF value (defined on top) 77 | ARG EDX_RELEASE_REF 78 | COPY --from=downloads "/downloads/edx-platform-open-release-${EDX_RELEASE_REF}" . 79 | 80 | COPY ./requirements.txt /edx/app/edxapp/edx-platform/requirements/edx/extend.txt 81 | 82 | # We copy default configuration files to "/config" and we point to them via 83 | # symlinks. That allows to easily override default configurations by mounting a 84 | # docker volume. 85 | COPY ./config /config 86 | RUN ln -sf /config/lms /edx/app/edxapp/edx-platform/lms/envs/fun && \ 87 | ln -sf /config/cms /edx/app/edxapp/edx-platform/cms/envs/fun && \ 88 | ln -sf /config/lms/root_urls.py /edx/app/edxapp/edx-platform/lms/ && \ 89 | ln -sf /config/cms/root_urls.py /edx/app/edxapp/edx-platform/cms/ 90 | 91 | # Add node_modules/.bin to the PATH so that paver-related commands can execute 92 | # node scripts 93 | ENV PATH="/edx/app/edxapp/edx-platform/node_modules/.bin:${PATH}" 94 | 95 | # === BUILDER === 96 | FROM edxapp as builder 97 | 98 | WORKDIR /builder 99 | 100 | # Install builder system dependencies 101 | RUN apt-get update && \ 102 | apt-get upgrade -y && \ 103 | apt-get install -y \ 104 | build-essential \ 105 | gettext \ 106 | git \ 107 | graphviz-dev \ 108 | libgeos-dev \ 109 | libmysqlclient-dev \ 110 | libxml2-dev \ 111 | libxmlsec1-dev \ 112 | python-dev \ 113 | rdfind && \ 114 | rm -rf /var/lib/apt/lists/* 115 | 116 | # Install the latest pip release 117 | COPY --from=downloads /downloads/get-pip.py ./get-pip.py 118 | RUN python get-pip.py 119 | 120 | WORKDIR /edx/app/edxapp/edx-platform 121 | 122 | # Patches 123 | COPY patches/* /tmp/ 124 | 125 | # Patches pre-install 126 | # Patch requirements to install py2neo==3.1.2 from github as this version has been removed from pypi.org 127 | RUN patch -p1 < /tmp/edx-platform_ironwood.2-oee_requirements-py2neo.patch 128 | 129 | # Patch to fix installation of Python modules 130 | RUN patch -p1 < /tmp/edx-platform_ironwood.2-oee_moto.patch 131 | 132 | # Install python dependencies 133 | RUN pip install --src /usr/local/src -r requirements/edx/base.txt && \ 134 | pip install -r requirements/edx/extend.txt 135 | 136 | # Patches post-install 137 | # Patch the CMS to activate our customizable LTI Xblock 138 | RUN patch -p1 < /tmp/edx-platform_ironwood.2-oee.patch 139 | # Patch ORA2 to hide empty file links 140 | RUN patch -d /usr/local/lib/python2.7/dist-packages/ -p1 < /tmp/edx-ora2_ironwood.2-oee.patch 141 | 142 | # Install Javascript requirements 143 | RUN npm install 144 | 145 | # Update assets skipping collectstatic (it should be done during deployment) 146 | RUN NO_PREREQ_INSTALL=1 \ 147 | paver update_assets --settings=fun.docker_build_production --skip-collect 148 | 149 | # === STATIC LINKS COLLECTOR === 150 | FROM builder as links_collector 151 | 152 | ARG EDXAPP_STATIC_ROOT 153 | 154 | RUN python manage.py lms collectstatic --link --noinput --settings=fun.docker_build_production && \ 155 | python manage.py cms collectstatic --link --noinput --settings=fun.docker_build_production 156 | 157 | # Replace duplicated file by a symlink to decrease the overall size of the 158 | # final image 159 | RUN rdfind -makesymlinks true -followsymlinks true ${EDXAPP_STATIC_ROOT} 160 | 161 | # === STATIC FILES COLLECTOR === 162 | FROM builder as files_collector 163 | 164 | ARG EDXAPP_STATIC_ROOT 165 | 166 | RUN python manage.py lms collectstatic --noinput --settings=fun.docker_build_production && \ 167 | python manage.py cms collectstatic --noinput --settings=fun.docker_build_production 168 | 169 | # Replace duplicated file by a symlink to decrease the overall size of the 170 | # final image 171 | RUN rdfind -makesymlinks true ${EDXAPP_STATIC_ROOT} 172 | 173 | # === DEVELOPMENT === 174 | FROM builder as development 175 | 176 | ARG DOCKER_UID 177 | ARG DOCKER_GID 178 | ARG EDX_RELEASE_REF 179 | 180 | # Install system dependencies 181 | RUN apt-get update && \ 182 | apt-get upgrade -y && \ 183 | apt-get install -y \ 184 | libsqlite3-dev \ 185 | mongodb && \ 186 | rm -rf /var/lib/apt/lists/* 187 | 188 | RUN groupadd --gid ${DOCKER_GID} edx || \ 189 | echo "Group with ID ${DOCKER_GID} already exists." && \ 190 | useradd \ 191 | --create-home \ 192 | --home-dir /home/edx \ 193 | --uid ${DOCKER_UID} \ 194 | --gid ${DOCKER_GID} \ 195 | edx || \ 196 | echo "Skip user creation (user with ID ${DOCKER_UID} already exists?)" 197 | 198 | # To prevent permission issues related to the non-privileged user running in 199 | # development, we will install development dependencies in a python virtual 200 | # environment belonging to that user 201 | RUN pip install virtualenv 202 | 203 | # Create the virtualenv directory where we will install python development 204 | # dependencies 205 | RUN mkdir -p /edx/app/edxapp/venv && \ 206 | chown -R ${DOCKER_UID}:${DOCKER_GID} /edx/app/edxapp/venv 207 | 208 | # Change edxapp directory owner to allow the development image docker user to 209 | # perform installations from edxapp sources (yeah, I know...) 210 | RUN chown -R ${DOCKER_UID}:${DOCKER_GID} /edx/app/edxapp 211 | 212 | # Copy the entrypoint that will activate the virtualenv 213 | COPY ./entrypoint.sh /usr/local/bin/entrypoint.sh 214 | 215 | # Switch to an un-privileged user matching the host user to prevent permission 216 | # issues with volumes (host folders) 217 | USER ${DOCKER_UID}:${DOCKER_GID} 218 | 219 | # Create the virtualenv with a non-privileged user 220 | RUN virtualenv -p python2.7 --system-site-packages /edx/app/edxapp/venv 221 | 222 | # Install development dependencies in a virtualenv 223 | RUN bash -c "source /edx/app/edxapp/venv/bin/activate && \ 224 | pip install --no-cache-dir -r requirements/edx/testing.txt && \ 225 | pip install --no-cache-dir -r requirements/edx/development.txt" 226 | 227 | ENTRYPOINT [ "/usr/local/bin/entrypoint.sh" ] 228 | 229 | 230 | # === PRODUCTION === 231 | FROM edxapp as production 232 | 233 | ARG EDXAPP_STATIC_ROOT 234 | 235 | # Install runner system dependencies 236 | RUN apt-get update && \ 237 | apt-get upgrade -y && \ 238 | apt-get install -y \ 239 | libgeos-dev \ 240 | libmysqlclient20 \ 241 | libxml2 \ 242 | libxmlsec1-dev \ 243 | lynx \ 244 | tzdata && \ 245 | rm -rf /var/lib/apt/lists/* 246 | 247 | # Copy installed dependencies 248 | COPY --from=builder /usr/local /usr/local 249 | 250 | # Copy modified sources (sic!) 251 | COPY --from=builder /edx/app/edxapp/edx-platform /edx/app/edxapp/edx-platform 252 | 253 | # Copy static files 254 | COPY --from=links_collector ${EDXAPP_STATIC_ROOT} ${EDXAPP_STATIC_ROOT} 255 | 256 | # Now that dependencies are installed and configuration has been set, the above 257 | # statements will run with a un-privileged user. 258 | USER 10000 259 | 260 | # To start the CMS, inject the SERVICE_VARIANT=cms environment variable 261 | # (defaults to "lms") 262 | ENV SERVICE_VARIANT=lms 263 | 264 | # Gunicorn configuration 265 | # 266 | # We want to be able to easily increase gunicorn if needed 267 | ENV GUNICORN_TIMEOUT 300 268 | 269 | # In docker we must increase the number of workers and threads created 270 | # by gunicorn. 271 | # This blogpost explains why and how to do that https://pythonspeed.com/articles/gunicorn-in-docker/ 272 | ENV GUNICORN_WORKERS 3 273 | ENV GUNICORN_THREADS 6 274 | 275 | # Use Gunicorn in production as web server 276 | CMD DJANGO_SETTINGS_MODULE=${SERVICE_VARIANT}.envs.fun.docker_run \ 277 | gunicorn \ 278 | --name=${SERVICE_VARIANT} \ 279 | --bind=0.0.0.0:8000 \ 280 | --max-requests=1000 \ 281 | --timeout=${GUNICORN_TIMEOUT} \ 282 | --workers=${GUNICORN_WORKERS} \ 283 | --threads=${GUNICORN_THREADS} \ 284 | ${SERVICE_VARIANT}.wsgi:application 285 | 286 | # === NGINX === 287 | FROM ${NGINX_IMAGE_NAME}:${NGINX_IMAGE_TAG} as nginx 288 | 289 | ARG EDXAPP_STATIC_ROOT 290 | 291 | # Switch back to the root user to include static files in the container 292 | USER root:root 293 | 294 | RUN mkdir -p ${EDXAPP_STATIC_ROOT} 295 | 296 | COPY --from=files_collector ${EDXAPP_STATIC_ROOT} ${EDXAPP_STATIC_ROOT} 297 | 298 | # Now that everything is included, run the container with a un-privileged user 299 | USER 10000 300 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/activate: -------------------------------------------------------------------------------- 1 | export EDX_RELEASE="ironwood.2" 2 | export FLAVOR="oee" 3 | export EDX_RELEASE_REF="ironwood.2" 4 | export EDX_DEMO_RELEASE_REF="open-release/ironwood.2" 5 | unset EDX_ARCHIVE_URL 6 | unset EDX_DEMO_ARCHIVE_URL 7 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/cms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfun/openedx-docker/4d7664c97b59f201230f0b2b48f7cfc255496bf5/releases/ironwood/2/oee/config/cms/__init__.py -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/cms/docker_build_development.py: -------------------------------------------------------------------------------- 1 | # This is a minimal settings file allowing us to run "update_assets" 2 | # in the Dockerfile for the development image of the edxapp CMS 3 | 4 | from .docker_build_production import * 5 | 6 | DEBUG = True 7 | REQUIRE_DEBUG = True 8 | 9 | WEBPACK_CONFIG_PATH = "webpack.dev.config.js" 10 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/cms/docker_build_production.py: -------------------------------------------------------------------------------- 1 | # This is a minimal settings file allowing us to run "update_assets" 2 | # in the Dockerfile for the production image of the edxapp CMS 3 | 4 | from openedx.core.lib.derived import derive_settings 5 | 6 | from lms.envs.fun.utils import Configuration 7 | from path import Path as path 8 | 9 | from .docker_run_production import * 10 | 11 | # Load custom configuration parameters 12 | config = Configuration() 13 | 14 | DATABASES = {"default": {}} 15 | 16 | XQUEUE_INTERFACE = {"url": None, "django_auth": None} 17 | 18 | # We need to override STATIC_ROOT because for CMS, edX appends the value of 19 | # "EDX_PLATFORM_REVISION" to it by default and we don't want to use this. 20 | # We should use Django's ManifestStaticFilesStorage for this purpose. 21 | STATIC_URL = "/static/studio/" 22 | STATIC_ROOT = path("/edx/app/edxapp/staticfiles/studio") 23 | 24 | # Allow setting a custom theme 25 | DEFAULT_SITE_THEME = config("DEFAULT_SITE_THEME", default=None) 26 | 27 | ########################## Derive Any Derived Settings ####################### 28 | 29 | derive_settings(__name__) 30 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/cms/docker_run.py: -------------------------------------------------------------------------------- 1 | # This file is meant to import the environment related settings file 2 | 3 | from docker_run_production import * 4 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/cms/docker_run_ci.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `CI` environment for the CMS, starting from 2 | # the settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | 6 | 7 | DEBUG = True 8 | 9 | EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" 10 | 11 | PIPELINE_ENABLED = False 12 | STATICFILES_STORAGE = "openedx.core.storage.DevelopmentStorage" 13 | 14 | ALLOWED_HOSTS = ["*"] 15 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/cms/docker_run_development.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `development` environment for the CMS, starting from 2 | # the settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from lms.envs.fun.utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | if "sentry" in LOGGING.get("handlers"): 11 | LOGGING["handlers"]["sentry"]["environment"] = "development" 12 | 13 | DEBUG = True 14 | REQUIRE_DEBUG = True 15 | 16 | EMAIL_BACKEND = config( 17 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 18 | ) 19 | 20 | PIPELINE_ENABLED = False 21 | STATICFILES_STORAGE = "openedx.core.storage.DevelopmentStorage" 22 | 23 | ALLOWED_HOSTS = ["*"] 24 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/cms/docker_run_feature.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `feature` environment for the CMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from lms.envs.fun.utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "feature" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/cms/docker_run_preprod.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `preprod` environment for the CMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from lms.envs.fun.utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "preprod" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/cms/docker_run_staging.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `staging` environment for the CMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from lms.envs.fun.utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "staging" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/cms/root_urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | OEE urls 3 | We define this file as root url (ROOT_URLCONF) to override and extend 4 | edx-platform's CMS routes 5 | """ 6 | from __future__ import absolute_import, unicode_literals 7 | 8 | from django.conf.urls import include, url 9 | 10 | from cms.urls import urlpatterns # pylint: disable=import-error 11 | 12 | 13 | urlpatterns += [ 14 | url(r'^openassessment/fileupload/', include('openassessment.fileupload.urls')), 15 | ] 16 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/lms/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openfun/openedx-docker/4d7664c97b59f201230f0b2b48f7cfc255496bf5/releases/ironwood/2/oee/config/lms/__init__.py -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/lms/backends.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from ratelimitbackend.backends import RateLimitModelBackend 4 | 5 | 6 | class ProxyRateLimitModelBackend(RateLimitModelBackend): 7 | """A rate limiting Backend that works behind a proxy.""" 8 | 9 | def get_ip(self, request): 10 | """Return the end user address string as set by proxies.""" 11 | return request.META['HTTP_X_FORWARDED_FOR'] 12 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/lms/docker_build_development.py: -------------------------------------------------------------------------------- 1 | # This is a minimal settings file allowing us to run "update_assets" 2 | # in the Dockerfile for the development image of the edxapp LMS 3 | 4 | from .docker_build_production import * 5 | 6 | DEBUG = True 7 | REQUIRE_DEBUG = True 8 | 9 | WEBPACK_CONFIG_PATH = "webpack.dev.config.js" 10 | 11 | CACHES = { 12 | "default": { 13 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 14 | "KEY_PREFIX": "default", 15 | }, 16 | "general": { 17 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 18 | "KEY_PREFIX": "general", 19 | }, 20 | "celery": { 21 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 22 | "KEY_PREFIX": "celery", 23 | }, 24 | "mongo_metadata_inheritance": { 25 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 26 | "KEY_PREFIX": "mongo_metadata_inheritance", 27 | }, 28 | "openassessment_submissions": { 29 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 30 | "KEY_PREFIX": "openassessment_submissions", 31 | }, 32 | "loc_cache": { 33 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 34 | "KEY_PREFIX": "loc_cache", 35 | }, 36 | # Cache backend used by Django 1.8 storage backend while processing static files 37 | "staticfiles": { 38 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 39 | "KEY_PREFIX": "staticfiles", 40 | }, 41 | } 42 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/lms/docker_build_production.py: -------------------------------------------------------------------------------- 1 | # This is a minimal settings file allowing us to run "update_assets" 2 | # in the Dockerfile for the production image of the edxapp LMS 3 | 4 | from openedx.core.lib.derived import derive_settings 5 | from path import Path as path 6 | 7 | from .docker_run_production import * 8 | from .utils import Configuration 9 | 10 | # Load custom configuration parameters 11 | config = Configuration() 12 | 13 | DATABASES = {"default": {}} 14 | 15 | XQUEUE_INTERFACE = {"url": None, "django_auth": None} 16 | 17 | STATIC_ROOT = path("/edx/app/edxapp/staticfiles") 18 | 19 | # Allow setting a custom theme 20 | DEFAULT_SITE_THEME = config("DEFAULT_SITE_THEME", default=None) 21 | 22 | CACHES = { 23 | "default": { 24 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 25 | "KEY_PREFIX": "default", 26 | }, 27 | "general": { 28 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 29 | "KEY_PREFIX": "general", 30 | }, 31 | "celery": { 32 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 33 | "KEY_PREFIX": "celery", 34 | }, 35 | "mongo_metadata_inheritance": { 36 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 37 | "KEY_PREFIX": "mongo_metadata_inheritance", 38 | }, 39 | "openassessment_submissions": { 40 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 41 | "KEY_PREFIX": "openassessment_submissions", 42 | }, 43 | "loc_cache": { 44 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 45 | "KEY_PREFIX": "loc_cache", 46 | }, 47 | # Cache backend used by Django 1.8 storage backend while processing static files 48 | "staticfiles": { 49 | "BACKEND": "django.core.cache.backends.locmem.LocMemCache", 50 | "KEY_PREFIX": "staticfiles", 51 | }, 52 | } 53 | 54 | 55 | ########################## Derive Any Derived Settings ####################### 56 | 57 | derive_settings(__name__) 58 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/lms/docker_run.py: -------------------------------------------------------------------------------- 1 | # This file is meant to import the environment related settings file 2 | 3 | from docker_run_production import * 4 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/lms/docker_run_ci.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `CI` environment for the LMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | 6 | 7 | DEBUG = True 8 | 9 | EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" 10 | 11 | PIPELINE_ENABLED = False 12 | STATICFILES_STORAGE = "openedx.core.storage.DevelopmentStorage" 13 | 14 | ALLOWED_HOSTS = ["*"] 15 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/lms/docker_run_development.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `development` environment for the LMS starting from the 2 | # settings of the `production` environment 3 | 4 | import json 5 | 6 | from docker_run_production import * 7 | from .utils import Configuration 8 | 9 | # Load custom configuration parameters from yaml files 10 | config = Configuration(os.path.dirname(__file__)) 11 | 12 | if "sentry" in LOGGING.get("handlers"): 13 | LOGGING["handlers"]["sentry"]["environment"] = "development" 14 | 15 | DEBUG = True 16 | REQUIRE_DEBUG = True 17 | 18 | EMAIL_BACKEND = config( 19 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 20 | ) 21 | 22 | PIPELINE_ENABLED = False 23 | STATICFILES_STORAGE = "openedx.core.storage.DevelopmentStorage" 24 | 25 | ALLOWED_HOSTS = ["*"] 26 | 27 | WEBPACK_CONFIG_PATH = "webpack.dev.config.js" 28 | 29 | AUTHENTICATION_BACKENDS = config( 30 | "AUTHENTICATION_BACKENDS", 31 | default=["django.contrib.auth.backends.ModelBackend"], 32 | formatter=json.loads 33 | ) 34 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/lms/docker_run_feature.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `feature` environment for the LMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from .utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "feature" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/lms/docker_run_preprod.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `preprod` environment for the LMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from .utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "preprod" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/lms/docker_run_staging.py: -------------------------------------------------------------------------------- 1 | # This file includes overrides to build the `staging` environment for the LMS starting from the 2 | # settings of the `production` environment 3 | 4 | from docker_run_production import * 5 | from .utils import Configuration 6 | 7 | # Load custom configuration parameters from yaml files 8 | config = Configuration(os.path.dirname(__file__)) 9 | 10 | LOGGING["handlers"]["sentry"]["environment"] = "staging" 11 | 12 | EMAIL_BACKEND = config( 13 | "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" 14 | ) 15 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/lms/root_urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | OEE urls 3 | We define this file as root url (ROOT_URLCONF) to override and extend 4 | edx-platform's LMS routes 5 | """ 6 | from __future__ import absolute_import, unicode_literals 7 | 8 | from django.conf.urls import include, url 9 | 10 | from lms.urls import urlpatterns # pylint: disable=import-error 11 | 12 | 13 | urlpatterns += [ 14 | # Fonzie API urls 15 | url(r"^api/", include("fonzie.urls", namespace="fonzie")), 16 | ] 17 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/lms/storage.py: -------------------------------------------------------------------------------- 1 | """Django static file storage backend for OpenEdX.""" 2 | from django.conf import settings 3 | 4 | from openedx.core.storage import ProductionStorage 5 | 6 | 7 | class CDNProductionStorage(ProductionStorage): 8 | """Open edX production static files storage backend that can be placed behing a CDN.""" 9 | 10 | def url(self, name, force=False): 11 | """Prepend static files path by the CDN base url when configured in settings.""" 12 | url = super(CDNProductionStorage, self).url(name, force=force) 13 | 14 | cdn_base_url = getattr(settings, "CDN_BASE_URL", None) 15 | if cdn_base_url: 16 | url = "{:s}{:s}".format(cdn_base_url, url) 17 | 18 | return url 19 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/config/lms/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import yaml 4 | import os 5 | 6 | from django.core.exceptions import ImproperlyConfigured 7 | 8 | 9 | class Configuration(dict): 10 | """ 11 | Try getting a setting from the settings.yml or secrets.yml files placed in 12 | the directory passed when initializing the configuration instance. 13 | """ 14 | 15 | def __init__(self, dir=None, *args, **kwargs): 16 | """ 17 | Initialize with the path to the directory in which the configuration is 18 | to be found. 19 | """ 20 | super(Configuration, self).__init__(*args, **kwargs) 21 | 22 | if dir is None: 23 | self.settings = {} 24 | 25 | else: 26 | # Load the content of a `settings.yml` file placed in the current 27 | # directory if any. This file is where customizable settings are stored 28 | # for a given environment. 29 | try: 30 | with open(os.path.join(dir, "settings.yml")) as f: 31 | settings = yaml.load(f.read()) or {} 32 | except IOError: 33 | settings = {} 34 | 35 | # Load the content of a `secrets.yml` file placed in the current 36 | # directory if any. This file is where sensitive credentials are stored 37 | # for a given environment. 38 | try: 39 | with open(os.path.join(dir, "secrets.yml")) as f: 40 | credentials = yaml.load(f.read()) or {} 41 | except IOError: 42 | credentials = {} 43 | 44 | settings.update(credentials) 45 | self.settings = settings 46 | 47 | def __call__(self, var_name, formatter=str, *args, **kwargs): 48 | """ 49 | The config returns in order of priority: 50 | 51 | - the value set in the secrets.yml file, 52 | - the value set in the settings.yml file, 53 | - the value set as environment variable 54 | - the value passed as default. 55 | 56 | If the value is passed as a string, a type is forced via the function passed in 57 | the "formatter" kwarg. 58 | 59 | Raise an "ImproperlyConfigured" error if the name is not found, except 60 | if the `default` key is given in kwargs (using kwargs allows to pass a 61 | default to None, which is different from not passing any default): 62 | 63 | $ config = Configuration('path/to/config/directory') 64 | $ config('foo') # raise ImproperlyConfigured error if `foo` is not defined 65 | $ config('foo', default='bar') # return 'bar' if `foo` is not defined 66 | $ config('foo', default=None) # return `None` if `foo` is not defined 67 | """ 68 | try: 69 | value = self.settings[var_name] 70 | except KeyError: 71 | try: 72 | value = formatter(os.environ[var_name]) 73 | except KeyError: 74 | if "default" in kwargs: 75 | value = kwargs["default"] 76 | else: 77 | raise ImproperlyConfigured( 78 | 'Please set the "{:s}" variable in a settings.yml file, a secrets.yml ' 79 | "file or an environment variable.".format(var_name) 80 | ) 81 | # If a formatter is specified, force the value but only if it was passed as a string 82 | if isinstance(value, basestring): 83 | value = formatter(value.encode("utf-8")) 84 | 85 | return value 86 | 87 | def get(self, name, *args, **kwargs): 88 | """ 89 | edX is loading the content of 2 json files to settings.ENV_TOKEN and settings.AUTH_TOKEN 90 | They have started calling these attributes anywhere in the code base, so we must make 91 | sure that the following call works (and the same for AUTH_TOKEN): 92 | 93 | settings.ENV_TOKEN.get('ANY_SETTING_NAME') 94 | 95 | That's what this method will do after we add this to our settings: 96 | ``` 97 | config = Configuration('path/to/my/settings/directory.yml') 98 | ENV_TOKEN = config 99 | AUTH_TOKEN = config 100 | ``` 101 | """ 102 | try: 103 | default = args[0] 104 | except IndexError: 105 | # As a first approach, all defaults that are not provided by Open edX are set to None. 106 | # If this creates a problem, we can either: 107 | # - make sure we provide a value for this setting in our yaml files, 108 | # - make a PR to Open edX to provide a better default for this setting. 109 | default = None 110 | return self(name, default=default) 111 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Development entrypoint 4 | # 5 | 6 | # Activate user's virtualenv 7 | source /edx/app/edxapp/venv/bin/activate 8 | 9 | # Override default root_urls 10 | ln -sf /config/lms/root_urls.py /edx/app/edxapp/edx-platform/lms/ 11 | ln -sf /config/cms/root_urls.py /edx/app/edxapp/edx-platform/cms/ 12 | 13 | exec "$@" 14 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/patches/edx-ora2_ironwood.2-oee.patch: -------------------------------------------------------------------------------- 1 | diff --git a/openassessment/templates/openassessmentblock/oa_uploaded_file.html b/openassessment/templates/openassessmentblock/oa_uploaded_file.html 2 | index 214fa75b..b07f3b0c 100644 3 | --- a/openassessment/templates/openassessmentblock/oa_uploaded_file.html 4 | +++ b/openassessment/templates/openassessmentblock/oa_uploaded_file.html 5 | @@ -13,6 +13,7 @@ 6 |
7 |
8 | {% for file_url, file_description in file_urls %} 9 | + {% if file_description %} 10 |
11 | {% if file_upload_type == "image" %} 12 | {% if file_description %} 13 | @@ -30,6 +31,7 @@ 14 | 15 | {% endif %} 16 |
17 | + {% endif %} 18 | {% endfor %} 19 |
20 | {% if show_warning %} 21 | 22 | 23 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/patches/edx-platform_ironwood.2-oee.patch: -------------------------------------------------------------------------------- 1 | diff --git a/cms/djangoapps/contentstore/views/component.py b/cms/djangoapps/contentstore/views/component.py 2 | index f65f281151..af47c239de 100644 3 | --- a/cms/djangoapps/contentstore/views/component.py 4 | +++ b/cms/djangoapps/contentstore/views/component.py 5 | @@ -27,6 +27,15 @@ from xblock_django.models import XBlockStudioConfigurationFlag 6 | from xmodule.modulestore.django import modulestore 7 | from xmodule.modulestore.exceptions import ItemNotFoundError 8 | 9 | +# Try to import the configurable LTI consumer xblock that overrides Open edX 10 | +# default LTI xblock. It has no effect if this alternative xblock is not 11 | +# installed. 12 | +try: 13 | + from configurable_lti_consumer import add_dynamic_components 14 | +except ImportError: 15 | + add_dynamic_components = None 16 | + 17 | + 18 | __all__ = [ 19 | 'container_handler', 20 | 'component_handler' 21 | @@ -359,6 +368,17 @@ def get_component_templates(courselike, library=False): 22 | "support_legend": create_support_legend_dict() 23 | } 24 | advanced_component_types = _advanced_component_types(allow_unsupported) 25 | + 26 | + # Read LTI_XBLOCK_CONFIGURATIONS setting to add links to the `Advanced` component button 27 | + if add_dynamic_components: 28 | + add_dynamic_components( 29 | + getattr(settings, "LTI_XBLOCK_CONFIGURATIONS"), 30 | + advanced_component_templates, 31 | + categories, 32 | + create_template_dict, 33 | + course_advanced_keys 34 | + ) 35 | + 36 | # Set component types according to course policy file 37 | if isinstance(course_advanced_keys, list): 38 | for category in course_advanced_keys: 39 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/patches/edx-platform_ironwood.2-oee_moto.patch: -------------------------------------------------------------------------------- 1 | diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt 2 | --- a/requirements/edx/testing.txt 3 | +++ b/requirements/edx/testing.txt 4 | @@ -205,7 +205,7 @@ 5 | mock==1.0.1 6 | mongoengine==0.10.0 7 | more-itertools==5.0.0 # via pytest 8 | -moto==0.3.1 9 | +# moto==0.3.1 version not available anymore but we don't need to run the tests anyway 10 | mysql-python==1.2.5 11 | networkx==1.7 12 | newrelic==4.10.0.112 13 | 14 | diff --git a/requirements/edx/testing.in b/requirements/edx/testing.in 15 | --- a/requirements/edx/testing.in 16 | +++ b/requirements/edx/testing.in 17 | @@ -28,7 +28,7 @@ 18 | freezegun # Allows tests to mock the output of assorted datetime module functions 19 | httpretty # Library for mocking HTTP requests, used in many tests 20 | isort # For checking and fixing the order of imports 21 | -moto==0.3.1 # Lets tests mock AWS access via the boto library 22 | +# moto==0.3.1 # Version not available anymore but we don't need to run the tests anyway 23 | pa11ycrawler # Python crawler (using Scrapy) that uses Pa11y to check accessibility of pages as it crawls 24 | pycodestyle # Checker for compliance with the Python style guide (PEP 8) 25 | polib # Library for manipulating gettext translation files, used to test paver i18n commands 26 | 27 | diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt 28 | --- a/requirements/edx/development.txt 29 | +++ b/requirements/edx/development.txt 30 | @@ -213,7 +213,7 @@ 31 | modernize==0.6.1 32 | mongoengine==0.10.0 33 | more-itertools==5.0.0 34 | -moto==0.3.1 35 | +# moto==0.3.1 version not available anymore but we don't need to run the tests anyway 36 | mysql-python==1.2.5 37 | networkx==1.7 38 | newrelic==4.10.0.112 39 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/patches/edx-platform_ironwood.2-oee_requirements-py2neo.patch: -------------------------------------------------------------------------------- 1 | diff --git a/requirements/edx/base.txt b/requirements/edx/base.txt 2 | index 8a5f114..338d8a5 100644 3 | --- a/requirements/edx/base.txt 4 | +++ b/requirements/edx/base.txt 5 | @@ -185,7 +185,8 @@ piexif==1.0.2 6 | pillow==5.4.1 7 | polib==1.1.0 # via edx-i18n-tools 8 | psutil==1.2.1 9 | -py2neo==3.1.2 10 | +# Old versions of py2neo have been moved into a package called py2neo-history 11 | +py2neo-history==3.1.2 12 | pycontracts==1.7.1 13 | pycountry==1.20 14 | pycparser==2.19 15 | 16 | diff --git a/requirements/edx/development.txt b/requirements/edx/development.txt 17 | index 80ac873..cbb87b4 100644 18 | --- a/requirements/edx/development.txt 19 | +++ b/requirements/edx/development.txt 20 | @@ -238,7 +238,8 @@ pip-tools==3.2.0 21 | pluggy==0.8.1 22 | polib==1.1.0 23 | psutil==1.2.1 24 | -py2neo==3.1.2 25 | +# Old versions of py2neo have been moved into a package called py2neo-history 26 | +py2neo-history==3.1.2 27 | py==1.7.0 28 | pyasn1-modules==0.2.3 29 | pyasn1==0.4.5 30 | 31 | diff --git a/requirements/edx/testing.txt b/requirements/edx/testing.txt 32 | index 9d55925..0123ad4 100644 33 | --- a/requirements/edx/testing.txt 34 | +++ b/requirements/edx/testing.txt 35 | @@ -228,7 +228,8 @@ pillow==5.4.1 36 | pluggy==0.8.1 # via pytest, tox 37 | polib==1.1.0 38 | psutil==1.2.1 39 | -py2neo==3.1.2 40 | +# Old versions of py2neo have been moved into a package called py2neo-history 41 | +py2neo-history==3.1.2 42 | py==1.7.0 # via pytest, tox 43 | pyasn1-modules==0.2.3 # via service-identity 44 | pyasn1==0.4.5 # via pyasn1-modules, service-identity 45 | -------------------------------------------------------------------------------- /releases/ironwood/2/oee/requirements.txt: -------------------------------------------------------------------------------- 1 | # Open edX extended: dependencies 2 | 3 | # ==== core ==== 4 | fonzie==0.2.1 5 | 6 | # ==== xblocks ==== 7 | configurable_lti_consumer-xblock==1.4.1 8 | 9 | # pin ora2 to a version that works with the filesystem 10 | git+https://github.com/edx/edx-ora2.git@2.2.7#egg=ora2==2.2.7 11 | 12 | # ==== third-party apps ==== 13 | celery-redis-sentinel==0.3.0 14 | # django-django-redis-sentinel-redux is not compatible with 15 | # django-redis > 4.5.0 16 | django-redis==4.5.0 17 | django-redis-sentinel-redux==0.2.0 18 | django-redis-sessions==0.6.1 19 | raven==6.9.0 20 | redis==2.10.6 21 | gunicorn==19.9.0 22 | --------------------------------------------------------------------------------