Merge branch 'master' into sameeramin/ENT-10688

This commit is contained in:
Muhammad Sameer Amin
2025-07-24 20:04:18 +05:00
committed by GitHub
38 changed files with 244 additions and 1758 deletions

View File

@@ -32,13 +32,19 @@ from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.shortcuts import redirect
from django.utils.translation import gettext as _
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.decorators.http import require_http_methods
from rest_framework.views import APIView
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import status
from common.djangoapps.course_modes.models import CourseMode
from eventtracking import tracker
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import AssetKey, CourseKey
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin
from common.djangoapps.edxmako.shortcuts import render_to_response
from common.djangoapps.student.auth import has_studio_write_access
from common.djangoapps.student.roles import GlobalStaff
@@ -47,6 +53,10 @@ from common.djangoapps.util.json_request import JsonResponse
from xmodule.modulestore import EdxJSONEncoder # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order
from cms.djangoapps.contentstore.views.serializers import CertificateActivationSerializer
from cms.djangoapps.contentstore.views.permissions import HasStudioWriteAccess
from ..exceptions import AssetNotFoundException
from ..toggles import use_new_certificates_page
from ..utils import (
@@ -360,39 +370,56 @@ class Certificate:
return self._certificate_data
@login_required
@require_http_methods(("POST",))
@ensure_csrf_cookie
def certificate_activation_handler(request, course_key_string):
class ModulestoreMixin:
"""
A handler for Certificate Activation/Deactivation
POST
json: is_active. update the activation state of certificate
Mixin to provide a get_modulestore() method for views.
Makes it easier to override or patch in tests.
"""
course_key = CourseKey.from_string(course_key_string)
store = modulestore()
try:
course = _get_course_and_check_access(course_key, request.user)
except PermissionDenied:
msg = _('PermissionDenied: Failed in authenticating {user}').format(user=request.user)
return JsonResponse({"error": msg}, status=403)
def get_modulestore(self):
return modulestore()
data = json.loads(request.body.decode('utf8'))
is_active = data.get('is_active', False)
certificates = CertificateManager.get_certificates(course)
# for certificate activation/deactivation, we are assuming one certificate in certificates collection.
for certificate in certificates:
certificate['is_active'] = is_active
break
class CertificateActivationAPIView(
DeveloperErrorViewMixin,
ModulestoreMixin,
APIView
):
"""
View for activating or deactivating course certificates.
This view allows instructors to toggle the activation state of course certificates.
"""
permission_classes = [IsAuthenticated, HasStudioWriteAccess]
serializer_class = CertificateActivationSerializer
store.update_item(course, request.user.id)
cert_event_type = 'activated' if is_active else 'deactivated'
CertificateManager.track_event(cert_event_type, {
'course_id': str(course.id),
})
return HttpResponse(status=200)
@method_decorator(ensure_csrf_cookie)
def post(self, request, course_key_string):
"""
A handler for Certificate Activation/Deactivation
POST
json: is_active. update the activation state of certificate
"""
course_key = CourseKey.from_string(course_key_string)
course = self.get_modulestore().get_course(course_key, depth=0)
serializer = self.serializer_class(data=request.data)
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
is_active = serializer.validated_data['is_active']
certificates = CertificateManager.get_certificates(course)
# for certificate activation/deactivation, we are assuming one certificate in certificates collection.
for certificate in certificates:
certificate['is_active'] = is_active
break
self.get_modulestore().update_item(course, request.user.id)
cert_event_type = 'activated' if is_active else 'deactivated'
CertificateManager.track_event(cert_event_type, {
'course_id': str(course.id),
})
return Response(status=status.HTTP_200_OK)
@login_required

View File

@@ -0,0 +1,22 @@
"""
Custom permissions for the content store views.
"""
from rest_framework.permissions import BasePermission
from common.djangoapps.student.auth import has_studio_write_access
from openedx.core.lib.api.view_utils import validate_course_key
class HasStudioWriteAccess(BasePermission):
"""
Check if the user has write access to studio.
"""
def has_permission(self, request, view):
"""
Check if the user has write access to studio.
"""
course_key_string = view.kwargs.get("course_key_string")
course_key = validate_course_key(course_key_string)
return has_studio_write_access(request.user, course_key)

View File

@@ -0,0 +1,16 @@
"""
Serializers for the contentstore.views module.
This module contains DRF serializers for various features such as certificates, blocks, and others.
Add new serializers here as needed for API endpoints in this module.
"""
from rest_framework import serializers
class CertificateActivationSerializer(serializers.Serializer):
"""
Serializer for activating or deactivating course certificates.
"""
# This field indicates whether the certificate should be activated or deactivated.
is_active = serializers.BooleanField(required=False, default=False)

View File

@@ -1303,13 +1303,12 @@ def create_xblock_info( # lint-amnesty, pylint: disable=too-many-statements
# Update with gating info
xblock_info.update(_get_gating_info(course, xblock))
# Also add upstream info
xblock_info["upstream_info"] = UpstreamLink.try_get_for_block(xblock, log_error=False).to_json()
if is_xblock_unit:
# if xblock is a Unit we add the discussion_enabled option
xblock_info["discussion_enabled"] = xblock.discussion_enabled
# Also add upstream info
xblock_info["upstream_info"] = UpstreamLink.try_get_for_block(xblock, log_error=False).to_json()
if xblock.category == "sequential":
# Entrance exam subsection should be hidden. in_entrance_exam is
# inherited metadata, all children will have it.

View File

@@ -1,13 +0,0 @@
"""
Settings for running management commands for the Analytics Exporter.
The Analytics Exporter jobs run edxapp management commands using production
settings and configuration, however they currently DO NOT use edxapp production
environments (such as edxapp Amazon AMIs or Docker images) where theme files
get installed. As a result we must disable comprehensive theming or else
startup checks from the theming app will throw an error due to missing themes.
"""
from .production import * # pylint: disable=wildcard-import, unused-wildcard-import
ENABLE_COMPREHENSIVE_THEMING = False

View File

@@ -1,553 +0,0 @@
# This file is an experimental extraction of /edx/etc/studio.yml from
# a CMS devstack container.
#
# When devstack is configured to use the new `openedx/` images
# instead of the old `edxops/edxapp` image, it will use this file
# as input to cms/envs/production.py (and, in turn, cms/envs/devstack.py).
# If you are using devstack with the `edxops/edxapp` image, though,
# this file is NOT used.
#
# Q. Should I update this file when I update devstack.py?
# A. You don't *have* to, because settings in devstack.py
# override these settings. But, it doesn't harm to also make them
# here in order to quell confusion. The hope is that we'll
# adpot OEP-45 eventually, which recommends against having
# a devstack.py at all.
#
# This is part of the effort to move our dev tools off of Ansible and
# Paver, described here: https://github.com/openedx/devstack/pull/866
# TODO: If the effort described above is abandoned, then this file should
# probably be deleted.
ACTIVATION_EMAIL_SUPPORT_LINK: ''
AFFILIATE_COOKIE_NAME: dev_affiliate_id
ALTERNATE_WORKER_QUEUES: lms
ANALYTICS_DASHBOARD_NAME: Your Platform Name Here Insights
ANALYTICS_DASHBOARD_URL: http://localhost:18110/courses
AUTH_PASSWORD_VALIDATORS:
- NAME: django.contrib.auth.password_validation.UserAttributeSimilarityValidator
- NAME: common.djangoapps.util.password_policy_validators.MinimumLengthValidator
OPTIONS:
min_length: 2
- NAME: common.djangoapps.util.password_policy_validators.MaximumLengthValidator
OPTIONS:
max_length: 75
AUTHORING_API_URL: https://example.com
AWS_ACCESS_KEY_ID: null
AWS_QUERYSTRING_AUTH: false
AWS_S3_CUSTOM_DOMAIN: SET-ME-PLEASE (ex. bucket-name.s3.amazonaws.com)
AWS_SECRET_ACCESS_KEY: null
AWS_SES_REGION_ENDPOINT: email.us-east-1.amazonaws.com
AWS_SES_REGION_NAME: us-east-1
AWS_STORAGE_BUCKET_NAME: SET-ME-PLEASE (ex. bucket-name)
BASE_COOKIE_DOMAIN: localhost
BLOCK_STRUCTURES_SETTINGS:
COURSE_PUBLISH_TASK_DELAY: 30
TASK_DEFAULT_RETRY_DELAY: 30
TASK_MAX_RETRIES: 5
BRANCH_IO_KEY: ''
BUGS_EMAIL: bugs@example.com
BULK_EMAIL_DEFAULT_FROM_EMAIL: no-reply@example.com
BULK_EMAIL_EMAILS_PER_TASK: 500
BULK_EMAIL_LOG_SENT_EMAILS: false
CACHES:
celery:
BACKEND: django.core.cache.backends.memcached.PyMemcacheCache
OPTIONS:
no_delay: true
ignore_exc: true
use_pooling: true
connect_timeout: 0.5
KEY_FUNCTION: common.djangoapps.util.memcache.safe_key
KEY_PREFIX: celery
LOCATION:
- edx.devstack.memcached:11211
TIMEOUT: '7200'
configuration:
BACKEND: django.core.cache.backends.memcached.PyMemcacheCache
OPTIONS:
no_delay: true
ignore_exc: true
use_pooling: true
connect_timeout: 0.5
KEY_FUNCTION: common.djangoapps.util.memcache.safe_key
KEY_PREFIX: 78f87108afce
LOCATION:
- edx.devstack.memcached:11211
course_structure_cache:
BACKEND: django.core.cache.backends.memcached.PyMemcacheCache
OPTIONS:
no_delay: true
ignore_exc: true
use_pooling: true
connect_timeout: 0.5
KEY_FUNCTION: common.djangoapps.util.memcache.safe_key
KEY_PREFIX: course_structure
LOCATION:
- edx.devstack.memcached:11211
TIMEOUT: '604800'
default:
BACKEND: django.core.cache.backends.memcached.PyMemcacheCache
OPTIONS:
ignore_exc: true
no_delay: true
use_pooling: true
connect_timeout: 0.5
KEY_FUNCTION: common.djangoapps.util.memcache.safe_key
KEY_PREFIX: default
LOCATION:
- edx.devstack.memcached:11211
VERSION: '1'
general:
BACKEND: django.core.cache.backends.memcached.PyMemcacheCache
OPTIONS:
no_delay: true
ignore_exc: true
use_pooling: true
connect_timeout: 0.5
KEY_FUNCTION: common.djangoapps.util.memcache.safe_key
KEY_PREFIX: general
LOCATION:
- edx.devstack.memcached:11211
mongo_metadata_inheritance:
BACKEND: django.core.cache.backends.memcached.PyMemcacheCache
OPTIONS:
no_delay: true
ignore_exc: true
use_pooling: true
connect_timeout: 0.5
KEY_FUNCTION: common.djangoapps.util.memcache.safe_key
KEY_PREFIX: mongo_metadata_inheritance
LOCATION:
- edx.devstack.memcached:11211
TIMEOUT: 300
staticfiles:
BACKEND: django.core.cache.backends.memcached.PyMemcacheCache
OPTIONS:
no_delay: true
ignore_exc: true
use_pooling: true
connect_timeout: 0.5
KEY_FUNCTION: common.djangoapps.util.memcache.safe_key
KEY_PREFIX: 78f87108afce_general
LOCATION:
- edx.devstack.memcached:11211
CAS_ATTRIBUTE_CALLBACK: ''
CAS_EXTRA_LOGIN_PARAMS: ''
CAS_SERVER_URL: ''
CELERYBEAT_SCHEDULER: celery.beat:PersistentScheduler
CELERY_BROKER_HOSTNAME: localhost
CELERY_BROKER_PASSWORD: ''
CELERY_BROKER_TRANSPORT: redis
CELERY_BROKER_USER: ''
CELERY_BROKER_USE_SSL: false
CELERY_BROKER_VHOST: ''
CELERY_EVENT_QUEUE_TTL: null
CELERY_QUEUES:
- edx.cms.core.default
- edx.cms.core.high
CELERY_TIMEZONE: UTC
CERTIFICATE_TEMPLATE_LANGUAGES:
en: English
es: Español
CERT_QUEUE: certificates
CMS_BASE: edx.devstack.studio:18010
CODE_JAIL:
limits:
CPU: 1
FSIZE: 1048576
PROXY: 0
REALTIME: 3
VMEM: 536870912
python_bin: /edx/app/edxapp/venvs/edxapp-sandbox/bin/python
user: sandbox
COMMENTS_SERVICE_KEY: password
COMMENTS_SERVICE_URL: http://localhost:18080
COMPREHENSIVE_THEME_DIRS:
- ''
COMPREHENSIVE_THEME_LOCALE_PATHS: []
CONTACT_EMAIL: info@example.com
CONTENTSTORE:
ADDITIONAL_OPTIONS: {}
DOC_STORE_CONFIG:
authsource: ''
collection: modulestore
connectTimeoutMS: 2000
db: edxapp
host:
- edx.devstack.mongo
password: password
port: 27017
read_preference: PRIMARY
replicaSet: ''
socketTimeoutMS: 3000
ssl: false
user: edxapp
ENGINE: xmodule.contentstore.mongo.MongoContentStore
OPTIONS:
auth_source: ''
db: edxapp
host:
- edx.devstack.mongo
password: password
port: 27017
ssl: false
user: edxapp
CORS_ORIGIN_ALLOW_ALL: false
CORS_ORIGIN_WHITELIST: []
COURSES_WITH_UNSAFE_CODE: []
COURSE_ABOUT_VISIBILITY_PERMISSION: see_exists
COURSE_AUTHORING_MICROFRONTEND_URL: null
COURSE_CATALOG_API_URL: http://localhost:8008/api/v1
COURSE_CATALOG_URL_ROOT: http://localhost:8008
COURSE_CATALOG_VISIBILITY_PERMISSION: see_exists
COURSE_IMPORT_EXPORT_BUCKET: ''
CREDENTIALS_INTERNAL_SERVICE_URL: http://localhost:8005
CREDENTIALS_PUBLIC_SERVICE_URL: http://localhost:8005
CREDIT_PROVIDER_SECRET_KEYS: {}
CROSS_DOMAIN_CSRF_COOKIE_DOMAIN: ''
CROSS_DOMAIN_CSRF_COOKIE_NAME: ''
CSRF_COOKIE_SECURE: false
CSRF_TRUSTED_ORIGINS: []
DASHBOARD_COURSE_LIMIT: null
DATABASES:
default:
ATOMIC_REQUESTS: true
CONN_MAX_AGE: 0
ENGINE: django.db.backends.mysql
HOST: edx.devstack.mysql80
NAME: edxapp
OPTIONS:
isolation_level: read committed
PASSWORD: password
PORT: '3306'
USER: edxapp001
read_replica:
CONN_MAX_AGE: 0
ENGINE: django.db.backends.mysql
HOST: edx.devstack.mysql80
NAME: edxapp
OPTIONS:
isolation_level: read committed
PASSWORD: password
PORT: '3306'
USER: edxapp001
student_module_history:
CONN_MAX_AGE: 0
ENGINE: django.db.backends.mysql
HOST: edx.devstack.mysql80
NAME: edxapp_csmh
OPTIONS:
isolation_level: read committed
PASSWORD: password
PORT: '3306'
USER: edxapp001
DATA_DIR: /edx/var/edxapp
DEFAULT_COURSE_VISIBILITY_IN_CATALOG: both
DEFAULT_FEEDBACK_EMAIL: feedback@example.com
DEFAULT_FILE_STORAGE: django.core.files.storage.FileSystemStorage
DEFAULT_FROM_EMAIL: registration@example.com
DEFAULT_JWT_ISSUER:
AUDIENCE: lms-key
ISSUER: http://edx.devstack.lms:18000/oauth2
SECRET_KEY: lms-secret
DEFAULT_MOBILE_AVAILABLE: false
DEFAULT_SITE_THEME: ''
DEPRECATED_ADVANCED_COMPONENT_TYPES: []
DJFS:
directory_root: /edx/var/edxapp/django-pyfs/static/django-pyfs
type: osfs
url_root: /static/django-pyfs
DOC_STORE_CONFIG:
authsource: ''
collection: modulestore
connectTimeoutMS: 2000
db: edxapp
host:
- edx.devstack.mongo
password: password
port: 27017
read_preference: PRIMARY
replicaSet: ''
socketTimeoutMS: 3000
ssl: false
user: edxapp
ECOMMERCE_API_SIGNING_KEY: lms-secret
ECOMMERCE_API_URL: http://localhost:8002/api/v2
ECOMMERCE_PUBLIC_URL_ROOT: http://localhost:8002
EDXMKTG_USER_INFO_COOKIE_NAME: edx-user-info
EDX_PLATFORM_REVISION: master
ELASTIC_SEARCH_CONFIG:
- host: edx.devstack.elasticsearch
port: 9200
use_ssl: false
EMAIL_BACKEND: django.core.mail.backends.smtp.EmailBackend
EMAIL_HOST: localhost
EMAIL_HOST_PASSWORD: ''
EMAIL_HOST_USER: ''
EMAIL_PORT: 25
EMAIL_USE_TLS: false
ENABLE_COMPREHENSIVE_THEMING: false
ENTERPRISE_API_URL: http://edx.devstack.lms:18000/enterprise/api/v1
ENTERPRISE_MARKETING_FOOTER_QUERY_PARAMS: {}
ENTERPRISE_SERVICE_WORKER_USERNAME: enterprise_worker
EVENT_TRACKING_SEGMENTIO_EMIT_WHITELIST: []
EXAMS_API_URL: http://localhost:8740/api/v1
EXTRA_MIDDLEWARE_CLASSES: []
FACEBOOK_API_VERSION: v2.1
FACEBOOK_APP_ID: FACEBOOK_APP_ID
FACEBOOK_APP_SECRET: FACEBOOK_APP_SECRET
FEATURES:
AUTH_USE_OPENID_PROVIDER: true
AUTOMATIC_AUTH_FOR_TESTING: false
CUSTOM_COURSES_EDX: false
ENABLE_BULK_ENROLLMENT_VIEW: false
ENABLE_COMBINED_LOGIN_REGISTRATION: true
ENABLE_CORS_HEADERS: false
ENABLE_COUNTRY_ACCESS: false
ENABLE_CREDIT_API: false
ENABLE_CREDIT_ELIGIBILITY: false
ENABLE_CROSS_DOMAIN_CSRF_COOKIE: false
ENABLE_CSMH_EXTENDED: true
ENABLE_DISCUSSION_HOME_PANEL: true
ENABLE_DISCUSSION_SERVICE: true
ENABLE_EDXNOTES: true
ENABLE_ENROLLMENT_RESET: false
ENABLE_EXPORT_GIT: false
ENABLE_GRADE_DOWNLOADS: true
ENABLE_LTI_PROVIDER: false
ENABLE_MKTG_SITE: false
ENABLE_MOBILE_REST_API: false
ENABLE_OAUTH2_PROVIDER: false
ENABLE_PUBLISHER: false
ENABLE_READING_FROM_MULTIPLE_HISTORY_TABLES: true
ENABLE_SPECIAL_EXAMS: false
ENABLE_SYSADMIN_DASHBOARD: false
ENABLE_THIRD_PARTY_AUTH: true
ENABLE_VIDEO_UPLOAD_PIPELINE: false
SHOW_FOOTER_LANGUAGE_SELECTOR: false
SHOW_HEADER_LANGUAGE_SELECTOR: false
FEEDBACK_SUBMISSION_EMAIL: ''
FERNET_KEYS:
- DUMMY KEY CHANGE BEFORE GOING TO PRODUCTION
FILE_UPLOAD_STORAGE_BUCKET_NAME: SET-ME-PLEASE (ex. bucket-name)
FILE_UPLOAD_STORAGE_PREFIX: submissions_attachments
FINANCIAL_REPORTS:
BUCKET: null
ROOT_PATH: sandbox
STORAGE_TYPE: localfs
GITHUB_REPO_ROOT: /edx/var/edxapp/data
GIT_REPO_EXPORT_DIR: /edx/var/edxapp/export_course_repos
GOOGLE_ANALYTICS_ACCOUNT: null
GRADES_DOWNLOAD:
BUCKET: ''
ROOT_PATH: ''
STORAGE_CLASS: django.core.files.storage.FileSystemStorage
STORAGE_KWARGS:
location: /tmp/edx-s3/grades
STORAGE_TYPE: ''
HELP_TOKENS_BOOKS:
course_author: http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course
learner: http://edx.readthedocs.io/projects/open-edx-learner-guide
ICP_LICENSE: null
ICP_LICENSE_INFO: {}
IDA_LOGOUT_URI_LIST: []
ID_VERIFICATION_SUPPORT_LINK: ''
INTEGRATED_CHANNELS_API_CHUNK_TRANSMISSION_LIMIT:
SAP: 1
JWT_AUTH:
JWT_AUDIENCE: lms-key
JWT_AUTH_COOKIE_HEADER_PAYLOAD: edx-jwt-cookie-header-payload
JWT_AUTH_COOKIE_SIGNATURE: edx-jwt-cookie-signature
JWT_ISSUER: http://edx.devstack.lms:18000/oauth2
JWT_ISSUERS:
- AUDIENCE: lms-key
ISSUER: http://edx.devstack.lms:18000/oauth2
SECRET_KEY: lms-secret
JWT_PUBLIC_SIGNING_JWK_SET: ''
JWT_SECRET_KEY: lms-secret
JWT_SIGNING_ALGORITHM: null
JWT_EXPIRATION: 30
JWT_ISSUER: http://edx.devstack.lms:18000/oauth2
JWT_PRIVATE_SIGNING_KEY: null
LANGUAGE_CODE: en
LANGUAGE_COOKIE: openedx-language-preference
LEARNER_PORTAL_URL_ROOT: https://learner-portal-edx.devstack.lms:18000
LMS_BASE: edx.devstack.lms:18000
LMS_INTERNAL_ROOT_URL: http://edx.devstack.lms:18000
LMS_ROOT_URL: http://edx.devstack.lms:18000
LOCAL_LOGLEVEL: INFO
LOGGING_ENV: sandbox
LOGIN_REDIRECT_WHITELIST: []
LOG_DIR: /edx/var/log/edx
MAINTENANCE_BANNER_TEXT: Sample banner message
MEDIA_ROOT: /edx/var/edxapp/media/
MEDIA_URL: /media/
MKTG_URLS: {}
MKTG_URL_LINK_MAP: {}
MOBILE_STORE_ACE_URLS: {}
MODULESTORE:
default:
ENGINE: xmodule.modulestore.mixed.MixedModuleStore
OPTIONS:
mappings: {}
stores:
- DOC_STORE_CONFIG:
authsource: ''
collection: modulestore
connectTimeoutMS: 2000
db: edxapp
host:
- edx.devstack.mongo
password: password
port: 27017
read_preference: PRIMARY
replicaSet: ''
socketTimeoutMS: 3000
ssl: false
user: edxapp
ENGINE: xmodule.modulestore.split_mongo.split_draft.DraftVersioningModuleStore
NAME: split
OPTIONS:
default_class: xmodule.hidden_block.HiddenBlock
fs_root: /edx/var/edxapp/data
render_template: common.djangoapps.edxmako.shortcuts.render_to_string
- DOC_STORE_CONFIG:
authsource: ''
collection: modulestore
connectTimeoutMS: 2000
db: edxapp
host:
- edx.devstack.mongo
password: password
port: 27017
read_preference: PRIMARY
replicaSet: ''
socketTimeoutMS: 3000
ssl: false
user: edxapp
ENGINE: xmodule.modulestore.mongo.DraftMongoModuleStore
NAME: draft
OPTIONS:
default_class: xmodule.hidden_block.HiddenBlock
fs_root: /edx/var/edxapp/data
render_template: common.djangoapps.edxmako.shortcuts.render_to_string
ORA2_FILE_PREFIX: default_env-default_deployment/ora2
PARSE_KEYS: {}
PARTNER_SUPPORT_EMAIL: ''
PASSWORD_POLICY_COMPLIANCE_ROLLOUT_CONFIG:
ENFORCE_COMPLIANCE_ON_LOGIN: false
PASSWORD_RESET_SUPPORT_LINK: ''
PAYMENT_SUPPORT_EMAIL: billing@example.com
PLATFORM_DESCRIPTION: Your Platform Description Here
PLATFORM_FACEBOOK_ACCOUNT: http://www.facebook.com/YourPlatformFacebookAccount
PLATFORM_NAME: Your Platform Name Here
PLATFORM_TWITTER_ACCOUNT: '@YourPlatformTwitterAccount'
POLICY_CHANGE_GRADES_ROUTING_KEY: edx.lms.core.default
SINGLE_LEARNER_COURSE_REGRADE_ROUTING_KEY: edx.lms.core.default
PREPEND_LOCALE_PATHS: []
PRESS_EMAIL: press@example.com
PROCTORING_BACKENDS:
DEFAULT: 'null'
'null': {}
PROCTORING_SETTINGS: {}
REGISTRATION_EXTRA_FIELDS:
city: hidden
confirm_email: hidden
country: required
gender: optional
goals: optional
honor_code: required
level_of_education: optional
mailing_address: hidden
terms_of_service: hidden
year_of_birth: optional
RETIRED_EMAIL_DOMAIN: retired.invalid
RETIRED_EMAIL_PREFIX: retired__user_
RETIRED_USERNAME_PREFIX: retired__user_
RETIRED_USER_SALTS:
- OVERRIDE ME WITH A RANDOM VALUE
- ROTATE SALTS BY APPENDING NEW VALUES
RETIREMENT_SERVICE_WORKER_USERNAME: retirement_worker
RETIREMENT_STATES:
- PENDING
- ERRORED
- ABORTED
- COMPLETE
SECRET_KEY: DUMMY KEY ONLY FOR TO DEVSTACK
SEGMENT_KEY: null
SERVER_EMAIL: sre@example.com
SESSION_COOKIE_DOMAIN: ''
SESSION_COOKIE_NAME: sessionid
SESSION_COOKIE_SECURE: false
SESSION_SAVE_EVERY_REQUEST: false
SITE_NAME: localhost
SOCIAL_AUTH_SAML_SP_PRIVATE_KEY: ''
SOCIAL_AUTH_SAML_SP_PRIVATE_KEY_DICT: {}
SOCIAL_AUTH_SAML_SP_PUBLIC_CERT: ''
SOCIAL_AUTH_SAML_SP_PUBLIC_CERT_DICT: {}
SOCIAL_MEDIA_FOOTER_URLS: {}
SOCIAL_SHARING_SETTINGS:
CERTIFICATE_FACEBOOK: false
CERTIFICATE_TWITTER: false
CUSTOM_COURSE_URLS: false
DASHBOARD_FACEBOOK: false
DASHBOARD_TWITTER: false
STATIC_ROOT_BASE: /edx/var/edxapp/staticfiles
STATIC_URL_BASE: /static/
STUDIO_NAME: Studio
STUDIO_SHORT_NAME: Studio
SUPPORT_SITE_LINK: ''
SWIFT_AUTH_URL: null
SWIFT_AUTH_VERSION: null
SWIFT_KEY: null
SWIFT_REGION_NAME: null
SWIFT_TEMP_URL_DURATION: 1800
SWIFT_TEMP_URL_KEY: null
SWIFT_TENANT_ID: null
SWIFT_TENANT_NAME: null
SWIFT_USERNAME: null
SWIFT_USE_TEMP_URLS: false
SYSLOG_SERVER: ''
SYSTEM_WIDE_ROLE_CLASSES: []
TECH_SUPPORT_EMAIL: technical@example.com
TIME_ZONE: America/New_York
UNIVERSITY_EMAIL: university@example.com
USERNAME_REPLACEMENT_WORKER: OVERRIDE THIS WITH A VALID USERNAME
VIDEO_IMAGE_MAX_AGE: 31536000
VIDEO_IMAGE_SETTINGS:
DIRECTORY_PREFIX: video-images/
STORAGE_KWARGS:
location: /edx/var/edxapp/media/
VIDEO_IMAGE_MAX_BYTES: 2097152
VIDEO_IMAGE_MIN_BYTES: 2048
BASE_URL: /media/
VIDEO_TRANSCRIPTS_MAX_AGE: 31536000
VIDEO_TRANSCRIPTS_SETTINGS:
DIRECTORY_PREFIX: video-transcripts/
STORAGE_KWARGS:
location: /edx/var/edxapp/media/
VIDEO_TRANSCRIPTS_MAX_BYTES: 3145728
BASE_URL: /media/
VIDEO_UPLOAD_PIPELINE:
BUCKET: ''
ROOT_PATH: ''
WIKI_ENABLED: true
XBLOCK_FS_STORAGE_BUCKET: null
XBLOCK_FS_STORAGE_PREFIX: null
XBLOCK_SETTINGS: {}
XQUEUE_INTERFACE:
basic_auth:
- edx
- edx
django_auth:
password: password
username: lms
url: http://edx.devstack.xqueue:18040
X_FRAME_OPTIONS: DENY
YOUTUBE_API_KEY: PUT_YOUR_API_KEY_HERE
ZENDESK_API_KEY: ''
ZENDESK_CUSTOM_FIELDS: {}
ZENDESK_GROUP_ID_MAPPING: {}
ZENDESK_OAUTH_ACCESS_TOKEN: ''
ZENDESK_URL: ''
ZENDESK_USER: ''

View File

@@ -1,3 +0,0 @@
""" Overrides for Docker-based devstack. """
from .devstack import * # pylint: disable=wildcard-import, unused-wildcard-import

View File

@@ -1,46 +0,0 @@
"""
Settings to run Studio in devstack using optimized static assets.
This configuration changes Studio to use the optimized static assets generated for testing,
rather than picking up the files directly from the source tree.
The following Paver command can be used to run Studio in optimized mode:
paver devstack studio --optimized
You can also generate the assets explicitly and then run Studio:
paver update_assets cms --settings=test_static_optimized
paver devstack studio --settings=devstack_optimized --fast
Note that changes to JavaScript assets will not be picked up automatically
as they are for non-optimized devstack. Instead, update_assets must be
invoked each time that changes have been made.
"""
########################## Devstack settings ###################################
from .devstack import * # pylint: disable=wildcard-import
TEST_ROOT = REPO_ROOT / "test_root"
############################ STATIC FILES #############################
# Enable debug so that static assets are served by Django
DEBUG = True
# Set REQUIRE_DEBUG to false so that it behaves like production
REQUIRE_DEBUG = False
# Fetch static files out of the pipeline's static root
STATICFILES_STORAGE = 'pipeline.storage.PipelineManifestStorage'
# Serve static files at /static directly from the staticfiles directory under test root.
# Note: optimized files for testing are generated with settings from test_static_optimized
STATIC_URL = "/static/"
STATICFILES_FINDERS = [
'django.contrib.staticfiles.finders.FileSystemFinder',
]
STATICFILES_DIRS = [
(TEST_ROOT / "staticfiles" / "cms").abspath(),
]

View File

@@ -1,22 +0,0 @@
"""
This config file follows the devstack enviroment, but adds the
requirement of a celery worker running in the background to process
celery tasks.
When testing locally, run lms/cms with this settings file as well, to test queueing
of tasks onto the appropriate workers.
In two separate processes on devstack:
paver devstack studio --settings=devstack_with_worker
DJANGO_SETTINGS_MODULE=cms.envs.devstack_with_worker celery worker --app=cms.celery:APP
"""
# We intentionally define lots of variables that aren't used, and
# want to import all variables from base settings files
# pylint: disable=wildcard-import, unused-wildcard-import
from cms.envs.devstack import *
# Require a separate celery worker
CELERY_ALWAYS_EAGER = False
CLEAR_REQUEST_CACHE_ON_TASK_COMPLETION = True
BROKER_URL = 'redis://:password@edx.devstack.redis:6379/'

View File

@@ -1,32 +0,0 @@
"""
Settings for OpenStack deployments.
"""
from .production import * # pylint: disable=wildcard-import, unused-wildcard-import
SWIFT_AUTH_URL = AUTH_TOKENS.get('SWIFT_AUTH_URL')
SWIFT_AUTH_VERSION = AUTH_TOKENS.get('SWIFT_AUTH_VERSION', 1)
SWIFT_USERNAME = AUTH_TOKENS.get('SWIFT_USERNAME')
SWIFT_KEY = AUTH_TOKENS.get('SWIFT_KEY')
SWIFT_TENANT_NAME = AUTH_TOKENS.get('SWIFT_TENANT_NAME')
SWIFT_TENANT_ID = AUTH_TOKENS.get('SWIFT_TENANT_ID')
SWIFT_CONTAINER_NAME = FILE_UPLOAD_STORAGE_BUCKET_NAME
SWIFT_NAME_PREFIX = FILE_UPLOAD_STORAGE_PREFIX
SWIFT_USE_TEMP_URLS = AUTH_TOKENS.get('SWIFT_USE_TEMP_URLS', False)
SWIFT_TEMP_URL_KEY = AUTH_TOKENS.get('SWIFT_TEMP_URL_KEY')
SWIFT_TEMP_URL_DURATION = AUTH_TOKENS.get('SWIFT_TEMP_URL_DURATION', 1800) # seconds
SWIFT_CONTENT_LENGTH_FROM_FD = AUTH_TOKENS.get('SWIFT_CONTENT_LENGTH_FROM_FD', False)
SWIFT_LAZY_CONNECT = AUTH_TOKENS.get('SWIFT_LAZY_CONNECT', True)
if AUTH_TOKENS.get('SWIFT_REGION_NAME'):
SWIFT_EXTRA_OPTIONS = {'region_name': AUTH_TOKENS['SWIFT_REGION_NAME']}
if AUTH_TOKENS.get('DEFAULT_FILE_STORAGE'):
DEFAULT_FILE_STORAGE = AUTH_TOKENS.get('DEFAULT_FILE_STORAGE')
elif SWIFT_AUTH_URL and SWIFT_USERNAME and SWIFT_KEY:
DEFAULT_FILE_STORAGE = 'swift.storage.SwiftStorage'
else:
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
# Use default file storage class set above for course import/export
COURSE_IMPORT_EXPORT_STORAGE = DEFAULT_FILE_STORAGE

View File

@@ -1,54 +0,0 @@
"""
Settings used when generating static assets for use in tests.
Note: it isn't possible to have a single settings file, because Django doesn't
support both generating static assets to a directory and also serving static
from the same directory.
"""
# Start with the common settings
from openedx.core.lib.derived import derive_settings
from openedx.core.lib.django_require.staticstorage import OptimizedCachedRequireJsStorage
from .common import * # pylint: disable=wildcard-import, unused-wildcard-import
# Use an in-memory database since this settings file is only used for updating assets
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'ATOMIC_REQUESTS': True,
},
}
######################### PIPELINE ####################################
# Use RequireJS optimized storage
STATICFILES_STORAGE = f"{OptimizedCachedRequireJsStorage.__module__}.{OptimizedCachedRequireJsStorage.__name__}"
# Revert to the default set of finders as we don't want to dynamically pick up files from the pipeline
STATICFILES_FINDERS = [
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
'openedx.core.lib.xblock_pipeline.finder.XBlockPipelineFinder',
]
# Redirect to the test_root folder within the repo
TEST_ROOT = REPO_ROOT / "test_root"
LOG_DIR = (TEST_ROOT / "log").abspath()
# Store the static files under test root so that they don't overwrite existing static assets
STATIC_ROOT = (TEST_ROOT / "staticfiles" / "cms").abspath()
WEBPACK_LOADER['DEFAULT']['STATS_FILE'] = STATIC_ROOT / "webpack-stats.json"
# Disable uglify when tests are running (used by build.js).
# 1. Uglify is by far the slowest part of the build process
# 2. Having full source code makes debugging tests easier for developers
os.environ['REQUIRE_BUILD_PROFILE_OPTIMIZE'] = 'none'
########################## Derive Any Derived Settings #######################
derive_settings(__name__)

View File

@@ -264,7 +264,7 @@ if core_toggles.ENTRANCE_EXAMS.is_enabled():
# Enable Web/HTML Certificates
if settings.FEATURES.get('CERTIFICATES_HTML_VIEW'):
from cms.djangoapps.contentstore.views.certificates import (
certificate_activation_handler,
CertificateActivationAPIView,
signatory_detail_handler,
certificates_detail_handler,
certificates_list_handler
@@ -272,7 +272,7 @@ if settings.FEATURES.get('CERTIFICATES_HTML_VIEW'):
urlpatterns += [
re_path(fr'^certificates/activation/{settings.COURSE_KEY_PATTERN}/',
certificate_activation_handler,
CertificateActivationAPIView.as_view(),
name='certificate_activation_handler'),
re_path(r'^certificates/{}/(?P<certificate_id>\d+)/signatories/(?P<signatory_id>\d+)?$'.format(
settings.COURSE_KEY_PATTERN), signatory_detail_handler, name='signatory_detail_handler'),

View File

@@ -21,7 +21,7 @@ class Command(BaseCommand):
Example usage:
# Change entitlement_mode for given order_number with course_uuid to new_mode:
$ ./manage.py lms --settings=devstack_docker update_entitlement_mode \
$ ./manage.py lms --settings=devstack update_entitlement_mode \
ORDER_NUMBER_123,ORDER_NUMBER_456 1234567-0000-1111-2222-123456789012 professional
"""
help = dedent(__doc__).strip()

View File

@@ -19,7 +19,7 @@ class Command(BaseCommand):
"""
help = """This command back-populates domain of the site the user account was created on.
Example: ./manage.py lms populate_created_on_site_user_attribute --users <user_id1>,<user_id2>...
'--activation-keys <key1>,<key2>... --site-domain <site_domain> --settings=devstack_docker"""
'--activation-keys <key1>,<key2>... --site-domain <site_domain> --settings=devstack"""
def add_arguments(self, parser):
"""

View File

@@ -320,10 +320,7 @@ def update_settings_module(service='lms'):
Set the "DJANGO_SETTINGS_MODULE" environment variable appropriately
for the module sphinx-apidoc is about to be run on.
"""
if os.environ.get('EDX_PLATFORM_SETTINGS') == 'devstack_docker':
settings_module = f'{service}.envs.devstack_docker'
else:
settings_module = f'{service}.envs.devstack'
settings_module = f'{service}.envs.devstack'
os.environ['DJANGO_SETTINGS_MODULE'] = settings_module

View File

@@ -287,6 +287,16 @@ class StudentModuleHistory(BaseStudentModuleHistory):
student_module = models.ForeignKey(StudentModule, db_index=True, db_constraint=False, on_delete=models.CASCADE)
def __repr__(self):
student_dict = {
"course_id": str(self.student_module.course_id),
"module_type": self.student_module.module_type,
"student_id": self.student_module.student_id,
"grade": self.grade,
}
return f"StudentModuleHistory<{student_dict!r}>"
def __str__(self):
return str(repr(self))

View File

@@ -7,6 +7,7 @@ from celery import shared_task
from django.contrib.auth import get_user_model
from edx_django_utils.monitoring import set_code_owner_attribute
from opaque_keys.edx.locator import CourseKey
from eventtracking import tracker
from common.djangoapps.student.roles import CourseStaffRole, CourseInstructorRole
from lms.djangoapps.courseware.courses import get_course_with_access
@@ -92,12 +93,18 @@ def send_response_endorsed_notifications(thread_id, response_id, course_key_str,
@shared_task
@set_code_owner_attribute
def delete_course_post_for_user(user_id, username, course_ids):
def delete_course_post_for_user(user_id, username, course_ids, event_data=None):
"""
Deletes all posts for user in a course.
"""
event_data = event_data or {}
log.info(f"<<Bulk Delete>> Deleting all posts for {username} in course {course_ids}")
threads_deleted = Thread.delete_user_threads(user_id, course_ids)
comments_deleted = Comment.delete_user_comments(user_id, course_ids)
log.info(f"<<Bulk Delete>> Deleted {threads_deleted} posts and {comments_deleted} comments for {username} "
f"in course {course_ids}")
event_data.update({
"number_of_posts_deleted": threads_deleted,
"number_of_comments_deleted": comments_deleted,
})
tracker.emit('edx.discussion.bulk_delete_user_posts', event_data)

View File

@@ -1585,8 +1585,14 @@ class BulkDeleteUserPosts(DeveloperErrorViewMixin, APIView):
thread_count = Thread.get_user_threads_count(user.id, course_ids)
if execute_task:
event_data = {
"triggered_by": request.user.username,
"username": username,
"course_or_org": course_or_org,
"course_key": course_id,
}
delete_course_post_for_user.apply_async(
args=(user.id, username, course_ids),
args=(user.id, username, course_ids, event_data),
)
return Response(
{"comment_count": comment_count, "thread_count": thread_count},

View File

@@ -1,13 +0,0 @@
"""
Settings for running management commands for the Analytics Exporter.
The Analytics Exporter jobs run edxapp management commands using production
settings and configuration, however they currently DO NOT use edxapp production
environments (such as edxapp Amazon AMIs or Docker images) where theme files
get installed. As a result we must disable comprehensive theming or else
startup checks from the theming app will throw an error due to missing themes.
"""
from .production import * # pylint: disable=wildcard-import, unused-wildcard-import
ENABLE_COMPREHENSIVE_THEMING = False

View File

@@ -1,655 +0,0 @@
# This file is an experimental extraction of /edx/etc/lms.yml from
# a LMS devstack container.
#
# When devstack is configured to use the new `openedx/` images
# instead of the old `edxops/edxapp` image, it will use this file
# as input to lms/envs/production.py (and, in turn, lms/envs/devstack.py).
# If you are using devstack with the `edxops/edxapp` image, though,
# this file is NOT used.
#
# Q. Should I update this file when I update devstack.py?
# A. You don't *have* to, because settings in devstack.py
# override these settings. But, it doesn't harm to also make them
# here in order to quell confusion. The hope is that we'll
# adopt OEP-45 eventually, which recommends against having
# a devstack.py at all.
#
# This is part of the effort to move our dev tools off of Ansible and
# Paver, described here: https://github.com/openedx/devstack/pull/866
# TODO: If the effort described above is abandoned, then this file should
# probably be deleted.
ACCOUNT_MICROFRONTEND_URL: null
ACE_CHANNEL_DEFAULT_EMAIL: django_email
ACE_CHANNEL_SAILTHRU_API_KEY: ''
ACE_CHANNEL_SAILTHRU_API_SECRET: ''
ACE_CHANNEL_SAILTHRU_DEBUG: true
ACE_CHANNEL_SAILTHRU_TEMPLATE_NAME: null
ACE_CHANNEL_TRANSACTIONAL_EMAIL: django_email
ACE_ENABLED_CHANNELS:
- django_email
ACE_ENABLED_POLICIES:
- bulk_email_optout
ACE_ROUTING_KEY: edx.lms.core.default
ACTIVATION_EMAIL_SUPPORT_LINK: ''
AFFILIATE_COOKIE_NAME: dev_affiliate_id
ALTERNATE_WORKER_QUEUES: cms
ANALYTICS_API_KEY: ''
ANALYTICS_API_URL: http://localhost:18100
ANALYTICS_DASHBOARD_NAME: Your Platform Name Here Insights
ANALYTICS_DASHBOARD_URL: http://localhost:18110/courses
API_ACCESS_FROM_EMAIL: api-requests@example.com
API_ACCESS_MANAGER_EMAIL: api-access@example.com
API_DOCUMENTATION_URL: http://course-catalog-api-guide.readthedocs.io/en/latest/
AUTH_DOCUMENTATION_URL: http://course-catalog-api-guide.readthedocs.io/en/latest/authentication/index.html
AUTH_PASSWORD_VALIDATORS:
- NAME: django.contrib.auth.password_validation.UserAttributeSimilarityValidator
- NAME: common.djangoapps.util.password_policy_validators.MinimumLengthValidator
OPTIONS:
min_length: 2
- NAME: common.djangoapps.util.password_policy_validators.MaximumLengthValidator
OPTIONS:
max_length: 75
AWS_ACCESS_KEY_ID: null
AWS_QUERYSTRING_AUTH: false
AWS_S3_CUSTOM_DOMAIN: SET-ME-PLEASE (ex. bucket-name.s3.amazonaws.com)
AWS_SECRET_ACCESS_KEY: null
AWS_SES_REGION_ENDPOINT: email.us-east-1.amazonaws.com
AWS_SES_REGION_NAME: us-east-1
AWS_STORAGE_BUCKET_NAME: SET-ME-PLEASE (ex. bucket-name)
BASE_COOKIE_DOMAIN: localhost
BLOCK_STRUCTURES_SETTINGS:
COURSE_PUBLISH_TASK_DELAY: 30
TASK_DEFAULT_RETRY_DELAY: 30
TASK_MAX_RETRIES: 5
BRANCH_IO_KEY: ''
BUGS_EMAIL: bugs@example.com
BULK_EMAIL_DEFAULT_FROM_EMAIL: no-reply@example.com
BULK_EMAIL_EMAILS_PER_TASK: 500
BULK_EMAIL_LOG_SENT_EMAILS: false
BULK_EMAIL_ROUTING_KEY_SMALL_JOBS: edx.lms.core.default
CACHES:
celery:
BACKEND: django.core.cache.backends.memcached.PyMemcacheCache
OPTIONS:
no_delay: true
ignore_exc: true
use_pooling: true
connect_timeout: 0.5
KEY_FUNCTION: common.djangoapps.util.memcache.safe_key
KEY_PREFIX: celery
LOCATION:
- edx.devstack.memcached:11211
TIMEOUT: '7200'
configuration:
BACKEND: django.core.cache.backends.memcached.PyMemcacheCache
OPTIONS:
no_delay: true
ignore_exc: true
use_pooling: true
connect_timeout: 0.5
KEY_FUNCTION: common.djangoapps.util.memcache.safe_key
KEY_PREFIX: 78f87108afce
LOCATION:
- edx.devstack.memcached:11211
course_structure_cache:
BACKEND: django.core.cache.backends.memcached.PyMemcacheCache
OPTIONS:
no_delay: true
ignore_exc: true
use_pooling: true
connect_timeout: 0.5
KEY_FUNCTION: common.djangoapps.util.memcache.safe_key
KEY_PREFIX: course_structure
LOCATION:
- edx.devstack.memcached:11211
TIMEOUT: '604800'
default:
BACKEND: django.core.cache.backends.memcached.PyMemcacheCache
OPTIONS:
no_delay: true
ignore_exc: true
use_pooling: true
connect_timeout: 0.5
KEY_FUNCTION: common.djangoapps.util.memcache.safe_key
KEY_PREFIX: default
LOCATION:
- edx.devstack.memcached:11211
VERSION: '1'
general:
BACKEND: django.core.cache.backends.memcached.PyMemcacheCache
OPTIONS:
no_delay: true
ignore_exc: true
use_pooling: true
connect_timeout: 0.5
KEY_FUNCTION: common.djangoapps.util.memcache.safe_key
KEY_PREFIX: general
LOCATION:
- edx.devstack.memcached:11211
mongo_metadata_inheritance:
BACKEND: django.core.cache.backends.memcached.PyMemcacheCache
OPTIONS:
no_delay: true
ignore_exc: true
use_pooling: true
connect_timeout: 0.5
KEY_FUNCTION: common.djangoapps.util.memcache.safe_key
KEY_PREFIX: mongo_metadata_inheritance
LOCATION:
- edx.devstack.memcached:11211
TIMEOUT: 300
staticfiles:
BACKEND: django.core.cache.backends.memcached.PyMemcacheCache
OPTIONS:
no_delay: true
ignore_exc: true
use_pooling: true
connect_timeout: 0.5
KEY_FUNCTION: common.djangoapps.util.memcache.safe_key
KEY_PREFIX: 78f87108afce_general
LOCATION:
- edx.devstack.memcached:11211
CAS_ATTRIBUTE_CALLBACK: ''
CAS_EXTRA_LOGIN_PARAMS: ''
CAS_SERVER_URL: ''
CELERYBEAT_SCHEDULER: celery.beat:PersistentScheduler
CELERY_BROKER_HOSTNAME: localhost
CELERY_BROKER_PASSWORD: ''
CELERY_BROKER_TRANSPORT: redis
CELERY_BROKER_USER: ''
CELERY_BROKER_USE_SSL: false
CELERY_BROKER_VHOST: ''
CELERY_EVENT_QUEUE_TTL: null
CELERY_QUEUES:
- edx.lms.core.default
- edx.lms.core.high
- edx.lms.core.high_mem
CELERY_TIMEZONE: UTC
CERTIFICATE_TEMPLATE_LANGUAGES:
en: English
es: Español
CERT_QUEUE: certificates
CMS_BASE: edx.devstack.studio:18010
CODE_JAIL:
limits:
CPU: 1
FSIZE: 1048576
PROXY: 0
REALTIME: 3
VMEM: 536870912
python_bin: /edx/app/edxapp/venvs/edxapp-sandbox/bin/python
user: sandbox
COMMENTS_SERVICE_KEY: password
COMMENTS_SERVICE_URL: http://localhost:18080
COMPREHENSIVE_THEME_DIRS:
- ''
COMPREHENSIVE_THEME_LOCALE_PATHS: []
CONTACT_EMAIL: info@example.com
CONTACT_MAILING_ADDRESS: SET-ME-PLEASE
CONTENTSTORE:
ADDITIONAL_OPTIONS: {}
DOC_STORE_CONFIG:
authsource: ''
collection: modulestore
connectTimeoutMS: 2000
db: edxapp
host:
- edx.devstack.mongo
password: password
port: 27017
read_preference: SECONDARY_PREFERRED
replicaSet: ''
socketTimeoutMS: 3000
ssl: false
user: edxapp
ENGINE: xmodule.contentstore.mongo.MongoContentStore
OPTIONS:
auth_source: ''
db: edxapp
host:
- edx.devstack.mongo
password: password
port: 27017
ssl: false
user: edxapp
CORS_ORIGIN_ALLOW_ALL: false
CORS_ORIGIN_WHITELIST: []
COURSES_WITH_UNSAFE_CODE: []
COURSE_ABOUT_VISIBILITY_PERMISSION: see_exists
COURSE_CATALOG_API_URL: http://localhost:8008/api/v1
COURSE_CATALOG_URL_ROOT: http://localhost:8008
COURSE_CATALOG_VISIBILITY_PERMISSION: see_exists
CREDENTIALS_INTERNAL_SERVICE_URL: http://localhost:8005
CREDENTIALS_PUBLIC_SERVICE_URL: http://localhost:8005
CREDIT_HELP_LINK_URL: ''
CREDIT_PROVIDER_SECRET_KEYS: {}
CROSS_DOMAIN_CSRF_COOKIE_DOMAIN: ''
CROSS_DOMAIN_CSRF_COOKIE_NAME: ''
CSRF_COOKIE_SECURE: false
CSRF_TRUSTED_ORIGINS: []
DASHBOARD_COURSE_LIMIT: null
DATABASES:
default:
ATOMIC_REQUESTS: true
CONN_MAX_AGE: 0
ENGINE: django.db.backends.mysql
HOST: edx.devstack.mysql80
NAME: edxapp
OPTIONS:
isolation_level: read committed
PASSWORD: password
PORT: '3306'
USER: edxapp001
read_replica:
CONN_MAX_AGE: 0
ENGINE: django.db.backends.mysql
HOST: edx.devstack.mysql80
NAME: edxapp
OPTIONS:
isolation_level: read committed
PASSWORD: password
PORT: '3306'
USER: edxapp001
student_module_history:
CONN_MAX_AGE: 0
ENGINE: django.db.backends.mysql
HOST: edx.devstack.mysql80
NAME: edxapp_csmh
OPTIONS:
isolation_level: read committed
PASSWORD: password
PORT: '3306'
USER: edxapp001
DATA_DIR: /edx/var/edxapp
DCS_SESSION_COOKIE_SAMESITE: Lax
DCS_SESSION_COOKIE_SAMESITE_FORCE_ALL: true
DEFAULT_COURSE_VISIBILITY_IN_CATALOG: both
DEFAULT_FEEDBACK_EMAIL: feedback@example.com
DEFAULT_FILE_STORAGE: django.core.files.storage.FileSystemStorage
DEFAULT_FROM_EMAIL: registration@example.com
DEFAULT_JWT_ISSUER:
AUDIENCE: lms-key
ISSUER: http://edx.devstack.lms:18000/oauth2
SECRET_KEY: lms-secret
DEFAULT_MOBILE_AVAILABLE: false
DEFAULT_SITE_THEME: ''
DEPRECATED_ADVANCED_COMPONENT_TYPES: []
DJFS:
directory_root: /edx/var/edxapp/django-pyfs/static/django-pyfs
type: osfs
url_root: /static/django-pyfs
DOC_STORE_CONFIG:
authsource: ''
collection: modulestore
connectTimeoutMS: 2000
db: edxapp
host:
- edx.devstack.mongo
password: password
port: 27017
read_preference: SECONDARY_PREFERRED
replicaSet: ''
socketTimeoutMS: 3000
ssl: false
user: edxapp
ECOMMERCE_API_SIGNING_KEY: lms-secret
ECOMMERCE_API_URL: http://localhost:8002/api/v2
ECOMMERCE_PUBLIC_URL_ROOT: http://localhost:8002
EDXMKTG_USER_INFO_COOKIE_NAME: edx-user-info
EDXNOTES_INTERNAL_API: http://edx.devstack.edx_notes_api:18120/api/v1
EDXNOTES_PUBLIC_API: http://localhost:18120/api/v1
EDX_API_KEY: PUT_YOUR_API_KEY_HERE
EDX_PLATFORM_REVISION: master
ELASTIC_SEARCH_CONFIG:
- host: edx.devstack.elasticsearch
port: 9200
use_ssl: false
EMAIL_BACKEND: django.core.mail.backends.smtp.EmailBackend
EMAIL_HOST: localhost
EMAIL_HOST_PASSWORD: ''
EMAIL_HOST_USER: ''
EMAIL_PORT: 25
EMAIL_USE_TLS: false
ENABLE_COMPREHENSIVE_THEMING: false
ENTERPRISE_API_URL: http://edx.devstack.lms:18000/enterprise/api/v1
ENTERPRISE_COURSE_ENROLLMENT_AUDIT_MODES:
- audit
- honor
ENTERPRISE_CUSTOMER_SUCCESS_EMAIL: customersuccess@edx.org
ENTERPRISE_ENROLLMENT_API_URL: http://edx.devstack.lms:18000/api/enrollment/v1/
ENTERPRISE_INTEGRATIONS_EMAIL: enterprise-integrations@edx.org
ENTERPRISE_MARKETING_FOOTER_QUERY_PARAMS: {}
ENTERPRISE_SERVICE_WORKER_USERNAME: enterprise_worker
ENTERPRISE_SUPPORT_URL: ''
ENTERPRISE_TAGLINE: ''
EVENT_TRACKING_SEGMENTIO_EMIT_WHITELIST: []
EXTRA_MIDDLEWARE_CLASSES: []
FACEBOOK_API_VERSION: v2.1
FACEBOOK_APP_ID: FACEBOOK_APP_ID
FACEBOOK_APP_SECRET: FACEBOOK_APP_SECRET
FEATURES:
AUTH_USE_OPENID_PROVIDER: true
AUTOMATIC_AUTH_FOR_TESTING: false
CUSTOM_COURSES_EDX: false
ENABLE_BULK_ENROLLMENT_VIEW: false
ENABLE_COMBINED_LOGIN_REGISTRATION: true
ENABLE_CORS_HEADERS: false
ENABLE_COUNTRY_ACCESS: false
ENABLE_CREDIT_API: false
ENABLE_CREDIT_ELIGIBILITY: false
ENABLE_CROSS_DOMAIN_CSRF_COOKIE: false
ENABLE_CSMH_EXTENDED: true
ENABLE_DISCUSSION_HOME_PANEL: true
ENABLE_DISCUSSION_SERVICE: true
ENABLE_EDXNOTES: true
ENABLE_ENROLLMENT_RESET: false
ENABLE_EXPORT_GIT: false
ENABLE_GRADE_DOWNLOADS: true
ENABLE_LTI_PROVIDER: false
ENABLE_MKTG_SITE: false
ENABLE_MOBILE_REST_API: false
ENABLE_OAUTH2_PROVIDER: false
ENABLE_PUBLISHER: false
ENABLE_READING_FROM_MULTIPLE_HISTORY_TABLES: true
ENABLE_SPECIAL_EXAMS: false
ENABLE_SYSADMIN_DASHBOARD: false
ENABLE_THIRD_PARTY_AUTH: true
ENABLE_VIDEO_UPLOAD_PIPELINE: false
SHOW_FOOTER_LANGUAGE_SELECTOR: false
SHOW_HEADER_LANGUAGE_SELECTOR: false
FEEDBACK_SUBMISSION_EMAIL: ''
FERNET_KEYS:
- DUMMY KEY CHANGE BEFORE GOING TO PRODUCTION
FILE_UPLOAD_STORAGE_BUCKET_NAME: SET-ME-PLEASE (ex. bucket-name)
FILE_UPLOAD_STORAGE_PREFIX: submissions_attachments
FINANCIAL_REPORTS:
BUCKET: null
ROOT_PATH: sandbox
STORAGE_TYPE: localfs
GITHUB_REPO_ROOT: /edx/var/edxapp/data
GIT_REPO_DIR: /edx/var/edxapp/course_repos
GOOGLE_ANALYTICS_ACCOUNT: null
GOOGLE_ANALYTICS_LINKEDIN: ''
GOOGLE_ANALYTICS_TRACKING_ID: ''
GOOGLE_SITE_VERIFICATION_ID: ''
GRADES_DOWNLOAD:
BUCKET: ''
ROOT_PATH: ''
STORAGE_CLASS: django.core.files.storage.FileSystemStorage
STORAGE_KWARGS:
location: /tmp/edx-s3/grades
STORAGE_TYPE: ''
HELP_TOKENS_BOOKS:
course_author: http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course
learner: http://edx.readthedocs.io/projects/open-edx-learner-guide
HTTPS: 'on'
ICP_LICENSE: null
ICP_LICENSE_INFO: {}
IDA_LOGOUT_URI_LIST: []
ID_VERIFICATION_SUPPORT_LINK: ''
INTEGRATED_CHANNELS_API_CHUNK_TRANSMISSION_LIMIT:
SAP: 1
JWT_AUTH:
JWT_AUDIENCE: lms-key
JWT_AUTH_COOKIE_HEADER_PAYLOAD: edx-jwt-cookie-header-payload
JWT_AUTH_COOKIE_SIGNATURE: edx-jwt-cookie-signature
JWT_ISSUER: http://edx.devstack.lms:18000/oauth2
JWT_ISSUERS:
- AUDIENCE: lms-key
ISSUER: http://edx.devstack.lms:18000/oauth2
SECRET_KEY: lms-secret
JWT_PRIVATE_SIGNING_JWK: None
JWT_PUBLIC_SIGNING_JWK_SET: ''
JWT_SECRET_KEY: lms-secret
JWT_SIGNING_ALGORITHM: null
JWT_EXPIRATION: 30
JWT_ISSUER: http://edx.devstack.lms:18000/oauth2
JWT_PRIVATE_SIGNING_KEY: null
LANGUAGE_CODE: en
LANGUAGE_COOKIE: openedx-language-preference
LEARNER_PORTAL_URL_ROOT: https://learner-portal-edx.devstack.lms:18000
LEARNING_MICROFRONTEND_URL: null
LMS_BASE: edx.devstack.lms:18000
LMS_INTERNAL_ROOT_URL: http://edx.devstack.lms:18000
LMS_ROOT_URL: http://edx.devstack.lms:18000
LOCAL_LOGLEVEL: INFO
LOGGING_ENV: sandbox
LOGIN_REDIRECT_WHITELIST: []
LOG_DIR: /edx/var/log/edx
LTI_AGGREGATE_SCORE_PASSBACK_DELAY: 900
LTI_USER_EMAIL_DOMAIN: lti.example.com
MAILCHIMP_NEW_USER_LIST_ID: null
MAINTENANCE_BANNER_TEXT: Sample banner message
MEDIA_ROOT: /edx/var/edxapp/media/
MEDIA_URL: /media/
MKTG_URLS: {}
MKTG_URL_LINK_MAP: {}
MOBILE_STORE_URLS: {}
MODULESTORE:
default:
ENGINE: xmodule.modulestore.mixed.MixedModuleStore
OPTIONS:
mappings: {}
stores:
- DOC_STORE_CONFIG:
authsource: ''
collection: modulestore
connectTimeoutMS: 2000
db: edxapp
host:
- edx.devstack.mongo
password: password
port: 27017
read_preference: SECONDARY_PREFERRED
replicaSet: ''
socketTimeoutMS: 3000
ssl: false
user: edxapp
ENGINE: xmodule.modulestore.split_mongo.split_draft.DraftVersioningModuleStore
NAME: split
OPTIONS:
default_class: xmodule.hidden_block.HiddenBlock
fs_root: /edx/var/edxapp/data
render_template: common.djangoapps.edxmako.shortcuts.render_to_string
- DOC_STORE_CONFIG:
authsource: ''
collection: modulestore
connectTimeoutMS: 2000
db: edxapp
host:
- edx.devstack.mongo
password: password
port: 27017
read_preference: PRIMARY
replicaSet: ''
socketTimeoutMS: 3000
ssl: false
user: edxapp
ENGINE: xmodule.modulestore.mongo.DraftMongoModuleStore
NAME: draft
OPTIONS:
default_class: xmodule.hidden_block.HiddenBlock
fs_root: /edx/var/edxapp/data
render_template: common.djangoapps.edxmako.shortcuts.render_to_string
OAUTH_DELETE_EXPIRED: true
OAUTH_ENFORCE_SECURE: false
OAUTH_EXPIRE_CONFIDENTIAL_CLIENT_DAYS: 365
OAUTH_EXPIRE_PUBLIC_CLIENT_DAYS: 30
OPTIMIZELY_PROJECT_ID: null
ORA2_FILE_PREFIX: default_env-default_deployment/ora2
ORDER_HISTORY_MICROFRONTEND_URL: null
ORGANIZATIONS_AUTOCREATE: true
PAID_COURSE_REGISTRATION_CURRENCY:
- usd
- $
PARENTAL_CONSENT_AGE_LIMIT: 13
PARTNER_SUPPORT_EMAIL: ''
PASSWORD_POLICY_COMPLIANCE_ROLLOUT_CONFIG:
ENFORCE_COMPLIANCE_ON_LOGIN: false
PASSWORD_RESET_SUPPORT_LINK: ''
PAYMENT_SUPPORT_EMAIL: billing@example.com
PDF_RECEIPT_BILLING_ADDRESS: 'Enter your receipt billing
address here.
'
PDF_RECEIPT_COBRAND_LOGO_PATH: ''
PDF_RECEIPT_DISCLAIMER_TEXT: 'ENTER YOUR RECEIPT DISCLAIMER TEXT HERE.
'
PDF_RECEIPT_FOOTER_TEXT: 'Enter your receipt footer text here.
'
PDF_RECEIPT_LOGO_PATH: ''
PDF_RECEIPT_TAX_ID: 00-0000000
PDF_RECEIPT_TAX_ID_LABEL: fake Tax ID
PDF_RECEIPT_TERMS_AND_CONDITIONS: 'Enter your receipt terms and conditions here.
'
PLATFORM_DESCRIPTION: Your Platform Description Here
PLATFORM_FACEBOOK_ACCOUNT: http://www.facebook.com/YourPlatformFacebookAccount
PLATFORM_NAME: Your Platform Name Here
PLATFORM_TWITTER_ACCOUNT: '@YourPlatformTwitterAccount'
POLICY_CHANGE_GRADES_ROUTING_KEY: edx.lms.core.default
SINGLE_LEARNER_COURSE_REGRADE_ROUTING_KEY: edx.lms.core.default
PREPEND_LOCALE_PATHS: []
PRESS_EMAIL: press@example.com
PROCTORING_BACKENDS:
DEFAULT: 'null'
'null': {}
PROCTORING_SETTINGS: {}
PROFILE_IMAGE_BACKEND:
class: openedx.core.storage.OverwriteStorage
options:
base_url: /media/profile-images/
location: /edx/var/edxapp/media/profile-images/
PROFILE_IMAGE_HASH_SEED: placeholder_secret_key
PROFILE_IMAGE_MAX_BYTES: 1048576
PROFILE_IMAGE_MIN_BYTES: 100
PROFILE_IMAGE_SIZES_MAP:
full: 500
large: 120
medium: 50
small: 30
PROFILE_MICROFRONTEND_URL: null
PROGRAM_CERTIFICATES_ROUTING_KEY: edx.lms.core.default
PROGRAM_CONSOLE_MICROFRONTEND_URL: null
RECALCULATE_GRADES_ROUTING_KEY: edx.lms.core.default
REGISTRATION_EXTRA_FIELDS:
city: hidden
confirm_email: hidden
country: required
gender: optional
goals: optional
honor_code: required
level_of_education: optional
mailing_address: hidden
terms_of_service: hidden
year_of_birth: optional
RETIRED_EMAIL_DOMAIN: retired.invalid
RETIRED_EMAIL_PREFIX: retired__user_
RETIRED_USERNAME_PREFIX: retired__user_
RETIRED_USER_SALTS:
- OVERRIDE ME WITH A RANDOM VALUE
- ROTATE SALTS BY APPENDING NEW VALUES
RETIREMENT_SERVICE_WORKER_USERNAME: retirement_worker
RETIREMENT_STATES:
- PENDING
- ERRORED
- ABORTED
- COMPLETE
SECRET_KEY: DUMMY KEY ONLY FOR TO DEVSTACK
SEGMENT_KEY: null
SERVER_EMAIL: sre@example.com
SESSION_COOKIE_DOMAIN: ''
SESSION_COOKIE_NAME: sessionid
SESSION_COOKIE_SECURE: false
SESSION_SAVE_EVERY_REQUEST: false
SITE_NAME: localhost
SOCIAL_AUTH_OAUTH_SECRETS: ''
SOCIAL_AUTH_SAML_SP_PRIVATE_KEY: ''
SOCIAL_AUTH_SAML_SP_PRIVATE_KEY_DICT: {}
SOCIAL_AUTH_SAML_SP_PUBLIC_CERT: ''
SOCIAL_AUTH_SAML_SP_PUBLIC_CERT_DICT: {}
SOCIAL_MEDIA_FOOTER_URLS: {}
SOCIAL_SHARING_SETTINGS:
CERTIFICATE_FACEBOOK: false
CERTIFICATE_TWITTER: false
CUSTOM_COURSE_URLS: false
DASHBOARD_FACEBOOK: false
DASHBOARD_TWITTER: false
STATIC_ROOT_BASE: /edx/var/edxapp/staticfiles
STATIC_URL_BASE: /static/
STUDIO_NAME: Studio
STUDIO_SHORT_NAME: Studio
SUPPORT_SITE_LINK: ''
SWIFT_AUTH_URL: null
SWIFT_AUTH_VERSION: null
SWIFT_KEY: null
SWIFT_REGION_NAME: null
SWIFT_TEMP_URL_DURATION: 1800
SWIFT_TEMP_URL_KEY: null
SWIFT_TENANT_ID: null
SWIFT_TENANT_NAME: null
SWIFT_USERNAME: null
SWIFT_USE_TEMP_URLS: false
SYSLOG_SERVER: ''
SYSTEM_WIDE_ROLE_CLASSES: []
TECH_SUPPORT_EMAIL: technical@example.com
THIRD_PARTY_AUTH_BACKENDS:
- social_core.backends.google.GoogleOAuth2
- social_core.backends.linkedin.LinkedinOAuth2
- social_core.backends.facebook.FacebookOAuth2
- social_core.backends.azuread.AzureADOAuth2
- common.djangoapps.third_party_auth.appleid.AppleIdAuth
- common.djangoapps.third_party_auth.identityserver3.IdentityServer3
- common.djangoapps.third_party_auth.saml.SAMLAuthBackend
- common.djangoapps.third_party_auth.lti.LTIAuthBackend
TIME_ZONE: America/New_York
TRACKING_SEGMENTIO_WEBHOOK_SECRET: ''
UNIVERSITY_EMAIL: university@example.com
USERNAME_REPLACEMENT_WORKER: OVERRIDE THIS WITH A VALID USERNAME
VERIFY_STUDENT:
DAYS_GOOD_FOR: 365
EXPIRING_SOON_WINDOW: 28
VIDEO_CDN_URL:
EXAMPLE_COUNTRY_CODE: http://example.com/edx/video?s3_url=
VIDEO_IMAGE_MAX_AGE: 31536000
VIDEO_IMAGE_SETTINGS:
DIRECTORY_PREFIX: video-images/
STORAGE_KWARGS:
location: /edx/var/edxapp/media/
VIDEO_IMAGE_MAX_BYTES: 2097152
VIDEO_IMAGE_MIN_BYTES: 2048
BASE_URL: /media/
VIDEO_TRANSCRIPTS_MAX_AGE: 31536000
VIDEO_TRANSCRIPTS_SETTINGS:
DIRECTORY_PREFIX: video-transcripts/
STORAGE_KWARGS:
location: /edx/var/edxapp/media/
VIDEO_TRANSCRIPTS_MAX_BYTES: 3145728
BASE_URL: /media/
VIDEO_UPLOAD_PIPELINE:
BUCKET: ''
ROOT_PATH: ''
WIKI_ENABLED: true
WRITABLE_GRADEBOOK_URL: null
XBLOCK_FS_STORAGE_BUCKET: null
XBLOCK_FS_STORAGE_PREFIX: null
XBLOCK_SETTINGS: {}
XQUEUE_INTERFACE:
basic_auth:
- edx
- edx
django_auth:
password: password
username: lms
url: http://edx.devstack.xqueue:18040
X_FRAME_OPTIONS: DENY
YOUTUBE_API_KEY: PUT_YOUR_API_KEY_HERE
ZENDESK_API_KEY: ''
ZENDESK_CUSTOM_FIELDS: {}
ZENDESK_GROUP_ID_MAPPING: {}
ZENDESK_OAUTH_ACCESS_TOKEN: ''
ZENDESK_URL: ''
ZENDESK_USER: ''

View File

@@ -1,9 +0,0 @@
"""
Left over environment file from before the transition of devstack from
vagrant to docker was complete.
This file should no longer be used, and is only around in case something
still refers to it.
"""
from .devstack import * # pylint: disable=wildcard-import, unused-wildcard-import

View File

@@ -1,45 +0,0 @@
"""
Settings to run LMS in devstack using optimized static assets.
This configuration changes LMS to use the optimized static assets generated for testing,
rather than picking up the files directly from the source tree.
The following Paver command can be used to run LMS in optimized mode:
paver devstack lms --optimized
You can also generate the assets explicitly and then run Studio:
paver update_assets lms --settings=test_static_optimized
paver devstack lms --settings=devstack_optimized --fast
Note that changes to JavaScript assets will not be picked up automatically
as they are for non-optimized devstack. Instead, update_assets must be
invoked each time that changes have been made.
"""
########################## Devstack settings ###################################
from .devstack import * # pylint: disable=wildcard-import
TEST_ROOT = REPO_ROOT / "test_root"
############################ STATIC FILES #############################
# Enable debug so that static assets are served by Django
DEBUG = True
# Set REQUIRE_DEBUG to false so that it behaves like production
REQUIRE_DEBUG = False
# Fetch static files out of the pipeline's static root
STATICFILES_STORAGE = 'pipeline.storage.PipelineManifestStorage'
# Serve static files at /static directly from the staticfiles directory under test root.
# Note: optimized files for testing are generated with settings from test_static_optimized
STATIC_URL = "/static/"
STATICFILES_FINDERS = ['django.contrib.staticfiles.finders.FileSystemFinder']
STATICFILES_DIRS = [
(TEST_ROOT / "staticfiles" / "lms").abspath(),
]

View File

@@ -1,23 +0,0 @@
"""
This config file follows the devstack environment, but adds the
requirement of a celery worker running in the background to process
celery tasks.
When testing locally, run lms/cms with this settings file as well, to test queueing
of tasks onto the appropriate workers.
In two separate processes on devstack:
paver devstack lms --settings=devstack_with_worker
DJANGO_SETTINGS_MODULE=lms.envs.devstack_with_worker celery worker --app=lms.celery:APP
"""
# We intentionally define lots of variables that aren't used, and
# want to import all variables from base settings files
# pylint: disable=wildcard-import, unused-wildcard-import
from lms.envs.devstack import *
# Require a separate celery worker
CELERY_ALWAYS_EAGER = False
CLEAR_REQUEST_CACHE_ON_TASK_COMPLETION = True
BROKER_URL = 'redis://:password@edx.devstack.redis:6379/'

View File

@@ -1,9 +0,0 @@
"""
Specific overrides to the base prod settings for a docker production deployment.
"""
from .production import * # pylint: disable=wildcard-import, unused-wildcard-import
from openedx.core.lib.logsettings import get_docker_logger_config
LOGGING = get_docker_logger_config()

View File

@@ -1,32 +0,0 @@
"""
Settings for OpenStack deployments.
"""
from .production import * # pylint: disable=wildcard-import, unused-wildcard-import
SWIFT_AUTH_URL = AUTH_TOKENS.get('SWIFT_AUTH_URL')
SWIFT_AUTH_VERSION = AUTH_TOKENS.get('SWIFT_AUTH_VERSION', 1)
SWIFT_USERNAME = AUTH_TOKENS.get('SWIFT_USERNAME')
SWIFT_KEY = AUTH_TOKENS.get('SWIFT_KEY')
SWIFT_TENANT_NAME = AUTH_TOKENS.get('SWIFT_TENANT_NAME')
SWIFT_TENANT_ID = AUTH_TOKENS.get('SWIFT_TENANT_ID')
SWIFT_CONTAINER_NAME = FILE_UPLOAD_STORAGE_BUCKET_NAME
SWIFT_NAME_PREFIX = FILE_UPLOAD_STORAGE_PREFIX
SWIFT_USE_TEMP_URLS = AUTH_TOKENS.get('SWIFT_USE_TEMP_URLS', False)
SWIFT_TEMP_URL_KEY = AUTH_TOKENS.get('SWIFT_TEMP_URL_KEY')
SWIFT_TEMP_URL_DURATION = AUTH_TOKENS.get('SWIFT_TEMP_URL_DURATION', 1800) # seconds
SWIFT_CONTENT_TYPE_FROM_FD = AUTH_TOKENS.get('SWIFT_CONTENT_TYPE_FROM_FD', True)
SWIFT_CONTENT_LENGTH_FROM_FD = AUTH_TOKENS.get('SWIFT_CONTENT_LENGTH_FROM_FD', False)
SWIFT_LAZY_CONNECT = AUTH_TOKENS.get('SWIFT_LAZY_CONNECT', True)
if AUTH_TOKENS.get('SWIFT_REGION_NAME'):
SWIFT_EXTRA_OPTIONS = {'region_name': AUTH_TOKENS['SWIFT_REGION_NAME']}
if AUTH_TOKENS.get('DEFAULT_FILE_STORAGE'):
DEFAULT_FILE_STORAGE = AUTH_TOKENS.get('DEFAULT_FILE_STORAGE')
elif SWIFT_AUTH_URL and SWIFT_USERNAME and SWIFT_KEY:
DEFAULT_FILE_STORAGE = 'swift.storage.SwiftStorage'
else:
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
ORA2_FILEUPLOAD_BACKEND = "django"

View File

@@ -1,76 +0,0 @@
"""
This config file runs the simplest dev environment using sqlite, and db-based
sessions. Assumes structure:
/envroot/
/db # This is where it'll write the database file
/edx-platform # The location of this repo
/log # Where we're going to write log files
"""
# We intentionally define lots of variables that aren't used, and
# want to import all variables from base settings files
# pylint: disable=wildcard-import, unused-wildcard-import
from openedx.core.lib.derived import derive_settings
from openedx.core.lib.logsettings import get_logger_config
from .common import *
STATIC_GRAB = True
LOGGING = get_logger_config(ENV_ROOT / "log",
logging_env="dev")
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ENV_ROOT / "db" / "edx.db",
'ATOMIC_REQUESTS': True,
},
'student_module_history': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': ENV_ROOT / "db" / "student_module_history.db",
'ATOMIC_REQUESTS': True,
}
}
CACHES = {
# This is the cache used for most things.
# In staging/prod envs, the sessions also live here.
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'edx_loc_mem_cache',
'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key',
},
# The general cache is what you get if you use our util.cache. It's used for
# things like caching the course.xml file for different A/B test groups.
# We set it to be a DummyCache to force reloading of course.xml in dev.
# In staging environments, we would grab VERSION from data uploaded by the
# push process.
'general': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
'KEY_PREFIX': 'general',
'VERSION': 4,
'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key',
}
}
# Dummy secret key for dev
SECRET_KEY = '85920908f28904ed733fe576320db18cabd7b6cd'
############################ FILE UPLOADS (for discussion forums) #############################
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
MEDIA_ROOT = ENV_ROOT / "uploads"
MEDIA_URL = "/discussion/upfiles/"
FILE_UPLOAD_TEMP_DIR = ENV_ROOT / "uploads"
FILE_UPLOAD_HANDLERS = [
'django.core.files.uploadhandler.MemoryFileUploadHandler',
'django.core.files.uploadhandler.TemporaryFileUploadHandler',
]
########################## Derive Any Derived Settings #######################
derive_settings(__name__)

View File

@@ -1,71 +0,0 @@
"""
Settings used when generating static assets for use in tests.
Note: it isn't possible to have a single settings file, because Django doesn't
support both generating static assets to a directory and also serving static
from the same directory.
"""
# Start with the common settings
from openedx.core.lib.derived import derive_settings
from openedx.core.lib.django_require.staticstorage import OptimizedCachedRequireJsStorage
from .common import * # pylint: disable=wildcard-import, unused-wildcard-import
# Use an in-memory database since this settings file is only used for updating assets
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'ATOMIC_REQUESTS': True,
},
'student_module_history': {
'ENGINE': 'django.db.backends.sqlite3',
},
}
# Provide a dummy XQUEUE_INTERFACE setting as LMS expects it to exist on start up
XQUEUE_INTERFACE = {
"url": "https://sandbox-xqueue.edx.org",
"django_auth": {
"username": "lms",
"password": "***REMOVED***"
},
"basic_auth": ('anant', 'agarwal'),
}
PROCTORING_BACKENDS = {
'DEFAULT': 'mock',
'mock': {},
'mock_proctoring_without_rules': {},
}
######################### PIPELINE ####################################
# Use RequireJS optimized storage
STATICFILES_STORAGE = f"{OptimizedCachedRequireJsStorage.__module__}.{OptimizedCachedRequireJsStorage.__name__}"
# Revert to the default set of finders as we don't want to dynamically pick up files from the pipeline
STATICFILES_FINDERS = [
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
'openedx.core.lib.xblock_pipeline.finder.XBlockPipelineFinder',
]
# Redirect to the test_root folder within the repo
TEST_ROOT = REPO_ROOT / "test_root"
LOG_DIR = (TEST_ROOT / "log").abspath()
# Store the static files under test root so that they don't overwrite existing static assets
STATIC_ROOT = (TEST_ROOT / "staticfiles" / "lms").abspath()
WEBPACK_LOADER['DEFAULT']['STATS_FILE'] = STATIC_ROOT / "webpack-stats.json"
# Disable uglify when tests are running (used by build.js).
# 1. Uglify is by far the slowest part of the build process
# 2. Having full source code makes debugging tests easier for developers
os.environ['REQUIRE_BUILD_PROFILE_OPTIMIZE'] = 'none'
########################## Derive Any Derived Settings #######################
derive_settings(__name__)

View File

@@ -39,7 +39,7 @@ def parse_args():
lms.add_argument(
'--settings',
help="Which django settings module to use under lms.envs. If not provided, the DJANGO_SETTINGS_MODULE "
"environment variable will be used if it is set, otherwise it will default to lms.envs.devstack_docker")
"environment variable will be used if it is set, otherwise it will default to lms.envs.devstack")
lms.add_argument(
'--service-variant',
choices=['lms', 'lms-xml', 'lms-preview'],
@@ -48,7 +48,7 @@ def parse_args():
lms.set_defaults(
help_string=lms.format_help(),
settings_base='lms/envs',
default_settings='lms.envs.devstack_docker',
default_settings='lms.envs.devstack',
)
cms = subparsers.add_parser(
@@ -60,12 +60,12 @@ def parse_args():
cms.add_argument(
'--settings',
help="Which django settings module to use under cms.envs. If not provided, the DJANGO_SETTINGS_MODULE "
"environment variable will be used if it is set, otherwise it will default to cms.envs.devstack_docker")
"environment variable will be used if it is set, otherwise it will default to cms.envs.devstack")
cms.add_argument('-h', '--help', action='store_true', help='show this help message and exit')
cms.set_defaults(
help_string=cms.format_help(),
settings_base='cms/envs',
default_settings='cms.envs.devstack_docker',
default_settings='cms.envs.devstack',
service_variant='cms',
)

View File

@@ -41,11 +41,11 @@ class Command(BaseCommand):
Example usage:
# Process all certs/grades changes for a given course:
$ ./manage.py lms --settings=devstack_docker notify_credentials \
$ ./manage.py lms --settings=devstack notify_credentials \
--courses course-v1:edX+DemoX+Demo_Course
# Process all certs/grades changes in a given time range:
$ ./manage.py lms --settings=devstack_docker notify_credentials \
$ ./manage.py lms --settings=devstack notify_credentials \
--start-date 2018-06-01 --end-date 2018-07-31
A Dry Run will produce output that looks like:

View File

@@ -0,0 +1,57 @@
"""
Management command to fix NotificationPreference records with invalid 'Mixed' email_cadence values
created during migration.
"""
import logging
from django.core.management.base import BaseCommand
from openedx.core.djangoapps.notifications.models import NotificationPreference
logger = logging.getLogger(__name__)
class Command(BaseCommand):
"""
Management command to identify and correct NotificationPreference records
with an invalid 'Mixed' value in the email_cadence field.
By default, the command runs in dry-run mode and only logs the count of
affected records. Use the `--fix` flag to replace all 'Mixed' values with
'Daily', ensuring data consistency with defined model choices.
Invoke with:
python manage.py [lms] fix_mixed_email_cadence --fix
"""
help = (
"Identifies NotificationPreference records with 'Mixed' as email_cadence "
"and optionally replaces it with a valid value (default: 'Daily')."
)
def add_arguments(self, parser):
parser.add_argument(
'--fix',
action='store_true',
help='Apply the fix by replacing "Mixed" with "Daily". Default is dry-run mode.'
)
def handle(self, *args, **options):
fix_mode = options['fix']
invalid_records = NotificationPreference.objects.filter(email_cadence='Mixed')
count = invalid_records.count()
if count == 0:
logger.info("No records found with invalid 'Mixed' value in email_cadence.")
return
logger.info(f"Found {count} NotificationPreference records with 'Mixed' email_cadence.")
if fix_mode:
updated_count = invalid_records.update(
email_cadence=NotificationPreference.EmailCadenceChoices.DAILY
)
logger.info(f"Successfully updated {updated_count} records. 'Mixed' replaced with 'Daily'.")
else:
logger.warning(
"Dry-run mode: no changes were made.\n"
"To apply changes, re-run the command with the --fix flag."
)

View File

@@ -130,21 +130,21 @@ can use:
::
./manage.py lms --settings devstack_docker send_recurring_nudge example.com
./manage.py lms --settings devstack send_recurring_nudge example.com
You can override the “current date” when running a command. The app will run,
using the date you specify as its "today":
::
./manage.py lms --settings devstack_docker send_recurring_nudge example.com --date 2017-11-13
./manage.py lms --settings devstack send_recurring_nudge example.com --date 2017-11-13
If the app is paired with Sailthru, you can override which email addresses the
app sends to. The app will send all emails to the address you specify:
::
./manage.py lms --settings devstack_docker send_recurring_nudge example.com --override-recipient-email developer@example.com
./manage.py lms --settings devstack send_recurring_nudge example.com --override-recipient-email developer@example.com
These management commands are meant to be run daily. We schedule them to
run automatically in a Jenkins job. You can use a similar automation
@@ -419,7 +419,7 @@ To begin using Litmus, follow these steps:
::
./manage.py lms --settings devstack_docker send_recurring_nudge example.com --override-recipient-email PUT-LITMUS-ADDRESS-HERE
./manage.py lms --settings devstack send_recurring_nudge example.com --override-recipient-email PUT-LITMUS-ADDRESS-HERE
Using the Litmus Browser Extenstion to test emails saved as local files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -7,13 +7,7 @@ from django.core import checks
_DEVSTACK_SETTINGS_MODULES = [
"lms.envs.devstack",
"lms.envs.devstack_docker",
"lms.envs.devstack_optimized",
"lms.envs.devstack_with_worker",
"cms.envs.devstack",
"cms.envs.devstack_docker",
"cms.envs.devstack_optimized",
"cms.envs.devstack_with_worker",
]
@@ -26,7 +20,7 @@ def warn_if_devstack_settings(**kwargs):
return [
checks.Warning(
"Open edX Devstack is deprecated, so the Django settings module you are using "
f"({settings.SETTINGS_MODULE}) will be removed from openedx/edx-platform by October 2024. "
f"({settings.SETTINGS_MODULE}) will be removed from openedx/edx-platform in 2025. "
"Please either migrate off of Devstack, or modify your Devstack fork to work with an externally-"
"managed Django settings file. "
"For details and discussion, see: https://github.com/openedx/public-engineering/issues/247.",

View File

@@ -51,7 +51,7 @@ django-stubs<6
# The team that owns this package will manually bump this package rather than having it pulled in automatically.
# This is to allow them to better control its deployment and to do it in a process that works better
# for them.
edx-enterprise==6.2.11
edx-enterprise==6.2.13
# Date: 2023-07-26
# Our legacy Sass code is incompatible with anything except this ancient libsass version.
@@ -70,7 +70,7 @@ numpy<2.0.0
# Date: 2023-09-18
# pinning this version to avoid updates while the library is being developed
# Issue for unpinning: https://github.com/openedx/edx-platform/issues/35269
openedx-learning==0.26.0
openedx-learning==0.27.0
# Date: 2023-11-29
# Open AI version 1.0.0 dropped support for openai.ChatCompletion which is currently in use in enterprise.

View File

@@ -473,7 +473,7 @@ edx-drf-extensions==10.6.0
# edxval
# enterprise-integrated-channels
# openedx-learning
edx-enterprise==6.2.11
edx-enterprise==6.2.13
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
@@ -566,7 +566,7 @@ enmerkar==0.7.1
# via enmerkar-underscore
enmerkar-underscore==2.4.0
# via -r requirements/edx/kernel.in
enterprise-integrated-channels==0.1.11
enterprise-integrated-channels==0.1.13
# via -r requirements/edx/bundled.in
event-tracking==3.3.0
# via
@@ -840,7 +840,7 @@ openedx-filters==2.1.0
# ora2
openedx-forum==0.3.0
# via -r requirements/edx/kernel.in
openedx-learning==0.26.0
openedx-learning==0.27.0
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/kernel.in
@@ -1174,7 +1174,9 @@ text-unidecode==1.3
tinycss2==1.4.0
# via bleach
tomlkit==0.13.2
# via snowflake-connector-python
# via
# openedx-learning
# snowflake-connector-python
tqdm==4.67.1
# via
# nltk

View File

@@ -750,7 +750,7 @@ edx-drf-extensions==10.6.0
# edxval
# enterprise-integrated-channels
# openedx-learning
edx-enterprise==6.2.11
edx-enterprise==6.2.13
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
@@ -879,7 +879,7 @@ enmerkar-underscore==2.4.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
enterprise-integrated-channels==0.1.11
enterprise-integrated-channels==0.1.13
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
@@ -1400,7 +1400,7 @@ openedx-forum==0.3.0
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
openedx-learning==0.26.0
openedx-learning==0.27.0
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/doc.txt
@@ -2104,6 +2104,7 @@ tomlkit==0.13.2
# via
# -r requirements/edx/doc.txt
# -r requirements/edx/testing.txt
# openedx-learning
# pylint
# snowflake-connector-python
tox==4.26.0

View File

@@ -555,7 +555,7 @@ edx-drf-extensions==10.6.0
# edxval
# enterprise-integrated-channels
# openedx-learning
edx-enterprise==6.2.11
edx-enterprise==6.2.13
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
@@ -653,7 +653,7 @@ enmerkar==0.7.1
# enmerkar-underscore
enmerkar-underscore==2.4.0
# via -r requirements/edx/base.txt
enterprise-integrated-channels==0.1.11
enterprise-integrated-channels==0.1.13
# via -r requirements/edx/base.txt
event-tracking==3.3.0
# via
@@ -1005,7 +1005,7 @@ openedx-filters==2.1.0
# ora2
openedx-forum==0.3.0
# via -r requirements/edx/base.txt
openedx-learning==0.26.0
openedx-learning==0.27.0
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
@@ -1474,6 +1474,7 @@ tinycss2==1.4.0
tomlkit==0.13.2
# via
# -r requirements/edx/base.txt
# openedx-learning
# snowflake-connector-python
tqdm==4.67.1
# via

View File

@@ -580,7 +580,7 @@ edx-drf-extensions==10.6.0
# edxval
# enterprise-integrated-channels
# openedx-learning
edx-enterprise==6.2.11
edx-enterprise==6.2.13
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
@@ -680,7 +680,7 @@ enmerkar==0.7.1
# enmerkar-underscore
enmerkar-underscore==2.4.0
# via -r requirements/edx/base.txt
enterprise-integrated-channels==0.1.11
enterprise-integrated-channels==0.1.13
# via -r requirements/edx/base.txt
event-tracking==3.3.0
# via
@@ -1064,7 +1064,7 @@ openedx-filters==2.1.0
# ora2
openedx-forum==0.3.0
# via -r requirements/edx/base.txt
openedx-learning==0.26.0
openedx-learning==0.27.0
# via
# -c requirements/edx/../constraints.txt
# -r requirements/edx/base.txt
@@ -1563,6 +1563,7 @@ tinycss2==1.4.0
tomlkit==0.13.2
# via
# -r requirements/edx/base.txt
# openedx-learning
# pylint
# snowflake-connector-python
tox==4.26.0

View File

@@ -1,24 +1,21 @@
"""
Template block
"""
import logging
from string import Template
from xblock.core import XBlock
from lxml import etree
from web_fragments.fragment import Fragment
from xblock.core import XBlock
from xmodule.editing_block import EditingMixin
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.raw_block import RawMixin
from xmodule.util.builtin_assets import add_webpack_js_to_fragment, add_css_to_fragment
from xmodule.x_module import (
ResourceTemplates,
shim_xmodule_js,
XModuleMixin,
XModuleToXBlockMixin,
)
from xmodule.util.builtin_assets import add_css_to_fragment, add_webpack_js_to_fragment
from xmodule.x_module import ResourceTemplates, XModuleMixin, XModuleToXBlockMixin, shim_xmodule_js
from xmodule.xml_block import XmlMixin
from openedx.core.djangolib.markup import Text
log = logging.getLogger(__name__)
class CustomTagTemplateBlock( # pylint: disable=abstract-method
@@ -76,8 +73,10 @@ class CustomTagBlock(CustomTagTemplateBlock): # pylint: disable=abstract-method
def render_template(self, system, xml_data):
'''Render the template, given the definition xml_data'''
if not xml_data:
return "Please set the template for this custom tag."
xmltree = etree.fromstring(xml_data)
if 'impl' in xmltree.attrib:
if 'impl' in xmltree.attrib and xmltree.attrib['impl']:
template_name = xmltree.attrib['impl']
else:
# VS[compat] backwards compatibility with old nested customtag structure
@@ -86,16 +85,19 @@ class CustomTagBlock(CustomTagTemplateBlock): # pylint: disable=abstract-method
template_name = child_impl.text
else:
# TODO (vshnayder): better exception type
raise Exception("Could not find impl attribute in customtag {}"
.format(self.location))
return Template("Could not find impl attribute in customtag {}").safe_substitute({})
params = dict(list(xmltree.items()))
# cdodge: look up the template as a module
template_loc = self.location.replace(category='custom_tag_template', name=template_name)
try:
template_block = system.get_block(template_loc)
template_block_data = template_block.data
except ItemNotFoundError as ex:
template_block_data = f"Could not find template block for custom tag with Id {template_name}"
log.info(template_block_data)
template_block = system.get_block(template_loc)
template_block_data = template_block.data
template = Template(template_block_data)
return template.safe_substitute(params)
@@ -120,8 +122,7 @@ class CustomTagBlock(CustomTagTemplateBlock): # pylint: disable=abstract-method
class TranslateCustomTagBlock( # pylint: disable=abstract-method
XModuleToXBlockMixin,
XModuleMixin,
CustomTagBlock,
):
"""
Converts olx of the form `<$custom_tag attr="" attr=""/>` to CustomTagBlock
@@ -129,19 +130,20 @@ class TranslateCustomTagBlock( # pylint: disable=abstract-method
"""
resources_dir = None
@classmethod
def parse_xml(cls, node, runtime, _keys):
"""
Transforms the xml_data from <$custom_tag attr="" attr=""/> to
<customtag attr="" attr="" impl="$custom_tag"/>
"""
runtime.error_tracker(Text('WARNING: the <{tag}> tag is deprecated. '
'Instead, use <customtag impl="{tag}" attr1="..." attr2="..."/>. ')
.format(tag=node.tag))
def render_template(self, system, xml_data):
xml_string = ""
if xml_data:
xmltree = etree.fromstring(xml_data)
xmltree = self.replace_xml(xmltree)
xml_string = etree.tostring(xmltree, pretty_print=True).decode("utf-8")
return super().render_template(system, xml_string or xml_data)
def replace_xml(self, node):
"""
Replaces the xml_data from <$custom_tag attr="" attr=""/> to
<customtag attr="" attr="" impl="$custom_tag"/>.
"""
tag = node.tag
node.tag = 'customtag'
node.attrib['impl'] = tag
return runtime.process_xml(etree.tostring(node))
return node