diff --git a/cms/envs/common.py b/cms/envs/common.py index 7a2a3343a8db..9fca3c7e12b5 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -46,16 +46,13 @@ from django.utils.translation import gettext_lazy as _ -import lms.envs.common - from openedx.envs.common import * # pylint: disable=wildcard-import from path import Path as path -from lms.djangoapps.lms_xblock.mixin import LmsBlockMixin from cms.lib.xblock.authoring_mixin import AuthoringMixin from cms.lib.xblock.upstream_sync import UpstreamSyncMixin -from xmodule.modulestore.edit_info import EditInfoMixin +from xmodule.x_module import ResourceTemplates from openedx.core.lib.derived import Derived from openedx.core.lib.features_setting_proxy import FeaturesProxy @@ -269,117 +266,50 @@ ############################# MICROFRONTENDS ################################### COURSE_AUTHORING_MICROFRONTEND_URL = None -############################# SOCIAL MEDIA SHARING ############################# -SOCIAL_SHARING_SETTINGS = { - # Note: Ensure 'CUSTOM_COURSE_URLS' has a matching value in lms/envs/common.py - 'CUSTOM_COURSE_URLS': False, - 'DASHBOARD_FACEBOOK': False, - 'CERTIFICATE_FACEBOOK': False, - 'CERTIFICATE_TWITTER': False, - 'DASHBOARD_TWITTER': False -} - ############################# SET PATH INFORMATION ############################# PROJECT_ROOT = path(__file__).abspath().dirname().dirname() # /edx-platform/cms -REPO_ROOT = PROJECT_ROOT.dirname() -COMMON_ROOT = REPO_ROOT / "common" -OPENEDX_ROOT = REPO_ROOT / "openedx" CMS_ROOT = REPO_ROOT / "cms" LMS_ROOT = REPO_ROOT / "lms" -ENV_ROOT = REPO_ROOT.dirname() # virtualenv dir /edx-platform is in -COURSES_ROOT = ENV_ROOT / "data" -XMODULE_ROOT = REPO_ROOT / "xmodule" GITHUB_REPO_ROOT = ENV_ROOT / "data" -# For geolocation ip database -GEOIP_PATH = REPO_ROOT / "common/static/data/geoip/GeoLite2-Country.mmdb" - -DATA_DIR = COURSES_ROOT - ######################## BRANCH.IO ########################### BRANCH_IO_KEY = '' -######################## GOOGLE ANALYTICS ########################### -GOOGLE_ANALYTICS_ACCOUNT = None - ######################## HOTJAR ########################### HOTJAR_ID = 00000 ############################# TEMPLATE CONFIGURATION ############################# -# Mako templating -import tempfile -MAKO_MODULE_DIR = os.path.join(tempfile.gettempdir(), 'mako_cms') -MAKO_TEMPLATE_DIRS_BASE = [ - PROJECT_ROOT / 'templates', - COMMON_ROOT / 'templates', - COMMON_ROOT / 'djangoapps' / 'pipeline_mako' / 'templates', - COMMON_ROOT / 'static', # required to statically include common Underscore templates - OPENEDX_ROOT / 'core' / 'djangoapps' / 'cors_csrf' / 'templates', - OPENEDX_ROOT / 'core' / 'djangoapps' / 'dark_lang' / 'templates', - OPENEDX_ROOT / 'core' / 'lib' / 'license' / 'templates', - CMS_ROOT / 'djangoapps' / 'pipeline_js' / 'templates', - XMODULE_ROOT / 'capa' / 'templates', -] -CONTEXT_PROCESSORS = ( - 'django.template.context_processors.request', - 'django.template.context_processors.static', - 'django.contrib.messages.context_processors.messages', - 'django.template.context_processors.i18n', - 'django.contrib.auth.context_processors.auth', # this is required for admin - 'django.template.context_processors.csrf', - 'help_tokens.context_processor', - 'openedx.core.djangoapps.site_configuration.context_processors.configuration_context', -) +MAKO_TEMPLATE_DIRS_BASE.insert(3, COMMON_ROOT / 'static') +MAKO_TEMPLATE_DIRS_BASE.append(CMS_ROOT / 'djangoapps' / 'pipeline_js' / 'templates') +MAKO_TEMPLATE_DIRS_BASE.append(XMODULE_ROOT / 'capa' / 'templates') -# Django templating -TEMPLATES = [ - { - 'NAME': 'django', - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - # Don't look for template source files inside installed applications. - 'APP_DIRS': False, - # Instead, look for template source files in these dirs. - 'DIRS': Derived(make_mako_template_dirs), - # Options specific to this backend. - 'OPTIONS': { - 'loaders': ( - # We have to use mako-aware template loaders to be able to include - # mako templates inside django templates (such as main_django.html). - 'openedx.core.djangoapps.theming.template_loaders.ThemeTemplateLoader', - 'common.djangoapps.edxmako.makoloader.MakoFilesystemLoader', - 'common.djangoapps.edxmako.makoloader.MakoAppDirectoriesLoader', - ), - 'context_processors': CONTEXT_PROCESSORS, - # Change 'debug' in your environment settings files - not here. - 'debug': False - } - }, - { - 'NAME': 'mako', - 'BACKEND': 'common.djangoapps.edxmako.backend.Mako', - 'APP_DIRS': False, - 'DIRS': Derived(make_mako_template_dirs), - 'OPTIONS': { - 'context_processors': CONTEXT_PROCESSORS, - 'debug': False, - } - }, + +def make_lms_template_path(settings): + """ + Make the path for the LMS "templates" dir + """ + templates_path = settings.PROJECT_ROOT / 'templates' + return templates_path.replace('cms', 'lms') + +lms_mako_template_dirs_base[0] = Derived(make_lms_template_path) + +TEMPLATES[0]['DIRS'] = Derived(make_mako_template_dirs) +TEMPLATES.append( { # This separate copy of the Mako backend is used to render previews using the LMS templates 'NAME': 'preview', 'BACKEND': 'common.djangoapps.edxmako.backend.Mako', 'APP_DIRS': False, - 'DIRS': lms.envs.common.MAKO_TEMPLATE_DIRS_BASE, + 'DIRS': lms_mako_template_dirs_base, 'OPTIONS': { 'context_processors': CONTEXT_PROCESSORS, 'debug': False, 'namespace': 'lms.main', } - }, -] -DEFAULT_TEMPLATE_ENGINE = TEMPLATES[0] + } +) #################################### AWS ####################################### AWS_SECURITY_TOKEN = None @@ -387,13 +317,8 @@ ############################################################################## # use the ratelimit backend to prevent brute force attacks -AUTHENTICATION_BACKENDS = [ - 'auth_backends.backends.EdXOAuth2', - 'rules.permissions.ObjectPermissionBackend', - 'openedx.core.djangoapps.content_libraries.auth.LtiAuthenticationBackend', - 'django.contrib.auth.backends.AllowAllUsersModelBackend', - 'bridgekeeper.backends.RulePermissionBackend', -] +AUTHENTICATION_BACKENDS.insert(0, 'auth_backends.backends.EdXOAuth2') +AUTHENTICATION_BACKENDS.insert(2, 'openedx.core.djangoapps.content_libraries.auth.LtiAuthenticationBackend') LMS_BASE = None @@ -416,8 +341,6 @@ MAINTENANCE_BANNER_TEXT = 'Sample banner message' -WIKI_ENABLED = True - CERT_QUEUE = 'certificates' ################################# Middleware ################################### @@ -510,27 +433,14 @@ ############# XBlock Configuration ########## -# Import after sys.path fixup -from xmodule.modulestore.inheritance import InheritanceMixin -from xmodule.x_module import XModuleMixin, ResourceTemplates - -# These are the Mixins that will be added to every Blocklike upon instantiation. -# DO NOT EXPAND THIS LIST!! We want it eventually to be EMPTY. Why? Because dynamically adding functions/behaviors to -# objects at runtime is confusing for both developers and static tooling (pylint/mypy). Instead... -# - to add special Blocklike behaviors just for your site: override `XBLOCK_EXTRA_MIXINS` with your own XBlockMixins. -# - to add new functionality to all Blocklikes: add it to the base Blocklike class in the core openedx/XBlock repo. -XBLOCK_MIXINS = ( - # TODO: For each of these, either - # (a) merge their functionality into the base Blocklike class, or - # (b) refactor their functionality out of the Blocklike objects and into the edx-platform block runtimes. - LmsBlockMixin, - InheritanceMixin, - ResourceTemplates, - XModuleMixin, - EditInfoMixin, +# DO NOT EXPAND THIS LIST!! See declaration in openedx/envs/common.py for more information +mixins = list(XBLOCK_MIXINS) +mixins.insert(2, ResourceTemplates) +mixins += [ UpstreamSyncMixin, # Should be above AuthoringMixin for UpstreamSyncMixin.editor_saved to take effect AuthoringMixin, -) +] +XBLOCK_MIXINS = tuple(mixins) ############################ ORA 2 ############################################ @@ -539,116 +449,19 @@ ############################ Modulestore Configuration ################################ -DOC_STORE_CONFIG = { - 'db': 'edxapp', - 'host': 'localhost', - 'replicaSet': '', - 'user': 'edxapp', - 'port': 27017, - 'collection': 'modulestore', - 'ssl': False, - # https://api.mongodb.com/python/2.9.1/api/pymongo/mongo_client.html#module-pymongo.mongo_client - # default is never timeout while the connection is open, - #this means it needs to explicitly close raising pymongo.errors.NetworkTimeout - 'socketTimeoutMS': 6000, - 'connectTimeoutMS': 2000, # default is 20000, I believe raises pymongo.errors.ConnectionFailure - # Not setting waitQueueTimeoutMS and waitQueueMultiple since pymongo defaults to nobody being allowed to wait - 'auth_source': None, - 'read_preference': 'PRIMARY' - # If 'asset_collection' defined, it'll be used - # as the collection name for asset metadata. - # Otherwise, a default collection name will be used. -} - -CONTENTSTORE = { - 'ENGINE': 'xmodule.contentstore.mongo.MongoContentStore', - # connection strings are duplicated temporarily for - # backward compatibility - 'OPTIONS': { - 'db': 'edxapp', - 'host': 'localhost', - 'password': 'password', - 'port': 27017, - 'user': 'edxapp', - 'ssl': False, - 'auth_source': None - }, - 'ADDITIONAL_OPTIONS': {}, - 'DOC_STORE_CONFIG': DOC_STORE_CONFIG -} +CONTENTSTORE['DOC_STORE_CONFIG']['read_preference'] = 'PRIMARY' MODULESTORE_BRANCH = 'draft-preferred' -MODULESTORE = { - 'default': { - 'ENGINE': 'xmodule.modulestore.mixed.MixedModuleStore', - 'OPTIONS': { - 'mappings': {}, - 'stores': [ - { - 'NAME': 'split', - 'ENGINE': 'xmodule.modulestore.split_mongo.split_draft.DraftVersioningModuleStore', - 'DOC_STORE_CONFIG': DOC_STORE_CONFIG, - 'OPTIONS': { - 'default_class': 'xmodule.hidden_block.HiddenBlock', - 'fs_root': DATA_DIR, - 'render_template': 'common.djangoapps.edxmako.shortcuts.render_to_string', - } - }, - { - 'NAME': 'draft', - 'ENGINE': 'xmodule.modulestore.mongo.DraftMongoModuleStore', - 'DOC_STORE_CONFIG': DOC_STORE_CONFIG, - 'OPTIONS': { - 'default_class': 'xmodule.hidden_block.HiddenBlock', - 'fs_root': DATA_DIR, - 'render_template': 'common.djangoapps.edxmako.shortcuts.render_to_string', - } - } - ] - } - } -} - -# Modulestore-level field override providers. These field override providers don't -# require student context. -MODULESTORE_FIELD_OVERRIDE_PROVIDERS = () - DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' -DEFAULT_HASHING_ALGORITHM = 'sha256' #################### Python sandbox ############################################ -CODE_JAIL = { - # from https://github.com/openedx/codejail/blob/master/codejail/django_integration.py#L24, '' should be same as None - 'python_bin': '/edx/app/edxapp/venvs/edxapp-sandbox/bin/python', - # User to run as in the sandbox. - 'user': 'sandbox', - - # Configurable limits. - 'limits': { - # How many CPU seconds can jailed code use? - 'CPU': 1, - # Limit the memory of the jailed process to something high but not - # infinite (512MiB in bytes) - 'VMEM': 536870912, - # Time in seconds that the jailed process has to run. - 'REALTIME': 3, - 'PROXY': 0, - # Needs to be non-zero so that jailed code can use it as their temp directory.(1MiB in bytes) - 'FSIZE': 1048576, - }, - - # Overrides to default configurable 'limits' (above). - # Keys should be course run ids. - # Values should be dictionaries that look like 'limits'. - "limit_overrides": {}, -} +# Needs to be non-zero so that jailed code can use it as their temp directory.(1MiB in bytes) +CODE_JAIL['limits']['FSIZE'] = 1048576 ############################ DJANGO_BUILTINS ################################ -ROOT_URLCONF = 'cms.urls' - COURSE_IMPORT_EXPORT_BUCKET = '' COURSE_METADATA_EXPORT_BUCKET = '' @@ -684,48 +497,19 @@ STATIC_URL = '/static/studio/' STATIC_ROOT = os.environ.get('STATIC_ROOT_CMS', ENV_ROOT / 'staticfiles' / 'studio') -STATICFILES_DIRS = [ - COMMON_ROOT / "static", - PROJECT_ROOT / "static", - # Temporarily adding the following static path as we are migrating the built-in blocks' Sass to vanilla CSS. - # Once all of the built-in blocks are extracted from edx-platform, we can remove this static path. - # Relevant ticket: https://github.com/openedx/edx-platform/issues/35300 - XMODULE_ROOT / "static", -] - +# Storage COURSE_IMPORT_EXPORT_STORAGE = 'django.core.files.storage.FileSystemStorage' COURSE_METADATA_EXPORT_STORAGE = 'django.core.files.storage.FileSystemStorage' -STATICI18N_ROOT = PROJECT_ROOT / "static" - ##### custom vendor plugin variables ##### ############################### PIPELINE ####################################### -PIPELINE = { - 'PIPELINE_ENABLED': True, - # Don't use compression by default - 'CSS_COMPRESSOR': None, +PIPELINE.update({ 'JS_COMPRESSOR': None, - # Don't wrap JavaScript as there is code that depends upon updating the global namespace - 'DISABLE_WRAPPER': True, - # Specify the UglifyJS binary to use - 'UGLIFYJS_BINARY': 'node_modules/.bin/uglifyjs', 'COMPILERS': (), 'YUI_BINARY': 'yui-compressor', -} - -STATICFILES_STORAGE_KWARGS = {} - -# List of finder classes that know how to find static files in various locations. -# Note: the pipeline finder is included to be able to discover optimized files -STATICFILES_FINDERS = [ - 'openedx.core.djangoapps.theming.finders.ThemeFilesFinder', - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', - 'openedx.core.lib.xblock_pipeline.finder.XBlockPipelineFinder', - 'pipeline.finders.PipelineFinder', -] +}) PIPELINE['STYLESHEETS'] = { 'style-vendor': { @@ -825,78 +609,23 @@ }, } -STATICFILES_IGNORE_PATTERNS = ( - "*.py", - "*.pyc", - - # It would be nice if we could do, for example, "**/*.scss", - # but these strings get passed down to the `fnmatch` module, - # which doesn't support that. :( - # http://docs.python.org/2/library/fnmatch.html - "sass/*.scss", - "sass/*/*.scss", - "sass/*/*/*.scss", - "sass/*/*/*/*.scss", - - # Ignore tests - "spec", - "spec_helpers", - - # Symlinks used by js-test-tool - "xmodule_js", - "common_static", -) +STATICFILES_IGNORE_PATTERNS.append("common_static") ################################# DJANGO-REQUIRE ############################### -# The name of a build profile to use for your project, relative to REQUIRE_BASE_URL. -# A sensible value would be 'app.build.js'. Leave blank to use the built-in default build profile. -# Set to False to disable running the default profile (e.g. if only using it to build Standalone -# Modules) -REQUIRE_BUILD_PROFILE = "cms/js/build.js" - # The name of the require.js script used by your project, relative to REQUIRE_BASE_URL. REQUIRE_JS = "js/vendor/requiresjs/require.js" -########################## DJANGO WEBPACK LOADER ############################## - -WEBPACK_LOADER = { - 'DEFAULT': { - 'BUNDLE_DIR_NAME': 'bundles/', - 'STATS_FILE': os.path.join(STATIC_ROOT, 'webpack-stats.json'), - }, - 'WORKERS': { - 'BUNDLE_DIR_NAME': 'bundles/', - 'STATS_FILE': os.path.join(STATIC_ROOT, 'webpack-worker-stats.json') - } -} - ############################ SERVICE_VARIANT ################################## -# SERVICE_VARIANT specifies name of the variant used, which decides what JSON -# configuration files are read during startup. -SERVICE_VARIANT = os.environ.get('SERVICE_VARIANT', 'cms') - -# CONFIG_PREFIX specifies the prefix of the JSON configuration files, -# based on the service variant. If no variant is use, don't use a -# prefix. -CONFIG_PREFIX = SERVICE_VARIANT + "." if SERVICE_VARIANT else "" - +SERVICE_VARIANT = 'cms' ################################# CELERY ###################################### -# Name the exchange and queues for each variant - -QUEUE_VARIANT = CONFIG_PREFIX.lower() - -CELERY_DEFAULT_EXCHANGE = f'edx.{QUEUE_VARIANT}core' - -HIGH_PRIORITY_QUEUE = f'edx.{QUEUE_VARIANT}core.high' -DEFAULT_PRIORITY_QUEUE = f'edx.{QUEUE_VARIANT}core.default' -LOW_PRIORITY_QUEUE = f'edx.{QUEUE_VARIANT}core.low' - -CELERY_DEFAULT_QUEUE = DEFAULT_PRIORITY_QUEUE -CELERY_DEFAULT_ROUTING_KEY = DEFAULT_PRIORITY_QUEUE +# Name the exchange and queues w.r.t the SERVICE_VARIANT +HIGH_PRIORITY_QUEUE = f'edx.{SERVICE_VARIANT}.core.high' +DEFAULT_PRIORITY_QUEUE = f'edx.{SERVICE_VARIANT}.core.default' +LOW_PRIORITY_QUEUE = f'edx.{SERVICE_VARIANT}.core.low' CELERY_QUEUES = { HIGH_PRIORITY_QUEUE: {}, @@ -904,17 +633,11 @@ LOW_PRIORITY_QUEUE: {}, } -# Queues configuration - CLEAR_REQUEST_CACHE_ON_TASK_COMPLETION = True -BROKER_USE_SSL = Derived(lambda settings: settings.CELERY_BROKER_USE_SSL) - CELERY_ALWAYS_EAGER = False -############################## HEARTBEAT ###################################### - -HEARTBEAT_CELERY_ROUTING_KEY = HIGH_PRIORITY_QUEUE +BROKER_USE_SSL = Derived(lambda settings: settings.CELERY_BROKER_USE_SSL) ############################## Video ########################################## @@ -923,12 +646,7 @@ ############################# SETTINGS FOR VIDEO UPLOAD PIPELINE ############################# -VIDEO_UPLOAD_PIPELINE = { - 'VEM_S3_BUCKET': '', - 'BUCKET': '', - 'ROOT_PATH': '', - 'CONCURRENT_UPLOAD_LIMIT': 4, -} +VIDEO_UPLOAD_PIPELINE['CONCURRENT_UPLOAD_LIMIT'] = 4 ############################ APPS ##################################### @@ -1193,131 +911,14 @@ "openedx_learning.apps.authoring.sections", ] - -################# EDX MARKETING SITE ################################## - -MKTG_URL_LINK_MAP = {} - -ID_VERIFICATION_SUPPORT_LINK = '' -PASSWORD_RESET_SUPPORT_LINK = '' -ACTIVATION_EMAIL_SUPPORT_LINK = '' -LOGIN_ISSUE_SUPPORT_LINK = '' - -############################## EVENT TRACKING ################################# - -TRACK_MAX_EVENT = 50000 - -TRACKING_BACKENDS = { - 'logger': { - 'ENGINE': 'common.djangoapps.track.backends.logger.LoggerBackend', - 'OPTIONS': { - 'name': 'tracking' - } - } -} - -# We're already logging events, and we don't want to capture user -# names/passwords. Heartbeat events are likely not interesting. -TRACKING_IGNORE_URL_PATTERNS = [r'^/event', r'^/login', r'^/heartbeat'] - -EVENT_TRACKING_ENABLED = True -EVENT_TRACKING_BACKENDS = { - 'tracking_logs': { - 'ENGINE': 'eventtracking.backends.routing.RoutingBackend', - 'OPTIONS': { - 'backends': { - 'logger': { - 'ENGINE': 'eventtracking.backends.logger.LoggerBackend', - 'OPTIONS': { - 'name': 'tracking', - 'max_event_size': TRACK_MAX_EVENT, - } - } - }, - 'processors': [ - {'ENGINE': 'common.djangoapps.track.shim.LegacyFieldMappingProcessor'}, - {'ENGINE': 'common.djangoapps.track.shim.PrefixedEventProcessor'} - ] - } - }, - 'segmentio': { - 'ENGINE': 'eventtracking.backends.routing.RoutingBackend', - 'OPTIONS': { - 'backends': { - 'segment': {'ENGINE': 'eventtracking.backends.segment.SegmentBackend'} - }, - 'processors': [ - { - 'ENGINE': 'eventtracking.processors.whitelist.NameWhitelistProcessor', - 'OPTIONS': { - 'whitelist': [] - } - }, - { - 'ENGINE': 'common.djangoapps.track.shim.GoogleAnalyticsProcessor' - } - ] - } - } -} -EVENT_TRACKING_PROCESSORS = [] - -EVENT_TRACKING_SEGMENTIO_EMIT_WHITELIST = [] +### Apps only installed in some instances +add_optional_apps(OPTIONAL_APPS, INSTALLED_APPS) ##### ACCOUNT LOCKOUT DEFAULT PARAMETERS ##### MAX_FAILED_LOGIN_ATTEMPTS_ALLOWED = 6 MAX_FAILED_LOGIN_ATTEMPTS_LOCKOUT_PERIOD_SECS = 30 * 60 -### Apps only installed in some instances -# The order of INSTALLED_APPS matters, so this tuple is the app name and the item in INSTALLED_APPS -# that this app should be inserted *before*. A None here means it should be appended to the list. -OPTIONAL_APPS = ( - ('problem_builder', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - ('edx_sga', None), - - # edx-ora2 - ('submissions', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - ('openassessment', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - ('openassessment.assessment', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - ('openassessment.fileupload', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - ('openassessment.staffgrader', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - ('openassessment.workflow', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - ('openassessment.xblock', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - - # edxval - ('edxval', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - - # Enterprise App (http://github.com/openedx/edx-enterprise) - ('enterprise', None), - ('consent', None), - ('integrated_channels.integrated_channel', None), - ('integrated_channels.degreed', None), - ('integrated_channels.degreed2', None), - ('integrated_channels.sap_success_factors', None), - ('integrated_channels.xapi', None), - ('integrated_channels.cornerstone', None), - ('integrated_channels.blackboard', None), - ('integrated_channels.canvas', None), - ('integrated_channels.moodle', None), -) - - -for app_name, insert_before in OPTIONAL_APPS: - # First attempt to only find the module rather than actually importing it, - # to avoid circular references - only try to import if it can't be found - # by find_spec, which doesn't work with import hooks - if importlib.util.find_spec(app_name) is None: - try: - __import__(app_name) - except ImportError: - continue - - try: - INSTALLED_APPS.insert(INSTALLED_APPS.index(insert_before), app_name) - except (IndexError, ValueError): - INSTALLED_APPS.append(app_name) - ### Size of chunks into which asset uploads will be divided UPLOAD_CHUNK_SIZE_IN_MB = 10 @@ -1413,27 +1014,11 @@ XBLOCK_FS_STORAGE_BUCKET = None XBLOCK_FS_STORAGE_PREFIX = None -############################ Global Database Configuration ##################### - -DATABASE_ROUTERS = [ - 'openedx.core.lib.django_courseware_routers.StudentModuleHistoryExtendedRouter', -] - ############################ OAUTH2 Provider ################################### # 5 minute expiration time for JWT id tokens issued for external API requests. OAUTH_ID_TOKEN_EXPIRATION = 5 * 60 -EDX_DRF_EXTENSIONS = { - # Set this value to an empty dict in order to prevent automatically updating - # user data from values in (possibly stale) JWTs. - 'JWT_PAYLOAD_USER_ATTRIBUTE_MAPPING': {}, -} - -############## Settings for Studio Context Sensitive Help ############## - -HELP_TOKENS_INI_FILE = REPO_ROOT / "cms" / "envs" / "help_tokens.ini" - ############## DJANGO-USER-TASKS ############## # How long until database records about the outcome of a task and its artifacts get deleted? @@ -1441,9 +1026,6 @@ ############################# Persistent Grades #################################### -# Queue to use for updating persistent grades -RECALCULATE_GRADES_ROUTING_KEY = DEFAULT_PRIORITY_QUEUE - # .. setting_name: DEFAULT_GRADE_DESIGNATIONS # .. setting_default: ['A', 'B', 'C', 'D'] # .. setting_description: The default 'pass' grade cutoff designations to be used. The failure grade @@ -1534,10 +1116,6 @@ VIDEO_IMAGE_MAX_AGE = 31536000 -########################## VIDEO TRANSCRIPTS STORAGE ############################ -TRANSCRIPT_LANG_CACHE_TIMEOUT = 60 * 60 * 24 - - ##### shoppingcart Payment ##### PAYMENT_SUPPORT_EMAIL = 'billing@example.com' @@ -1582,19 +1160,7 @@ ############################ JWT ################################# -REGISTRATION_EXTRA_FIELDS = { - 'confirm_email': 'hidden', - 'level_of_education': 'optional', - 'gender': 'optional', - 'year_of_birth': 'optional', - 'mailing_address': 'optional', - 'goals': 'optional', - 'honor_code': 'required', - 'terms_of_service': 'hidden', - 'city': 'hidden', - 'country': 'hidden', - 'marketing_emails_opt_in': 'hidden', -} +REGISTRATION_EXTRA_FIELDS['marketing_emails_opt_in'] = 'hidden' EDXAPP_PARSE_KEYS = {} PARSE_KEYS = {} @@ -1675,18 +1241,7 @@ def _should_send_xblock_events(settings): return settings.ENABLE_SEND_XBLOCK_LIFECYCLE_EVENTS_OVER_BUS -# .. setting_name: EVENT_BUS_PRODUCER_CONFIG -# .. setting_default: all events disabled -# .. setting_description: Dictionary of event_types mapped to dictionaries of topic to topic-related configuration. -# Each topic configuration dictionary contains -# * `enabled`: a toggle denoting whether the event will be published to the topic. These should be annotated -# according to -# https://docs.openedx.org/projects/edx-toggles/en/latest/how_to/documenting_new_feature_toggles.html -# * `event_key_field` which is a period-delimited string path to event data field to use as event key. -# Note: The topic names should not include environment prefix as it will be dynamically added based on -# EVENT_BUS_TOPIC_PREFIX setting. - -EVENT_BUS_PRODUCER_CONFIG = { +EVENT_BUS_PRODUCER_CONFIG.update({ 'org.openedx.content_authoring.course.catalog_info.changed.v1': { 'course-catalog-info-changed': {'event_key_field': 'catalog_info.course_key', @@ -1714,29 +1269,7 @@ def _should_send_xblock_events(settings): 'course-authoring-xblock-lifecycle': {'event_key_field': 'xblock_info.usage_key', 'enabled': Derived(_should_send_xblock_events)}, }, - # LMS events. These have to be copied over here because lms.common adds some derived entries as well, - # and the derivation fails if the keys are missing. If we ever remove the import of lms.common, we can remove these. - 'org.openedx.learning.certificate.created.v1': { - 'learning-certificate-lifecycle': - {'event_key_field': 'certificate.course.course_key', 'enabled': False}, - }, - 'org.openedx.learning.certificate.revoked.v1': { - 'learning-certificate-lifecycle': - {'event_key_field': 'certificate.course.course_key', 'enabled': False}, - }, - "org.openedx.learning.course.passing.status.updated.v1": { - "learning-badges-lifecycle": { - "event_key_field": "course_passing_status.course.course_key", - "enabled": Derived(should_send_learning_badge_events), - }, - }, - "org.openedx.learning.ccx.course.passing.status.updated.v1": { - "learning-badges-lifecycle": { - "event_key_field": "course_passing_status.course.ccx_course_key", - "enabled": Derived(should_send_learning_badge_events), - }, - }, -} +}) ################### Authoring API ###################### diff --git a/cms/envs/production.py b/cms/envs/production.py index c6a0f090f39e..95f88ed23ad2 100644 --- a/cms/envs/production.py +++ b/cms/envs/production.py @@ -134,8 +134,6 @@ def get_env_setting(setting): if STATIC_ROOT_BASE: STATIC_ROOT = path(STATIC_ROOT_BASE) / 'studio' - WEBPACK_LOADER['DEFAULT']['STATS_FILE'] = STATIC_ROOT / "webpack-stats.json" - WEBPACK_LOADER['WORKERS']['STATS_FILE'] = STATIC_ROOT / "webpack-worker-stats.json" DATA_DIR = path(DATA_DIR) @@ -263,7 +261,7 @@ def get_env_setting(setting): # Then add alternate environment queues _YAML_ALTERNATE_WORKER_QUEUES = _YAML_TOKENS.get('ALTERNATE_WORKER_QUEUES', '').split() ALTERNATE_QUEUES = [ - DEFAULT_PRIORITY_QUEUE.replace(QUEUE_VARIANT, alternate + '.') + DEFAULT_PRIORITY_QUEUE.replace(SERVICE_VARIANT, alternate) for alternate in _YAML_ALTERNATE_WORKER_QUEUES ] diff --git a/cms/envs/test.py b/cms/envs/test.py index c8fcbe6b0413..b25c22dc7e40 100644 --- a/cms/envs/test.py +++ b/cms/envs/test.py @@ -14,6 +14,7 @@ import os +import tempfile from django.utils.translation import gettext_lazy from edx_django_utils.plugins import add_plugins @@ -38,7 +39,8 @@ COMMON_TEST_DATA_ROOT = COMMON_ROOT / "test" / "data" -WEBPACK_LOADER["DEFAULT"]["STATS_FILE"] = STATIC_ROOT / "webpack-stats.json" +COMPREHENSIVE_THEME_DIRS = [REPO_ROOT / "themes", REPO_ROOT / "common/test"] + WEBPACK_LOADER['DEFAULT']['LOADER_CLASS'] = 'webpack_loader.loader.FakeWebpackLoader' GITHUB_REPO_ROOT = TEST_ROOT / "data" diff --git a/lms/envs/common.py b/lms/envs/common.py index e157856ff082..0a8076e808ec 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -40,7 +40,6 @@ # and throws spurious errors. Therefore, we disable invalid-name checking. # pylint: disable=invalid-name -import importlib.util import os from corsheaders.defaults import default_headers as corsheaders_default_headers @@ -65,7 +64,6 @@ from openedx.core.lib.derived import Derived from openedx.envs.common import * # pylint: disable=wildcard-import -from lms.djangoapps.lms_xblock.mixin import LmsBlockMixin from openedx.core.lib.features_setting_proxy import FeaturesProxy # A proxy for feature flags stored in the settings namespace @@ -735,28 +733,16 @@ RETRY_CALENDAR_SYNC_EMAIL_MAX_ATTEMPTS = 5 ############################# SET PATH INFORMATION ############################# + PROJECT_ROOT = path(__file__).abspath().dirname().dirname() # /edx-platform/lms -REPO_ROOT = PROJECT_ROOT.dirname() -COMMON_ROOT = REPO_ROOT / "common" -OPENEDX_ROOT = REPO_ROOT / "openedx" -XMODULE_ROOT = REPO_ROOT / "xmodule" -ENV_ROOT = REPO_ROOT.dirname() # virtualenv dir /edx-platform is in -COURSES_ROOT = ENV_ROOT / "data" NODE_MODULES_ROOT = REPO_ROOT / "node_modules" -DATA_DIR = COURSES_ROOT - -# For geolocation ip database -GEOIP_PATH = REPO_ROOT / "common/static/data/geoip/GeoLite2-Country.mmdb" # Where to look for a status message STATUS_MESSAGE_PATH = ENV_ROOT / "status_message.json" ############################ Global Database Configuration ##################### -DATABASE_ROUTERS = [ - 'openedx.core.lib.django_courseware_routers.StudentModuleHistoryExtendedRouter', - 'edx_django_utils.db.read_replica.ReadReplicaRouter', -] +DATABASE_ROUTERS.append('edx_django_utils.db.read_replica.ReadReplicaRouter') ################################## DJANGO OAUTH TOOLKIT ####################################### @@ -812,27 +798,11 @@ TPA_AUTOMATIC_LOGOUT_ENABLED = False ################################## TEMPLATE CONFIGURATION ##################################### -# Mako templating -import tempfile # pylint: disable=wrong-import-position,wrong-import-order -MAKO_MODULE_DIR = os.path.join(tempfile.gettempdir(), 'mako_lms') -MAKO_TEMPLATE_DIRS_BASE = [ - PROJECT_ROOT / 'templates', - COMMON_ROOT / 'templates', - XMODULE_ROOT / 'capa' / 'templates', - COMMON_ROOT / 'djangoapps' / 'pipeline_mako' / 'templates', - OPENEDX_ROOT / 'core' / 'djangoapps' / 'cors_csrf' / 'templates', - OPENEDX_ROOT / 'core' / 'djangoapps' / 'dark_lang' / 'templates', - OPENEDX_ROOT / 'core' / 'lib' / 'license' / 'templates', - OPENEDX_ROOT / 'features' / 'course_experience' / 'templates', -] -CONTEXT_PROCESSORS = [ - 'django.template.context_processors.request', - 'django.template.context_processors.static', - 'django.template.context_processors.i18n', - 'django.contrib.auth.context_processors.auth', # this is required for admin - 'django.template.context_processors.csrf', +MAKO_TEMPLATE_DIRS_BASE = lms_mako_template_dirs_base +CONTEXT_PROCESSORS.remove('django.contrib.messages.context_processors.messages') +CONTEXT_PROCESSORS[5:5] = [ # Added for django-wiki 'django.template.context_processors.media', 'django.template.context_processors.tz', @@ -844,11 +814,8 @@ # Timezone processor (sends language and time_zone preference) 'lms.djangoapps.courseware.context_processor.user_timezone_locale_prefs', - - # Online contextual help - 'help_tokens.context_processor', - 'openedx.core.djangoapps.site_configuration.context_processors.configuration_context', - +] +CONTEXT_PROCESSORS += [ # Mobile App processor (Detects if request is from the mobile app) 'lms.djangoapps.mobile_api.context_processor.is_from_mobile_app', @@ -856,61 +823,10 @@ 'openedx.features.survey_report.context_processors.admin_extra_context', ] -# Django templating -TEMPLATES = [ - { - 'NAME': 'django', - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - # Don't look for template source files inside installed applications. - 'APP_DIRS': False, - # Instead, look for template source files in these dirs. - 'DIRS': [ - PROJECT_ROOT / "templates", - COMMON_ROOT / 'templates', - XMODULE_ROOT / 'capa' / 'templates', - COMMON_ROOT / 'djangoapps' / 'pipeline_mako' / 'templates', - COMMON_ROOT / 'static', # required to statically include common Underscore templates - ], - # Options specific to this backend. - 'OPTIONS': { - 'loaders': [ - # We have to use mako-aware template loaders to be able to include - # mako templates inside django templates (such as main_django.html). - 'openedx.core.djangoapps.theming.template_loaders.ThemeTemplateLoader', - 'common.djangoapps.edxmako.makoloader.MakoFilesystemLoader', - 'common.djangoapps.edxmako.makoloader.MakoAppDirectoriesLoader', - ], - 'context_processors': CONTEXT_PROCESSORS, - # Change 'debug' in your environment settings files - not here. - 'debug': False - } - }, - { - 'NAME': 'mako', - 'BACKEND': 'common.djangoapps.edxmako.backend.Mako', - # Don't look for template source files inside installed applications. - 'APP_DIRS': False, - # Instead, look for template source files in these dirs. - 'DIRS': Derived(make_mako_template_dirs), - # Options specific to this backend. - 'OPTIONS': { - 'context_processors': CONTEXT_PROCESSORS, - # Change 'debug' in your environment settings files - not here. - 'debug': False, - } - }, -] -DEFAULT_TEMPLATE_ENGINE = TEMPLATES[0] -DEFAULT_TEMPLATE_ENGINE_DIRS = DEFAULT_TEMPLATE_ENGINE['DIRS'][:] +DEFAULT_TEMPLATE_ENGINE_DIRS = Derived(lambda settings: settings.TEMPLATES[0]['DIRS'][:]) ############################################################################################### -AUTHENTICATION_BACKENDS = [ - 'rules.permissions.ObjectPermissionBackend', - 'django.contrib.auth.backends.AllowAllUsersModelBackend', - 'bridgekeeper.backends.RulePermissionBackend', -] - STUDENT_FILEUPLOAD_MAX_SIZE = 4 * 1000 * 1000 # 4 MB MAX_FILEUPLOADS_PER_INPUT = 20 @@ -951,15 +867,6 @@ ENABLE_MULTICOURSE = False # set to False to disable multicourse display (see lib.util.views.edXhome) -# .. toggle_name: WIKI_ENABLED -# .. toggle_implementation: DjangoSetting -# .. toggle_default: True -# .. toggle_description: This setting allows us to have a collaborative tool to contribute or -# modify content of course related materials. -# .. toggle_use_cases: open_edx -# .. toggle_creation_date: 2012-07-13 -WIKI_ENABLED = True - ### # IP addresses that are allowed to reload the course, etc. @@ -969,66 +876,9 @@ ############################## EVENT TRACKING ################################# LMS_SEGMENT_KEY = None -# FIXME: Should we be doing this truncation? -TRACK_MAX_EVENT = 50000 - DEBUG_TRACK_LOG = False -TRACKING_BACKENDS = { - 'logger': { - 'ENGINE': 'common.djangoapps.track.backends.logger.LoggerBackend', - 'OPTIONS': { - 'name': 'tracking' - } - } -} - -# We're already logging events, and we don't want to capture user -# names/passwords. Heartbeat events are likely not interesting. -TRACKING_IGNORE_URL_PATTERNS = [r'^/event', r'^/login', r'^/heartbeat', r'^/segmentio/event', r'^/performance'] - -EVENT_TRACKING_ENABLED = True -EVENT_TRACKING_BACKENDS = { - 'tracking_logs': { - 'ENGINE': 'eventtracking.backends.routing.RoutingBackend', - 'OPTIONS': { - 'backends': { - 'logger': { - 'ENGINE': 'eventtracking.backends.logger.LoggerBackend', - 'OPTIONS': { - 'name': 'tracking', - 'max_event_size': TRACK_MAX_EVENT, - } - } - }, - 'processors': [ - {'ENGINE': 'common.djangoapps.track.shim.LegacyFieldMappingProcessor'}, - {'ENGINE': 'common.djangoapps.track.shim.PrefixedEventProcessor'} - ] - } - }, - 'segmentio': { - 'ENGINE': 'eventtracking.backends.routing.RoutingBackend', - 'OPTIONS': { - 'backends': { - 'segment': {'ENGINE': 'eventtracking.backends.segment.SegmentBackend'} - }, - 'processors': [ - { - 'ENGINE': 'eventtracking.processors.whitelist.NameWhitelistProcessor', - 'OPTIONS': { - 'whitelist': [] - } - }, - { - 'ENGINE': 'common.djangoapps.track.shim.GoogleAnalyticsProcessor' - } - ] - } - } -} -EVENT_TRACKING_PROCESSORS = [] -EVENT_TRACKING_SEGMENTIO_EMIT_WHITELIST = [] +TRACKING_IGNORE_URL_PATTERNS += [r'^/segmentio/event', r'^/performance'] TRACKING_SEGMENTIO_WEBHOOK_SECRET = None TRACKING_SEGMENTIO_ALLOWED_TYPES = ['track'] @@ -1039,7 +889,6 @@ } ######################## GOOGLE ANALYTICS ########################### -GOOGLE_ANALYTICS_ACCOUNT = None GOOGLE_SITE_VERIFICATION_ID = None GOOGLE_ANALYTICS_LINKEDIN = None GOOGLE_ANALYTICS_TRACKING_ID = None @@ -1054,98 +903,12 @@ ######################## subdomain specific settings ########################### COURSE_LISTINGS = {} -############# XBlock Configuration ########## - -# Import after sys.path fixup -from xmodule.modulestore.edit_info import EditInfoMixin # lint-amnesty, pylint: disable=wrong-import-order, wrong-import-position -from xmodule.modulestore.inheritance import InheritanceMixin # lint-amnesty, pylint: disable=wrong-import-order, wrong-import-position -from xmodule.x_module import XModuleMixin # lint-amnesty, pylint: disable=wrong-import-order, wrong-import-position - -# These are the Mixins that will be added to every Blocklike upon instantiation. -# DO NOT EXPAND THIS LIST!! We want it eventually to be EMPTY. Why? Because dynamically adding functions/behaviors to -# objects at runtime is confusing for both developers and static tooling (pylint/mypy). Instead... -# - to add special Blocklike behaviors just for your site: override `XBLOCK_EXTRA_MIXINS` with your own XBlockMixins. -# - to add new functionality to all Blocklikes: add it to the base Blocklike class in the core openedx/XBlock repo. -XBLOCK_MIXINS = ( - # TODO: For each of these, either - # (a) merge their functionality into the base Blocklike class, or - # (b) refactor their functionality out of the Blocklike objects and into the edx-platform block runtimes. - LmsBlockMixin, - InheritanceMixin, - XModuleMixin, - EditInfoMixin, -) - ############# ModuleStore Configuration ########## -MODULESTORE_BRANCH = 'published-only' - -DOC_STORE_CONFIG = { - 'db': 'edxapp', - 'host': 'localhost', - 'replicaSet': '', - 'password': 'password', - 'port': 27017, - 'user': 'edxapp', - 'collection': 'modulestore', - 'ssl': False, - # https://api.mongodb.com/python/2.9.1/api/pymongo/mongo_client.html#module-pymongo.mongo_client - # default is never timeout while the connection is open, - #this means it needs to explicitly close raising pymongo.errors.NetworkTimeout - 'socketTimeoutMS': 6000, - 'connectTimeoutMS': 2000, # default is 20000, I believe raises pymongo.errors.ConnectionFailure - # Not setting waitQueueTimeoutMS and waitQueueMultiple since pymongo defaults to nobody being allowed to wait - 'auth_source': None, - 'read_preference': 'SECONDARY_PREFERRED' -} - -CONTENTSTORE = { - 'ENGINE': 'xmodule.contentstore.mongo.MongoContentStore', - # connection strings are duplicated temporarily for - # backward compatibility - 'OPTIONS': { - 'db': 'edxapp', - 'host': 'localhost', - 'password': 'password', - 'port': 27017, - 'user': 'edxapp', - 'ssl': False, - 'auth_source': None - }, - 'ADDITIONAL_OPTIONS': {}, - 'DOC_STORE_CONFIG': DOC_STORE_CONFIG -} +CONTENTSTORE['DOC_STORE_CONFIG']['password'] = 'password' +CONTENTSTORE['DOC_STORE_CONFIG']['read_preference'] = 'SECONDARY_PREFERRED' -MODULESTORE = { - 'default': { - 'ENGINE': 'xmodule.modulestore.mixed.MixedModuleStore', - 'OPTIONS': { - 'mappings': {}, - 'stores': [ - { - 'NAME': 'split', - 'ENGINE': 'xmodule.modulestore.split_mongo.split_draft.DraftVersioningModuleStore', - 'DOC_STORE_CONFIG': DOC_STORE_CONFIG, - 'OPTIONS': { - 'default_class': 'xmodule.hidden_block.HiddenBlock', - 'fs_root': Derived(lambda settings: settings.DATA_DIR), - 'render_template': 'common.djangoapps.edxmako.shortcuts.render_to_string', - } - }, - { - 'NAME': 'draft', - 'ENGINE': 'xmodule.modulestore.mongo.DraftMongoModuleStore', - 'DOC_STORE_CONFIG': DOC_STORE_CONFIG, - 'OPTIONS': { - 'default_class': 'xmodule.hidden_block.HiddenBlock', - 'fs_root': Derived(lambda settings: settings.DATA_DIR), - 'render_template': 'common.djangoapps.edxmako.shortcuts.render_to_string', - } - } - ] - } - } -} +MODULESTORE_BRANCH = 'published-only' HOSTNAME_MODULESTORE_DEFAULT_MAPPINGS = {} MONGODB_LOG = {} @@ -1200,9 +963,6 @@ STUDIO_NAME = 'Studio' STUDIO_SHORT_NAME = 'Studio' -# Site info -ROOT_URLCONF = 'lms.urls' -# NOTE: Please set ALLOWED_HOSTS to some sane value, as we do not allow the default '*' # Platform Email EMAIL_FILE_PATH = Derived(lambda settings: path(settings.DATA_DIR) / "emails" / "lms") DEFAULT_FROM_EMAIL = 'registration@example.com' @@ -1225,17 +985,7 @@ STATIC_URL = '/static/' STATIC_ROOT = os.environ.get('STATIC_ROOT_LMS', ENV_ROOT / "staticfiles") -STATICFILES_DIRS = [ - COMMON_ROOT / "static", - PROJECT_ROOT / "static", - NODE_MODULES_ROOT / "@edx", - # Temporarily adding the following static path as we are migrating the built-in blocks' Sass to vanilla CSS. - # Once all of the built-in blocks are extracted from edx-platform, we can remove this static path. - # Relevant ticket: https://github.com/openedx/edx-platform/issues/35300 - XMODULE_ROOT / "static", -] - -STATICI18N_ROOT = PROJECT_ROOT / "static" +STATICFILES_DIRS.insert(2, NODE_MODULES_ROOT / "@edx") # Guidelines for translators TRANSLATORS_GUIDE = 'https://docs.openedx.org/en/latest/translators/index.html' @@ -1494,27 +1244,7 @@ ############################### PIPELINE ####################################### -PIPELINE = { - 'PIPELINE_ENABLED': True, - 'CSS_COMPRESSOR': None, - 'JS_COMPRESSOR': 'pipeline.compressors.uglifyjs.UglifyJSCompressor', - # Don't wrap JavaScript as there is code that depends upon updating the global namespace - 'DISABLE_WRAPPER': True, - # Specify the UglifyJS binary to use - 'UGLIFYJS_BINARY': 'node_modules/.bin/uglifyjs', -} - -STATICFILES_STORAGE_KWARGS = {} - -# List of finder classes that know how to find static files in various locations. -# Note: the pipeline finder is included to be able to discover optimized files -STATICFILES_FINDERS = [ - 'openedx.core.djangoapps.theming.finders.ThemeFilesFinder', - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', - 'openedx.core.lib.xblock_pipeline.finder.XBlockPipelineFinder', - 'pipeline.finders.PipelineFinder', -] +PIPELINE['JS_COMPRESSOR'] = 'pipeline.compressors.uglifyjs.UglifyJSCompressor' from openedx.core.lib.rooted_paths import rooted_glob # pylint: disable=wrong-import-position @@ -1871,37 +1601,8 @@ } } - -STATICFILES_IGNORE_PATTERNS = ( - "*.py", - "*.pyc", - - # It would be nice if we could do, for example, "**/*.scss", - # but these strings get passed down to the `fnmatch` module, - # which doesn't support that. :( - # http://docs.python.org/2/library/fnmatch.html - "sass/*.scss", - "sass/*/*.scss", - "sass/*/*/*.scss", - "sass/*/*/*/*.scss", - - # Ignore tests - "spec", - "spec_helpers", - - # Symlinks used by js-test-tool - "xmodule_js", -) - - ################################# DJANGO-REQUIRE ############################### -# The name of a build profile to use for your project, relative to REQUIRE_BASE_URL. -# A sensible value would be 'app.build.js'. Leave blank to use the built-in default build profile. -# Set to False to disable running the default profile (e.g. if only using it to build Standalone -# Modules) -REQUIRE_BUILD_PROFILE = "lms/js/build.js" - # The name of the require.js script used by your project, relative to REQUIRE_BASE_URL. REQUIRE_JS = "common/js/vendor/require.js" @@ -1934,18 +1635,9 @@ 'hls': 'common/js/vendor/hls.js' } -########################## DJANGO WEBPACK LOADER ############################## +############################ SERVICE_VARIANT ################################## -WEBPACK_LOADER = { - 'DEFAULT': { - 'BUNDLE_DIR_NAME': 'bundles/', - 'STATS_FILE': os.path.join(STATIC_ROOT, 'webpack-stats.json'), - }, - 'WORKERS': { - 'BUNDLE_DIR_NAME': 'bundles/', - 'STATS_FILE': os.path.join(STATIC_ROOT, 'webpack-worker-stats.json') - } -} +SERVICE_VARIANT = 'lms' ################################# CELERY ###################################### @@ -1961,28 +1653,10 @@ # These packages are added in addition to those added by CELERY_IMPORTS. CELERY_EXTRA_IMPORTS = [] -# SERVICE_VARIANT specifies name of the variant used, which decides what JSON -# configuration files are read during startup. -SERVICE_VARIANT = os.environ.get('SERVICE_VARIANT', "lms") - -# CONFIG_PREFIX specifies the prefix of the JSON configuration files, -# based on the service variant. If no variant is use, don't use a -# prefix. -CONFIG_PREFIX = SERVICE_VARIANT + "." if SERVICE_VARIANT else "" - -# Queues configuration - # Name the exchange and queues w.r.t the SERVICE_VARIANT -QUEUE_VARIANT = CONFIG_PREFIX.lower() - -CELERY_DEFAULT_EXCHANGE = f'edx.{QUEUE_VARIANT}core' - -HIGH_PRIORITY_QUEUE = f'edx.{QUEUE_VARIANT}core.high' -DEFAULT_PRIORITY_QUEUE = f'edx.{QUEUE_VARIANT}core.default' -HIGH_MEM_QUEUE = f'edx.{QUEUE_VARIANT}core.high_mem' - -CELERY_DEFAULT_QUEUE = DEFAULT_PRIORITY_QUEUE -CELERY_DEFAULT_ROUTING_KEY = DEFAULT_PRIORITY_QUEUE +HIGH_PRIORITY_QUEUE = f'edx.{SERVICE_VARIANT}.core.high' +DEFAULT_PRIORITY_QUEUE = f'edx.{SERVICE_VARIANT}.core.default' +HIGH_MEM_QUEUE = f'edx.{SERVICE_VARIANT}.core.high_mem' CELERY_QUEUES = { HIGH_PRIORITY_QUEUE: {}, @@ -1998,10 +1672,6 @@ BROKER_USE_SSL = False -############################## HEARTBEAT ###################################### - -HEARTBEAT_CELERY_ROUTING_KEY = HIGH_PRIORITY_QUEUE - ################################ Bulk Email ################################### # Initial delay used for retrying tasks. Additional retries use @@ -2386,6 +2056,24 @@ "openedx_learning.apps.authoring.sections", ] +# Add LMS specific optional apps +OPTIONAL_APPS += [ + # Channel Integrations Apps + ('channel_integrations.integrated_channel', None), + ('channel_integrations.degreed2', None), + ('channel_integrations.sap_success_factors', None), + ('channel_integrations.cornerstone', None), + ('channel_integrations.xapi', None), + ('channel_integrations.blackboard', None), + ('channel_integrations.canvas', None), + ('channel_integrations.moodle', None), + + # Required by the Enterprise App + ('django_object_actions', None), # https://github.com/crccheck/django-object-actions +] + +add_optional_apps(OPTIONAL_APPS, INSTALLED_APPS) + ######################### Django Rest Framework ######################## SWAGGER_SETTINGS = { @@ -2395,7 +2083,7 @@ ######################### MARKETING SITE ############################### -MKTG_URL_LINK_MAP = { +MKTG_URL_LINK_MAP.update({ 'ABOUT': 'about', 'CONTACT': 'contact', 'FAQ': 'help', @@ -2412,15 +2100,11 @@ # Verified Certificates 'WHAT_IS_VERIFIED_CERT': 'verified-certificate', -} +}) STATIC_TEMPLATE_VIEW_DEFAULT_FILE_EXTENSION = 'html' SEND_ACTIVATION_EMAIL_URL = '' -ACTIVATION_EMAIL_SUPPORT_LINK = Derived(lambda settings: settings.SUPPORT_SITE_LINK) -ID_VERIFICATION_SUPPORT_LINK = Derived(lambda settings: settings.SUPPORT_SITE_LINK) -LOGIN_ISSUE_SUPPORT_LINK = Derived(lambda settings: settings.SUPPORT_SITE_LINK) -PASSWORD_RESET_SUPPORT_LINK = Derived(lambda settings: settings.SUPPORT_SITE_LINK) # .. setting_name: SECURITY_PAGE_URL # .. setting_default: None @@ -2434,19 +2118,13 @@ ############################# SOCIAL MEDIA SHARING ############################# # Social Media Sharing on Student Dashboard -SOCIAL_SHARING_SETTINGS = { - # Note: Ensure 'CUSTOM_COURSE_URLS' has a matching value in cms/envs/common.py - 'CUSTOM_COURSE_URLS': False, - 'DASHBOARD_FACEBOOK': False, +SOCIAL_SHARING_SETTINGS.update({ 'FACEBOOK_BRAND': None, - 'CERTIFICATE_FACEBOOK': False, 'CERTIFICATE_FACEBOOK_TEXT': None, - 'CERTIFICATE_TWITTER': False, + 'TWITTER_BRAND': None, 'CERTIFICATE_TWITTER_TEXT': None, - 'DASHBOARD_TWITTER': False, 'DASHBOARD_TWITTER_TEXT': None, - 'TWITTER_BRAND': None -} +}) ################# Social Media Footer Links ####################### # The names list controls the order of social media @@ -2575,30 +2253,6 @@ ###################### Registration ################################## -# .. setting_name: REGISTRATION_EXTRA_FIELDS -# .. setting_default: {'confirm_email': 'hidden', 'level_of_education': 'optional', 'gender': 'optional', -# 'year_of_birth': 'optional', 'mailing_address': 'optional', 'goals': 'optional', 'honor_code': 'required', -# 'terms_of_service': 'hidden', 'city': 'hidden', 'country': 'hidden'} -# .. setting_description: The signup form may contain extra fields that are presented to every user. For every field, we -# can specifiy whether it should be "required": to display the field, and make it mandatory; "optional": to display -# the optional field as part of a toggled input field list; "optional-exposed": to display the optional fields among -# the required fields, and make it non-mandatory; "hidden": to not display the field. -# When the terms of service are not visible and agreement to the honor code is required (the default), the signup page -# includes a paragraph that links to the honor code page (defined my MKTG_URLS["HONOR"]). This page might not be -# available for all Open edX platforms. In such cases, the "honor_code" registration field should be "hidden". -REGISTRATION_EXTRA_FIELDS = { - 'confirm_email': 'hidden', - 'level_of_education': 'optional', - 'gender': 'optional', - 'year_of_birth': 'optional', - 'mailing_address': 'optional', - 'goals': 'optional', - 'honor_code': 'required', - 'terms_of_service': 'hidden', - 'city': 'hidden', - 'country': 'hidden', -} - REGISTRATION_FIELD_ORDER = [ "name", "first_name", @@ -2634,8 +2288,6 @@ # the ones that contain information other than grades. GRADES_DOWNLOAD_ROUTING_KEY = Derived(lambda settings: settings.HIGH_MEM_QUEUE) -RECALCULATE_GRADES_ROUTING_KEY = 'edx.lms.core.default' - ############################ ORA 2 ############################################ ORA_WORKFLOW_UPDATE_ROUTING_KEY = "edx.lms.core.ora_workflow_update" @@ -2655,69 +2307,6 @@ ##### LMS DEADLINE DISPLAY TIME_ZONE ####### TIME_ZONE_DISPLAYED_FOR_DEADLINES = 'UTC' -########################## VIDEO TRANSCRIPTS STORAGE ############################ - -### Apps only installed in some instances -# The order of INSTALLED_APPS matters, so this tuple is the app name and the item in INSTALLED_APPS -# that this app should be inserted *before*. A None here means it should be appended to the list. -OPTIONAL_APPS = [ - ('problem_builder', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - ('edx_sga', None), - - # edx-ora2 - ('submissions', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - ('openassessment', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - ('openassessment.assessment', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - ('openassessment.fileupload', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - ('openassessment.staffgrader', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - ('openassessment.workflow', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - ('openassessment.xblock', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - - # edxval - ('edxval', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), - - # Enterprise Apps (http://github.com/openedx/edx-enterprise) - ('enterprise', None), - ('consent', None), - ('integrated_channels.integrated_channel', None), - ('integrated_channels.degreed', None), - ('integrated_channels.degreed2', None), - ('integrated_channels.sap_success_factors', None), - ('integrated_channels.cornerstone', None), - ('integrated_channels.xapi', None), - ('integrated_channels.blackboard', None), - ('integrated_channels.canvas', None), - ('integrated_channels.moodle', None), - - # Channel Integrations Apps - ('channel_integrations.integrated_channel', None), - ('channel_integrations.degreed2', None), - ('channel_integrations.sap_success_factors', None), - ('channel_integrations.cornerstone', None), - ('channel_integrations.xapi', None), - ('channel_integrations.blackboard', None), - ('channel_integrations.canvas', None), - ('channel_integrations.moodle', None), - - # Required by the Enterprise App - ('django_object_actions', None), # https://github.com/crccheck/django-object-actions -] - -for app_name, insert_before in OPTIONAL_APPS: - # First attempt to only find the module rather than actually importing it, - # to avoid circular references - only try to import if it can't be found - # by find_spec, which doesn't work with import hooks - if importlib.util.find_spec(app_name) is None: - try: - __import__(app_name) - except ImportError: - continue - - try: - INSTALLED_APPS.insert(INSTALLED_APPS.index(insert_before), app_name) - except (IndexError, ValueError): - INSTALLED_APPS.append(app_name) - ### Analytics API ANALYTICS_API_KEY = "" ANALYTICS_API_URL = "http://localhost:18100" @@ -2966,13 +2555,8 @@ ################################ Settings for JWTs ################################ -EDX_DRF_EXTENSIONS = { - # Set this value to an empty dict in order to prevent automatically updating - # user data from values in (possibly stale) JWTs. - 'JWT_PAYLOAD_USER_ATTRIBUTE_MAPPING': {}, - # Allows JWT authentication to find the LMS user id for verification - 'VERIFY_LMS_USER_ID_PROPERTY_NAME': 'id', -} +# Allows JWT authentication to find the LMS user id for verification +EDX_DRF_EXTENSIONS['VERIFY_LMS_USER_ID_PROPERTY_NAME'] = 'id' ################################ Settings for rss_proxy ################################ @@ -3030,10 +2614,6 @@ # Queue to use for award program certificates PROGRAM_CERTIFICATES_ROUTING_KEY = Derived(lambda settings: settings.DEFAULT_PRIORITY_QUEUE) -############## Settings for LMS Context Sensitive Help ############## - -HELP_TOKENS_INI_FILE = REPO_ROOT / "lms" / "envs" / "help_tokens.ini" - ############## OPEN EDX ENTERPRISE SERVICE CONFIGURATION ###################### # The Open edX Enterprise service is currently hosted via the LMS container/process. # However, for all intents and purposes this service is treated as a standalone IDA. @@ -3145,9 +2725,6 @@ DATA_CONSENT_SHARE_CACHE_TIMEOUT = 8 * 60 * 60 # 8 hours -TRANSCRIPT_LANG_CACHE_TIMEOUT = 60 * 60 * 24 # 24 hours - - ############## Settings for the Discovery App ###################### COURSES_API_CACHE_TIMEOUT = 3600 # Value is in seconds @@ -3317,9 +2894,6 @@ from openedx.core.djangoapps.ace_common.settings import common as ace_common_settings ACE_ROUTING_KEY = ace_common_settings.ACE_ROUTING_KEY -############### Settings swift ##################################### -SWIFT_USE_TEMP_URLS = None - ############### Settings for facebook ############################## FACEBOOK_APP_ID = None FACEBOOK_APP_SECRET = None @@ -3336,13 +2910,6 @@ INSTALLED_APPS.extend(get_plugin_apps(ProjectType.LMS)) add_plugins(__name__, ProjectType.LMS, SettingsType.COMMON) -############### Settings for video pipeline ################## -VIDEO_UPLOAD_PIPELINE = { - 'VEM_S3_BUCKET': '', - 'BUCKET': '', - 'ROOT_PATH': '', -} - PROCTORED_EXAM_VIEWABLE_PAST_DUE = False ######################### rate limit for yt_video_metadata api ############## @@ -3499,23 +3066,12 @@ DISABLED_ORGS_FOR_PROGRAM_NUDGE = [] +#### Event bus producing #### + def _should_send_certificate_events(settings): return settings.SEND_LEARNING_CERTIFICATE_LIFECYCLE_EVENTS_TO_BUS - -#### Event bus producing #### - -# .. setting_name: EVENT_BUS_PRODUCER_CONFIG -# .. setting_default: all events disabled -# .. setting_description: Dictionary of event_types mapped to dictionaries of topic to topic-related configuration. -# Each topic configuration dictionary contains -# * `enabled`: a toggle denoting whether the event will be published to the topic. These should be annotated -# according to -# https://docs.openedx.org/projects/edx-toggles/en/latest/how_to/documenting_new_feature_toggles.html -# * `event_key_field` which is a period-delimited string path to event data field to use as event key. -# Note: The topic names should not include environment prefix as it will be dynamically added based on -# EVENT_BUS_TOPIC_PREFIX setting. -EVENT_BUS_PRODUCER_CONFIG = { +EVENT_BUS_PRODUCER_CONFIG.update({ 'org.openedx.learning.certificate.created.v1': { 'learning-certificate-lifecycle': {'event_key_field': 'certificate.course.course_key', 'enabled': Derived(_should_send_certificate_events)}, @@ -3566,40 +3122,13 @@ def _should_send_certificate_events(settings): 'learner-credit-course-enrollment-lifecycle': {'event_key_field': 'learner_credit_course_enrollment.uuid', 'enabled': False}, }, - # CMS events. These have to be copied over here because cms.common adds some derived entries as well, - # and the derivation fails if the keys are missing. If we ever fully decouple the lms and cms settings, - # we can remove these. - 'org.openedx.content_authoring.xblock.published.v1': { - 'course-authoring-xblock-lifecycle': - {'event_key_field': 'xblock_info.usage_key', 'enabled': False}, - }, - 'org.openedx.content_authoring.xblock.deleted.v1': { - 'course-authoring-xblock-lifecycle': - {'event_key_field': 'xblock_info.usage_key', 'enabled': False}, - }, - 'org.openedx.content_authoring.xblock.duplicated.v1': { - 'course-authoring-xblock-lifecycle': - {'event_key_field': 'xblock_info.usage_key', 'enabled': False}, - }, - "org.openedx.learning.course.passing.status.updated.v1": { - "learning-badges-lifecycle": { - "event_key_field": "course_passing_status.course.course_key", - "enabled": Derived(should_send_learning_badge_events), - }, - }, - "org.openedx.learning.ccx.course.passing.status.updated.v1": { - "learning-badges-lifecycle": { - "event_key_field": "course_passing_status.course.ccx_course_key", - "enabled": Derived(should_send_learning_badge_events), - }, - }, "org.openedx.learning.external_grader.score.submitted.v1": { "learning-external-grader-score-lifecycle": { "event_key_field": "score.submission_id", "enabled": False }, }, -} +}) #### Survey Report #### # .. toggle_name: SURVEY_REPORT_ENABLE diff --git a/lms/envs/production.py b/lms/envs/production.py index 34899a126e5a..63246821d954 100644 --- a/lms/envs/production.py +++ b/lms/envs/production.py @@ -122,9 +122,6 @@ def get_env_setting(setting): # collected if STATIC_ROOT_BASE: STATIC_ROOT = path(STATIC_ROOT_BASE) - WEBPACK_LOADER['DEFAULT']['STATS_FILE'] = STATIC_ROOT / "webpack-stats.json" - WEBPACK_LOADER['WORKERS']['STATS_FILE'] = STATIC_ROOT / "webpack-worker-stats.json" - # STATIC_URL_BASE specifies the base url to use for static files if STATIC_URL_BASE: @@ -165,7 +162,7 @@ def get_env_setting(setting): # Then add alternate environment queues _YAML_ALTERNATE_WORKER_QUEUES = _YAML_TOKENS.get('ALTERNATE_WORKER_QUEUES', '').split() ALTERNATE_QUEUES = [ - DEFAULT_PRIORITY_QUEUE.replace(QUEUE_VARIANT, alternate + '.') + DEFAULT_PRIORITY_QUEUE.replace(SERVICE_VARIANT, alternate) for alternate in _YAML_ALTERNATE_WORKER_QUEUES ] diff --git a/lms/envs/test.py b/lms/envs/test.py index ee51d1444beb..e396549501c1 100644 --- a/lms/envs/test.py +++ b/lms/envs/test.py @@ -53,7 +53,8 @@ COMMON_TEST_DATA_ROOT = COMMON_ROOT / "test" / "data" -WEBPACK_LOADER['DEFAULT']['STATS_FILE'] = STATIC_ROOT / "webpack-stats.json" +COMPREHENSIVE_THEME_DIRS = [REPO_ROOT / "themes", REPO_ROOT / "common/test"] + WEBPACK_LOADER['DEFAULT']['LOADER_CLASS'] = 'webpack_loader.loader.FakeWebpackLoader' STATUS_MESSAGE_PATH = TEST_ROOT / "status_message.json" diff --git a/lms/wsgi_apache_lms.py b/lms/wsgi_apache_lms.py index d1c6013e2bfc..be74f3dc2580 100644 --- a/lms/wsgi_apache_lms.py +++ b/lms/wsgi_apache_lms.py @@ -12,7 +12,6 @@ import os # lint-amnesty, pylint: disable=wrong-import-order, wrong-import-position os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.envs.aws") -os.environ.setdefault("SERVICE_VARIANT", "lms") # This application object is used by the development server # as well as any WSGI server configured to use this file. diff --git a/manage.py b/manage.py index cc7c78d032df..90e87b6b1dcc 100755 --- a/manage.py +++ b/manage.py @@ -44,7 +44,6 @@ def parse_args(): help_string=lms.format_help(), settings_base='lms/envs', default_settings='lms.envs.devstack', - service_variant='lms', ) cms = subparsers.add_parser( @@ -62,16 +61,15 @@ def parse_args(): help_string=cms.format_help(), settings_base='cms/envs', default_settings='cms.envs.devstack', - service_variant='cms', ) - edx_args, django_args = parser.parse_known_args() + known_args, remaining_args = parser.parse_known_args() - if edx_args.help: + if known_args.help: print("edX:") - print(edx_args.help_string) + print(known_args.help_string) - return edx_args, django_args + return known_args, remaining_args if __name__ == "__main__": @@ -84,7 +82,6 @@ def parse_args(): os.environ["DJANGO_SETTINGS_MODULE"] = edx_args_base + os.environ["EDX_PLATFORM_SETTINGS"] os.environ.setdefault("DJANGO_SETTINGS_MODULE", edx_args.default_settings) - os.environ.setdefault("SERVICE_VARIANT", edx_args.service_variant) if edx_args.help: print("Django:") diff --git a/openedx/core/djangoapps/content/course_overviews/management/commands/simulate_publish.py b/openedx/core/djangoapps/content/course_overviews/management/commands/simulate_publish.py index 6c66831201f3..efa36e7e227f 100644 --- a/openedx/core/djangoapps/content/course_overviews/management/commands/simulate_publish.py +++ b/openedx/core/djangoapps/content/course_overviews/management/commands/simulate_publish.py @@ -15,12 +15,12 @@ import copy import logging -import os import sys import textwrap import time from django.core.management.base import BaseCommand, CommandError +from django.conf import settings from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey from openedx.core.djangoapps.content.course_overviews.models import SimulateCoursePublishConfig @@ -189,14 +189,14 @@ def handle(self, *args, **options): options['delay'] ) - if os.environ.get('SERVICE_VARIANT', 'cms').startswith('lms'): + if settings.SERVICE_VARIANT == 'lms': if options['force_lms']: log.info("Forcing simulate_publish to run in LMS process.") else: log.fatal( # lint-amnesty, pylint: disable=logging-not-lazy "simulate_publish should be run as a CMS (Studio) " + "command, not %s (override with --force-lms).", - os.environ.get('SERVICE_VARIANT') + settings.SERVICE_VARIANT ) sys.exit(1) diff --git a/openedx/core/djangoapps/content/course_overviews/management/commands/tests/test_simulate_publish.py b/openedx/core/djangoapps/content/course_overviews/management/commands/tests/test_simulate_publish.py index a91ff0ffffa4..1235559969c2 100644 --- a/openedx/core/djangoapps/content/course_overviews/management/commands/tests/test_simulate_publish.py +++ b/openedx/core/djangoapps/content/course_overviews/management/commands/tests/test_simulate_publish.py @@ -95,7 +95,7 @@ def options(self, **kwargs): receivers=default_receivers, courses=None, delay=0, - force_lms=False, + force_lms=True, skip_ccx=False, args_from_database=False ) @@ -155,12 +155,12 @@ def test_args_from_database(self): # Add a config config = SimulateCoursePublishConfig.current() - config.arguments = '--delay 20 --dry-run' + config.arguments = '--delay 20 --dry-run --force-lms ' config.enabled = True config.save() with LogCapture(LOGGER_NAME) as log: - call_command('simulate_publish') + call_command('simulate_publish', '--force-lms') log.check_present( ( diff --git a/openedx/envs/common.py b/openedx/envs/common.py index cfb82c90946e..29b77e48717a 100644 --- a/openedx/envs/common.py +++ b/openedx/envs/common.py @@ -26,6 +26,7 @@ def center_with_hashes(text: str, width: int = 76): ``` """ import os +import importlib.util from path import Path as path from django.utils.translation import gettext_lazy as _ @@ -48,6 +49,11 @@ def center_with_hashes(text: str, width: int = 76): USAGE_ID_PATTERN, ) +from xmodule.modulestore.edit_info import EditInfoMixin +from xmodule.modulestore.inheritance import InheritanceMixin +from xmodule.x_module import XModuleMixin +from lms.djangoapps.lms_xblock.mixin import LmsBlockMixin + ################ Shared Functions for Derived Configuration ################ @@ -91,11 +97,22 @@ def _make_locale_paths(settings): locale_paths += (path(locale_path), ) return locale_paths +################################## Paths ################################### + +REPO_ROOT = path(__file__).abspath().dirname().dirname().dirname() +COMMON_ROOT = REPO_ROOT / "common" +OPENEDX_ROOT = REPO_ROOT / "openedx" +ENV_ROOT = REPO_ROOT.dirname() # virtualenv dir /edx-platform is in +COURSES_ROOT = ENV_ROOT / "data" +XMODULE_ROOT = REPO_ROOT / "xmodule" +DATA_DIR = COURSES_ROOT + ############################# Django Built-Ins ############################# DEBUG = False USE_TZ = True +TIME_ZONE = 'UTC' # User-uploaded content MEDIA_ROOT = '/edx/var/edxapp/media/' @@ -117,18 +134,57 @@ def _make_locale_paths(settings): SESSION_SAVE_EVERY_REQUEST = False SESSION_SERIALIZER = 'openedx.core.lib.session_serializers.PickleSerializer' +CSRF_COOKIE_AGE = 60 * 60 * 24 * 7 * 52 +CSRF_TRUSTED_ORIGINS = [] +# It is highly recommended that you override this in any environment accessed by end users +CSRF_COOKIE_SECURE = False + +ALLOWED_HOSTS = ['*'] + +# Clickjacking protection can be disbaled by setting this to 'ALLOW' +X_FRAME_OPTIONS = 'DENY' + +ROOT_URLCONF = Derived(lambda settings: f'{settings.SERVICE_VARIANT}.urls') + ADMINS = [] MANAGERS = ADMINS DEFAULT_FROM_EMAIL = 'registration@example.com' SERVER_EMAIL = 'devops@example.com' +# Set request limits for maximum size of a request body and maximum number of GET/POST parameters. (>=Django 1.10) +# Limits are currently disabled - but can be used for finer-grained denial-of-service protection. +DATA_UPLOAD_MAX_MEMORY_SIZE = None +DATA_UPLOAD_MAX_NUMBER_FIELDS = None + # See https://github.com/openedx/edx-django-sites-extensions for more info. # Default site to use if site matching request headers does not exist. SITE_ID = 1 -# Clickjacking protection can be disbaled by setting this to 'ALLOW' -X_FRAME_OPTIONS = 'DENY' +STATICFILES_DIRS = [ + COMMON_ROOT / "static", + Derived(lambda settings: settings.PROJECT_ROOT / "static"), + # Temporarily adding the following static path as we are migrating the built-in blocks' Sass to vanilla CSS. + # Once all of the built-in blocks are extracted from edx-platform, we can remove this static path. + # Relevant ticket: https://github.com/openedx/edx-platform/issues/35300 + XMODULE_ROOT / "static", +] + +# List of finder classes that know how to find static files in various locations. +# Note: the pipeline finder is included to be able to discover optimized files +STATICFILES_FINDERS = [ + 'openedx.core.djangoapps.theming.finders.ThemeFilesFinder', + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', + 'openedx.core.lib.xblock_pipeline.finder.XBlockPipelineFinder', + 'pipeline.finders.PipelineFinder', +] + +AUTHENTICATION_BACKENDS = [ + 'rules.permissions.ObjectPermissionBackend', + 'django.contrib.auth.backends.AllowAllUsersModelBackend', + 'bridgekeeper.backends.RulePermissionBackend', +] AUTH_PASSWORD_VALIDATORS = [ { @@ -160,6 +216,8 @@ def _make_locale_paths(settings): # Messages MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage' +USE_I18N = True + # these languages display right to left LANGUAGES_BIDI = ("he", "ar", "fa", "ur", "fa-ir", "rtl") @@ -167,6 +225,8 @@ def _make_locale_paths(settings): LOCALE_PATHS = Derived(_make_locale_paths) +LANGUAGE_CODE = 'en' # http://www.i18nguy.com/unicode/language-identifiers.html + # Sourced from http://www.localeplanet.com/icu/ and wikipedia LANGUAGES = [ ('en', 'English'), @@ -251,6 +311,231 @@ def _make_locale_paths(settings): ('zh-tw', '中文 (台灣)'), # Chinese (Taiwan) ] +CACHES = { + 'course_structure_cache': { + 'KEY_PREFIX': 'course_structure', + 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', + 'LOCATION': ['localhost:11211'], + 'TIMEOUT': '604800', # 1 week + 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', + 'OPTIONS': { + 'no_delay': True, + 'ignore_exc': True, + 'use_pooling': True, + 'connect_timeout': 0.5 + } + }, + 'celery': { + 'KEY_PREFIX': 'celery', + 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', + 'LOCATION': ['localhost:11211'], + 'TIMEOUT': '7200', + 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', + 'OPTIONS': { + 'no_delay': True, + 'ignore_exc': True, + 'use_pooling': True, + 'connect_timeout': 0.5 + } + }, + 'mongo_metadata_inheritance': { + 'KEY_PREFIX': 'mongo_metadata_inheritance', + 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', + 'LOCATION': ['localhost:11211'], + 'TIMEOUT': 300, + 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', + 'OPTIONS': { + 'no_delay': True, + 'ignore_exc': True, + 'use_pooling': True, + 'connect_timeout': 0.5 + } + }, + 'staticfiles': { + 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', + 'LOCATION': ['localhost:11211'], + 'KEY_PREFIX': 'staticfiles_general', + 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', + 'OPTIONS': { + 'no_delay': True, + 'ignore_exc': True, + 'use_pooling': True, + 'connect_timeout': 0.5 + } + }, + 'default': { + 'VERSION': '1', + 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', + 'LOCATION': ['localhost:11211'], + 'KEY_PREFIX': 'default', + 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', + 'OPTIONS': { + 'no_delay': True, + 'ignore_exc': True, + 'use_pooling': True, + 'connect_timeout': 0.5 + } + }, + 'configuration': { + 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', + 'LOCATION': ['localhost:11211'], + 'KEY_PREFIX': 'configuration', + 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', + 'OPTIONS': { + 'no_delay': True, + 'ignore_exc': True, + 'use_pooling': True, + 'connect_timeout': 0.5 + } + }, + 'general': { + 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', + 'LOCATION': ['localhost:11211'], + 'KEY_PREFIX': 'general', + 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', + 'OPTIONS': { + 'no_delay': True, + 'ignore_exc': True, + 'use_pooling': True, + 'connect_timeout': 0.5 + } + }, +} + +DATABASES = { + # edxapp's edxapp-migrate scripts and the edxapp_migrate play + # will ensure that any DB not named read_replica will be migrated + # for both the lms and cms. + 'default': { + 'ATOMIC_REQUESTS': True, + 'CONN_MAX_AGE': 0, + 'ENGINE': 'django.db.backends.mysql', + 'HOST': '127.0.0.1', + 'NAME': 'edxapp', + 'OPTIONS': {}, + 'PASSWORD': 'password', + 'PORT': '3306', + 'USER': 'edxapp001' + }, + 'read_replica': { + 'CONN_MAX_AGE': 0, + 'ENGINE': 'django.db.backends.mysql', + 'HOST': '127.0.0.1', + 'NAME': 'edxapp', + 'OPTIONS': {}, + 'PASSWORD': 'password', + 'PORT': '3306', + 'USER': 'edxapp001' + }, + 'student_module_history': { + 'CONN_MAX_AGE': 0, + 'ENGINE': 'django.db.backends.mysql', + 'HOST': '127.0.0.1', + 'NAME': 'edxapp_csmh', + 'OPTIONS': {}, + 'PASSWORD': 'password', + 'PORT': '3306', + 'USER': 'edxapp001' + } +} + +DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' + +DATABASE_ROUTERS = [ + 'openedx.core.lib.django_courseware_routers.StudentModuleHistoryExtendedRouter', +] + +TEMPLATES = [ + { + 'NAME': 'django', + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + # Don't look for template source files inside installed applications. + 'APP_DIRS': False, + # Instead, look for template source files in these dirs. + 'DIRS': [ + Derived(lambda settings: settings.PROJECT_ROOT / "templates"), + COMMON_ROOT / 'templates', + XMODULE_ROOT / 'capa' / 'templates', + COMMON_ROOT / 'djangoapps' / 'pipeline_mako' / 'templates', + COMMON_ROOT / 'static', # required to statically include common Underscore templates + ], + # Options specific to this backend. + 'OPTIONS': { + 'loaders': [ + # We have to use mako-aware template loaders to be able to include + # mako templates inside django templates (such as main_django.html). + 'openedx.core.djangoapps.theming.template_loaders.ThemeTemplateLoader', + 'common.djangoapps.edxmako.makoloader.MakoFilesystemLoader', + 'common.djangoapps.edxmako.makoloader.MakoAppDirectoriesLoader', + ], + 'context_processors': Derived(lambda settings: settings.CONTEXT_PROCESSORS), + # Change 'debug' in your environment settings files - not here. + 'debug': False + } + }, + { + 'NAME': 'mako', + 'BACKEND': 'common.djangoapps.edxmako.backend.Mako', + # Don't look for template source files inside installed applications. + 'APP_DIRS': False, + # Instead, look for template source files in these dirs. + 'DIRS': Derived(make_mako_template_dirs), + # Options specific to this backend. + 'OPTIONS': { + 'context_processors': Derived(lambda settings: settings.CONTEXT_PROCESSORS), + # Change 'debug' in your environment settings files - not here. + 'debug': False, + } + }, +] + +################################ Templates ################################# + + +def make_mako_module_dir(settings): + """ + Returns the directory where Mako templates are stored. + + Args: + settings: A Django settings module object. + + Returns: + list: A list of Mako template directories, potentially updated with additional + theme directories. + """ + import tempfile + return os.path.join(tempfile.gettempdir(), f'mako_{settings.SERVICE_VARIANT}') + +MAKO_MODULE_DIR = Derived(make_mako_module_dir) +MAKO_TEMPLATE_DIRS_BASE = [ + Derived(lambda settings: settings.PROJECT_ROOT / 'templates'), + COMMON_ROOT / 'templates', + COMMON_ROOT / 'djangoapps' / 'pipeline_mako' / 'templates', + OPENEDX_ROOT / 'core' / 'djangoapps' / 'cors_csrf' / 'templates', + OPENEDX_ROOT / 'core' / 'djangoapps' / 'dark_lang' / 'templates', + OPENEDX_ROOT / 'core' / 'lib' / 'license' / 'templates', +] + +# Since the CMS uses the LMS's list of mako template directories for the "preview" +# template engine, we define the list here +lms_mako_template_dirs_base = list(MAKO_TEMPLATE_DIRS_BASE) +lms_mako_template_dirs_base.insert(2, XMODULE_ROOT / 'capa' / 'templates') +lms_mako_template_dirs_base.append(OPENEDX_ROOT / 'features' / 'course_experience' / 'templates') + +CONTEXT_PROCESSORS = [ + 'django.template.context_processors.request', + 'django.template.context_processors.static', + 'django.contrib.messages.context_processors.messages', + 'django.template.context_processors.i18n', + 'django.contrib.auth.context_processors.auth', # this is required for admin + 'django.template.context_processors.csrf', + # Online contextual help + 'help_tokens.context_processor', + 'openedx.core.djangoapps.site_configuration.context_processors.configuration_context', +] + +DEFAULT_TEMPLATE_ENGINE = TEMPLATES[0] + ############################## Site Settings ############################### HTTPS = 'on' @@ -460,6 +745,62 @@ def _make_locale_paths(settings): LANGUAGE_DICT = dict(LANGUAGES) +############################## Optional Apps ############################### + +OPTIONAL_APPS = [ + ('problem_builder', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), + ('edx_sga', None), + + # edx-ora2 + ('submissions', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), + ('openassessment', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), + ('openassessment.assessment', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), + ('openassessment.fileupload', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), + ('openassessment.staffgrader', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), + ('openassessment.workflow', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), + ('openassessment.xblock', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), + + # edxval + ('edxval', 'openedx.core.djangoapps.content.course_overviews.apps.CourseOverviewsConfig'), + + # Enterprise Apps (http://github.com/openedx/edx-enterprise) + ('enterprise', None), + ('consent', None), + ('integrated_channels.integrated_channel', None), + ('integrated_channels.degreed', None), + ('integrated_channels.degreed2', None), + ('integrated_channels.sap_success_factors', None), + ('integrated_channels.cornerstone', None), + ('integrated_channels.xapi', None), + ('integrated_channels.blackboard', None), + ('integrated_channels.canvas', None), + ('integrated_channels.moodle', None), +] + + +def add_optional_apps(optional_apps, installed_apps): + """ + Adds apps from optional_apps to installed_apps if they can be imported. + + :param optional_apps: List of tuples (str, str). The tuples should contain the name + of the app and the name of the app which it should be inserted before. + :param installed_apps: List of installed Django apps to modify (i.e. INSTALLED_APPS) + """ + for app_name, insert_before in optional_apps: + # First attempt to only find the module rather than actually importing it, + # to avoid circular references - only try to import if it can't be found + # by find_spec, which doesn't work with import hooks + if importlib.util.find_spec(app_name) is None: + try: + __import__(app_name) + except ImportError: + continue + + try: + installed_apps.insert(installed_apps.index(insert_before), app_name) + except (IndexError, ValueError): + installed_apps.append(app_name) + ########################## Django Rest Framework ########################### REST_FRAMEWORK = { @@ -537,14 +878,17 @@ def _make_locale_paths(settings): CELERY_SEND_EVENTS = True CELERY_SEND_TASK_SENT_EVENT = True -# Exchange configuration -CELERY_DEFAULT_EXCHANGE = 'edx.core' -CELERY_DEFAULT_EXCHANGE_TYPE = 'direct' - # Queues configuration CELERY_QUEUE_HA_POLICY = 'all' CELERY_CREATE_MISSING_QUEUES = True +# Exchange configuration +CELERY_DEFAULT_EXCHANGE_TYPE = 'direct' +CELERY_DEFAULT_EXCHANGE = Derived(lambda settings: f'edx.{settings.SERVICE_VARIANT}.core') + +CELERY_DEFAULT_QUEUE = Derived(lambda settings: settings.DEFAULT_PRIORITY_QUEUE) +CELERY_DEFAULT_ROUTING_KEY = CELERY_DEFAULT_QUEUE = Derived(lambda settings: settings.DEFAULT_PRIORITY_QUEUE) + # Checks run in normal mode by the heartbeat djangoapp HEARTBEAT_CHECKS = [ 'openedx.core.djangoapps.heartbeat.default_checks.check_modulestore', @@ -558,6 +902,8 @@ def _make_locale_paths(settings): HEARTBEAT_CELERY_TIMEOUT = 5 +HEARTBEAT_CELERY_ROUTING_KEY = Derived(lambda settings: settings.HIGH_PRIORITY_QUEUE) + ############################ RedirectMiddleware ############################ # Setting this to None causes Redirect data to never expire @@ -614,6 +960,12 @@ def _make_locale_paths(settings): 'JWT_AUTH_HEADER_PREFIX': 'JWT', } +EDX_DRF_EXTENSIONS = { + # Set this value to an empty dict in order to prevent automatically updating + # user data from values in (possibly stale) JWTs. + 'JWT_PAYLOAD_USER_ATTRIBUTE_MAPPING': {}, +} + ################################# Features ################################# # .. setting_name: PLATFORM_NAME @@ -1046,107 +1398,8 @@ def _make_locale_paths(settings): } } -########################### Cache Configuration ############################ - -CACHES = { - 'course_structure_cache': { - 'KEY_PREFIX': 'course_structure', - 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', - 'LOCATION': ['localhost:11211'], - 'TIMEOUT': '604800', # 1 week - 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', - 'OPTIONS': { - 'no_delay': True, - 'ignore_exc': True, - 'use_pooling': True, - 'connect_timeout': 0.5 - } - }, - 'celery': { - 'KEY_PREFIX': 'celery', - 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', - 'LOCATION': ['localhost:11211'], - 'TIMEOUT': '7200', - 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', - 'OPTIONS': { - 'no_delay': True, - 'ignore_exc': True, - 'use_pooling': True, - 'connect_timeout': 0.5 - } - }, - 'mongo_metadata_inheritance': { - 'KEY_PREFIX': 'mongo_metadata_inheritance', - 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', - 'LOCATION': ['localhost:11211'], - 'TIMEOUT': 300, - 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', - 'OPTIONS': { - 'no_delay': True, - 'ignore_exc': True, - 'use_pooling': True, - 'connect_timeout': 0.5 - } - }, - 'staticfiles': { - 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', - 'LOCATION': ['localhost:11211'], - 'KEY_PREFIX': 'staticfiles_general', - 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', - 'OPTIONS': { - 'no_delay': True, - 'ignore_exc': True, - 'use_pooling': True, - 'connect_timeout': 0.5 - } - }, - 'default': { - 'VERSION': '1', - 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', - 'LOCATION': ['localhost:11211'], - 'KEY_PREFIX': 'default', - 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', - 'OPTIONS': { - 'no_delay': True, - 'ignore_exc': True, - 'use_pooling': True, - 'connect_timeout': 0.5 - } - }, - 'configuration': { - 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', - 'LOCATION': ['localhost:11211'], - 'KEY_PREFIX': 'configuration', - 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', - 'OPTIONS': { - 'no_delay': True, - 'ignore_exc': True, - 'use_pooling': True, - 'connect_timeout': 0.5 - } - }, - 'general': { - 'KEY_FUNCTION': 'common.djangoapps.util.memcache.safe_key', - 'LOCATION': ['localhost:11211'], - 'KEY_PREFIX': 'general', - 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', - 'OPTIONS': { - 'no_delay': True, - 'ignore_exc': True, - 'use_pooling': True, - 'connect_timeout': 0.5 - } - }, -} - ################################### CSRF ################################### -CSRF_COOKIE_AGE = 60 * 60 * 24 * 7 * 52 - -# It is highly recommended that you override this in any environment accessed by -# end users -CSRF_COOKIE_SECURE = False - # If setting a cross-domain cookie, it's really important to choose # a name for the cookie that is DIFFERENT than the cookies used # by each subdomain. For example, suppose the applications @@ -1174,8 +1427,6 @@ def _make_locale_paths(settings): # the client won't be able to read the cookie. CROSS_DOMAIN_CSRF_COOKIE_DOMAIN = '' -CSRF_TRUSTED_ORIGINS = [] - ENABLE_CROSS_DOMAIN_CSRF_COOKIE = False ########################## Cross-domain Requests ########################### @@ -1212,6 +1463,21 @@ def _make_locale_paths(settings): 'facebook': 'http://email-media.s3.amazonaws.com/edX/2021/social_1_fb.png', } +########################### Social Media Sharing ########################### + +# Social Media Sharing on Student Dashboard +SOCIAL_SHARING_SETTINGS = { + 'CUSTOM_COURSE_URLS': False, # This value should not differ between lms/cms + 'DASHBOARD_FACEBOOK': False, + 'CERTIFICATE_FACEBOOK': False, + 'CERTIFICATE_TWITTER': False, + 'DASHBOARD_TWITTER': False, +} + +############################# Google Analytics ############################# + +GOOGLE_ANALYTICS_ACCOUNT = None + ############################# Block Structures ############################# # .. setting_name: BLOCK_STRUCTURES_SETTINGS @@ -1316,6 +1582,16 @@ def _make_locale_paths(settings): VIDEO_TRANSCRIPTS_MAX_AGE = 31536000 +TRANSCRIPT_LANG_CACHE_TIMEOUT = 60 * 60 * 24 # 24 hours + +############################## Video Pipeline ############################## + +VIDEO_UPLOAD_PIPELINE = { + 'VEM_S3_BUCKET': '', + 'BUCKET': '', + 'ROOT_PATH': '', +} + ############################ Parental Controls ############################# # .. setting_name: PARENTAL_CONSENT_AGE_LIMIT @@ -1366,6 +1642,30 @@ def _make_locale_paths(settings): # .. toggle_creation_date: 2021-06-10 SHOW_ACCOUNT_ACTIVATION_CTA = False +# .. setting_name: REGISTRATION_EXTRA_FIELDS +# .. setting_default: {'confirm_email': 'hidden', 'level_of_education': 'optional', 'gender': 'optional', +# 'year_of_birth': 'optional', 'mailing_address': 'optional', 'goals': 'optional', 'honor_code': 'required', +# 'terms_of_service': 'hidden', 'city': 'hidden', 'country': 'hidden'} +# .. setting_description: The signup form may contain extra fields that are presented to every user. For every field, we +# can specifiy whether it should be "required": to display the field, and make it mandatory; "optional": to display +# the optional field as part of a toggled input field list; "optional-exposed": to display the optional fields among +# the required fields, and make it non-mandatory; "hidden": to not display the field. +# When the terms of service are not visible and agreement to the honor code is required (the default), the signup page +# includes a paragraph that links to the honor code page (defined my MKTG_URLS["HONOR"]). This page might not be +# available for all Open edX platforms. In such cases, the "honor_code" registration field should be "hidden". +REGISTRATION_EXTRA_FIELDS = { + 'confirm_email': 'hidden', + 'level_of_education': 'optional', + 'gender': 'optional', + 'year_of_birth': 'optional', + 'mailing_address': 'optional', + 'goals': 'optional', + 'honor_code': 'required', + 'terms_of_service': 'hidden', + 'city': 'hidden', + 'country': 'hidden', +} + ######################### Course Enrollment Modes ########################## # The min_price key refers to the minimum price allowed for an instance @@ -1465,45 +1765,81 @@ def _make_locale_paths(settings): ASSET_IGNORE_REGEX = r"(^\._.*$)|(^\.DS_Store$)|(^.*~$)" -DATABASES = { - # edxapp's edxapp-migrate scripts and the edxapp_migrate play - # will ensure that any DB not named read_replica will be migrated - # for both the lms and cms. - 'default': { - 'ATOMIC_REQUESTS': True, - 'CONN_MAX_AGE': 0, - 'ENGINE': 'django.db.backends.mysql', - 'HOST': '127.0.0.1', - 'NAME': 'edxapp', - 'OPTIONS': {}, - 'PASSWORD': 'password', - 'PORT': '3306', - 'USER': 'edxapp001' - }, - 'read_replica': { - 'CONN_MAX_AGE': 0, - 'ENGINE': 'django.db.backends.mysql', - 'HOST': '127.0.0.1', - 'NAME': 'edxapp', - 'OPTIONS': {}, - 'PASSWORD': 'password', - 'PORT': '3306', - 'USER': 'edxapp001' +# This Django setting was removed in Django 4.0, but it is still being referenced +# in the code. It would be worth investigating if we still need this and, if we do, +# leave a comment explaining why here. See https://github.com/openedx/edx-platform/issues/37744. +DEFAULT_HASHING_ALGORITHM = 'sha256' + +DOC_STORE_CONFIG = { + 'db': 'edxapp', + 'host': 'localhost', + 'replicaSet': '', + 'port': 27017, + 'user': 'edxapp', + 'collection': 'modulestore', + 'ssl': False, + # https://api.mongodb.com/python/2.9.1/api/pymongo/mongo_client.html#module-pymongo.mongo_client + # default is never timeout while the connection is open, + #this means it needs to explicitly close raising pymongo.errors.NetworkTimeout + 'socketTimeoutMS': 6000, + 'connectTimeoutMS': 2000, # default is 20000, I believe raises pymongo.errors.ConnectionFailure + # Not setting waitQueueTimeoutMS and waitQueueMultiple since pymongo defaults to nobody being allowed to wait + 'auth_source': None, + # If 'asset_collection' defined, it'll be used as the collection name for asset metadata. + # Otherwise, a default collection name will be used. +} + +CONTENTSTORE = { + 'ENGINE': 'xmodule.contentstore.mongo.MongoContentStore', + # connection strings are duplicated temporarily for + # backward compatibility + 'OPTIONS': { + 'db': 'edxapp', + 'host': 'localhost', + 'password': 'password', + 'port': 27017, + 'user': 'edxapp', + 'ssl': False, + 'auth_source': None }, - 'student_module_history': { - 'CONN_MAX_AGE': 0, - 'ENGINE': 'django.db.backends.mysql', - 'HOST': '127.0.0.1', - 'NAME': 'edxapp_csmh', - 'OPTIONS': {}, - 'PASSWORD': 'password', - 'PORT': '3306', - 'USER': 'edxapp001' + 'ADDITIONAL_OPTIONS': {}, + 'DOC_STORE_CONFIG': DOC_STORE_CONFIG +} + +MODULESTORE = { + 'default': { + 'ENGINE': 'xmodule.modulestore.mixed.MixedModuleStore', + 'OPTIONS': { + 'mappings': {}, + 'stores': [ + { + 'NAME': 'split', + 'ENGINE': 'xmodule.modulestore.split_mongo.split_draft.DraftVersioningModuleStore', + 'DOC_STORE_CONFIG': DOC_STORE_CONFIG, + 'OPTIONS': { + 'default_class': 'xmodule.hidden_block.HiddenBlock', + 'fs_root': Derived(lambda settings: settings.DATA_DIR), + 'render_template': 'common.djangoapps.edxmako.shortcuts.render_to_string', + } + }, + { + 'NAME': 'draft', + 'ENGINE': 'xmodule.modulestore.mongo.DraftMongoModuleStore', + 'DOC_STORE_CONFIG': DOC_STORE_CONFIG, + 'OPTIONS': { + 'default_class': 'xmodule.hidden_block.HiddenBlock', + 'fs_root': Derived(lambda settings: settings.DATA_DIR), + 'render_template': 'common.djangoapps.edxmako.shortcuts.render_to_string', + } + } + ] + } } } -DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' -DEFAULT_HASHING_ALGORITHM = 'sha256' +# Modulestore-level field override providers. These field override providers don't +# require student context. +MODULESTORE_FIELD_OVERRIDE_PROVIDERS = () ############################# Micro-frontends ############################## @@ -1652,6 +1988,21 @@ def _make_locale_paths(settings): # .. setting_description: The django cache key of the cache to use for storing anonymous user state for XBlocks. XBLOCK_RUNTIME_V2_EPHEMERAL_DATA_CACHE = 'default' +# These are the Mixins that will be added to every Blocklike upon instantiation. +# DO NOT EXPAND THIS LIST!! We want it eventually to be EMPTY. Why? Because dynamically adding functions/behaviors to +# objects at runtime is confusing for both developers and static tooling (pylint/mypy). Instead... +# - to add special Blocklike behaviors just for your site: override `XBLOCK_EXTRA_MIXINS` with your own XBlockMixins. +# - to add new functionality to all Blocklikes: add it to the base Blocklike class in the core openedx/XBlock repo. +XBLOCK_MIXINS = ( + # TODO: For each of these, either + # (a) merge their functionality into the base Blocklike class, or + # (b) refactor their functionality out of the Blocklike objects and into the edx-platform block runtimes. + LmsBlockMixin, + InheritanceMixin, + XModuleMixin, + EditInfoMixin, +) + ######################## Built-in Blocks Extraction ######################## # The following Django settings flags have been introduced temporarily to facilitate @@ -1748,9 +2099,15 @@ def _make_locale_paths(settings): MKTG_URLS = {} MKTG_URL_OVERRIDES = {} +MKTG_URL_LINK_MAP = {} SUPPORT_SITE_LINK = '' +ACTIVATION_EMAIL_SUPPORT_LINK = Derived(lambda settings: settings.SUPPORT_SITE_LINK) +ID_VERIFICATION_SUPPORT_LINK = Derived(lambda settings: settings.SUPPORT_SITE_LINK) +LOGIN_ISSUE_SUPPORT_LINK = Derived(lambda settings: settings.SUPPORT_SITE_LINK) +PASSWORD_RESET_SUPPORT_LINK = Derived(lambda settings: settings.SUPPORT_SITE_LINK) + ################################# ChatGPT ################################## CHAT_COMPLETION_API = '' @@ -1791,6 +2148,31 @@ def _make_locale_paths(settings): ############################## Python sandbox ############################## +CODE_JAIL = { + # from https://github.com/openedx/codejail/blob/master/codejail/django_integration.py#L24, '' should be same as None + 'python_bin': '/edx/app/edxapp/venvs/edxapp-sandbox/bin/python', + # User to run as in the sandbox. + 'user': 'sandbox', + + # Configurable limits. + 'limits': { + # How many CPU seconds can jailed code use? + 'CPU': 1, + # Limit the memory of the jailed process to something high but not + # infinite (512MiB in bytes) + 'VMEM': 536870912, + # Time in seconds that the jailed process has to run. + 'REALTIME': 3, + 'PROXY': 0, + }, + + # Overrides to default configurable 'limits' (above). + # Keys should be course run ids (or, in the special case of code running + # on the /debug/run_python page, the key is 'debug_run_python'). + # Values should be dictionaries that look like 'limits'. + "limit_overrides": {}, +} + # Some courses are allowed to run unsafe code. This is a list of regexes, one # of them must match the course id for that course to run unsafe code. # @@ -1831,8 +2213,6 @@ def _make_locale_paths(settings): # Locale/Internationalization CELERY_TIMEZONE = 'UTC' -TIME_ZONE = 'UTC' -LANGUAGE_CODE = 'en' # http://www.i18nguy.com/unicode/language-identifiers.html # Languages supported for custom course certificate templates CERTIFICATE_TEMPLATE_LANGUAGES = { @@ -1840,26 +2220,58 @@ def _make_locale_paths(settings): 'es': 'Español', } -USE_I18N = True -USE_L10N = True - STATICI18N_FILENAME_FUNCTION = 'statici18n.utils.legacy_filename' STATICI18N_OUTPUT_DIR = "js/i18n" +STATICI18N_ROOT = Derived(lambda settings: settings.PROJECT_ROOT / "static") ################################# Pipeline ################################# +PIPELINE = { + 'PIPELINE_ENABLED': True, + # Don't use compression by default + 'CSS_COMPRESSOR': None, + # Don't wrap JavaScript as there is code that depends upon updating the global namespace + 'DISABLE_WRAPPER': True, + # Specify the UglifyJS binary to use + 'UGLIFYJS_BINARY': 'node_modules/.bin/uglifyjs', +} + STATICFILES_STORAGE_KWARGS = {} -# List of finder classes that know how to find static files in various locations. -# Note: the pipeline finder is included to be able to discover optimized files -STATICFILES_FINDERS = [ - 'openedx.core.djangoapps.theming.finders.ThemeFilesFinder', - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', - 'openedx.core.lib.xblock_pipeline.finder.XBlockPipelineFinder', - 'pipeline.finders.PipelineFinder', +STATICFILES_IGNORE_PATTERNS = [ + "*.py", + "*.pyc", + + # It would be nice if we could do, for example, "**/*.scss", + # but these strings get passed down to the `fnmatch` module, + # which doesn't support that. :( + # http://docs.python.org/2/library/fnmatch.html + "sass/*.scss", + "sass/*/*.scss", + "sass/*/*/*.scss", + "sass/*/*/*/*.scss", + + # Ignore tests + "spec", + "spec_helpers", + + # Symlinks used by js-test-tool + "xmodule_js", ] +########################## Django Webpack Loader ########################### + +WEBPACK_LOADER = { + 'DEFAULT': { + 'BUNDLE_DIR_NAME': 'bundles/', + 'STATS_FILE': Derived(lambda settings: os.path.join(settings.STATIC_ROOT, 'webpack-stats.json')), + }, + 'WORKERS': { + 'BUNDLE_DIR_NAME': 'bundles/', + 'STATS_FILE': Derived(lambda settings: os.path.join(settings.STATIC_ROOT, 'webpack-worker-stats.json')), + } +} + ############################## django-require ############################## # The baseUrl to pass to the r.js optimizer, relative to STATIC_ROOT. @@ -1868,6 +2280,12 @@ def _make_locale_paths(settings): # Whether to run django-require in debug mode. REQUIRE_DEBUG = False +# The name of a build profile to use for your project, relative to REQUIRE_BASE_URL. +# A sensible value would be 'app.build.js'. Leave blank to use the built-in default build profile. +# Set to False to disable running the default profile (e.g. if only using it to build Standalone +# Modules) +REQUIRE_BUILD_PROFILE = Derived(lambda settings: f"{settings.SERVICE_VARIANT}/js/build.js") + ########################### Student Verification ########################### VERIFY_STUDENT = { @@ -1992,7 +2410,6 @@ def _make_locale_paths(settings): # .. toggle_creation_date: 2016-06-30 ENABLE_COMPREHENSIVE_THEMING = False - ################################ Ecommerce ################################# ECOMMERCE_PUBLIC_URL_ROOT = 'http://localhost:8002' @@ -2068,7 +2485,7 @@ def _make_locale_paths(settings): HELP_TOKENS_LANGUAGE_CODE = Derived(lambda settings: settings.LANGUAGE_CODE) HELP_TOKENS_VERSION = Derived(lambda settings: doc_version()) - +HELP_TOKENS_INI_FILE = Derived(lambda settings: REPO_ROOT / settings.SERVICE_VARIANT / "envs" / "help_tokens.ini") HELP_TOKENS_BOOKS = { 'learner': 'https://docs.openedx.org/en/latest/learners', 'course_author': 'https://docs.openedx.org/en/latest/educators', @@ -2245,9 +2662,92 @@ def _make_locale_paths(settings): def should_send_learning_badge_events(settings): return settings.BADGES_ENABLED -############################## ALLOWED_HOSTS ############################### +# .. setting_name: EVENT_BUS_PRODUCER_CONFIG +# .. setting_default: all events disabled +# .. setting_description: Dictionary of event_types mapped to dictionaries of topic to topic-related configuration. +# Each topic configuration dictionary contains +# * `enabled`: a toggle denoting whether the event will be published to the topic. These should be annotated +# according to +# https://docs.openedx.org/projects/edx-toggles/en/latest/how_to/documenting_new_feature_toggles.html +# * `event_key_field` which is a period-delimited string path to event data field to use as event key. +# Note: The topic names should not include environment prefix as it will be dynamically added based on +# EVENT_BUS_TOPIC_PREFIX setting. +EVENT_BUS_PRODUCER_CONFIG = { + "org.openedx.learning.course.passing.status.updated.v1": { + "learning-badges-lifecycle": { + "event_key_field": "course_passing_status.course.course_key", + "enabled": Derived(should_send_learning_badge_events), + }, + }, + "org.openedx.learning.ccx.course.passing.status.updated.v1": { + "learning-badges-lifecycle": { + "event_key_field": "course_passing_status.course.ccx_course_key", + "enabled": Derived(should_send_learning_badge_events), + }, + }, +} + +### event tracking -ALLOWED_HOSTS = ['*'] +EVENT_TRACKING_ENABLED = True +EVENT_TRACKING_PROCESSORS = [] +EVENT_TRACKING_SEGMENTIO_EMIT_WHITELIST = [] + +# FIXME: Should we be doing this truncation? +TRACK_MAX_EVENT = 50000 + +TRACKING_BACKENDS = { + 'logger': { + 'ENGINE': 'common.djangoapps.track.backends.logger.LoggerBackend', + 'OPTIONS': { + 'name': 'tracking' + } + } +} + +EVENT_TRACKING_BACKENDS = { + 'tracking_logs': { + 'ENGINE': 'eventtracking.backends.routing.RoutingBackend', + 'OPTIONS': { + 'backends': { + 'logger': { + 'ENGINE': 'eventtracking.backends.logger.LoggerBackend', + 'OPTIONS': { + 'name': 'tracking', + 'max_event_size': TRACK_MAX_EVENT, + } + } + }, + 'processors': [ + {'ENGINE': 'common.djangoapps.track.shim.LegacyFieldMappingProcessor'}, + {'ENGINE': 'common.djangoapps.track.shim.PrefixedEventProcessor'} + ] + } + }, + 'segmentio': { + 'ENGINE': 'eventtracking.backends.routing.RoutingBackend', + 'OPTIONS': { + 'backends': { + 'segment': {'ENGINE': 'eventtracking.backends.segment.SegmentBackend'} + }, + 'processors': [ + { + 'ENGINE': 'eventtracking.processors.whitelist.NameWhitelistProcessor', + 'OPTIONS': { + 'whitelist': [] + } + }, + { + 'ENGINE': 'common.djangoapps.track.shim.GoogleAnalyticsProcessor' + } + ] + } + } +} + +# We're already logging events, and we don't want to capture user +# names/passwords. Heartbeat events are likely not interesting. +TRACKING_IGNORE_URL_PATTERNS = [r'^/event', r'^/login', r'^/heartbeat'] ############################## Miscellaneous ############################### @@ -2299,6 +2799,9 @@ def should_send_learning_badge_events(settings): SOFTWARE_SECURE_VERIFICATION_ROUTING_KEY = Derived(lambda settings: settings.HIGH_PRIORITY_QUEUE) +# Queue to use for updating persistent grades +RECALCULATE_GRADES_ROUTING_KEY = Derived(lambda settings: settings.DEFAULT_PRIORITY_QUEUE) + # Queue to use for updating grades due to grading policy change POLICY_CHANGE_GRADES_ROUTING_KEY = Derived(lambda settings: settings.DEFAULT_PRIORITY_QUEUE) @@ -2340,11 +2843,6 @@ def should_send_learning_badge_events(settings): MARKETING_EMAILS_OPT_IN = False -# Set request limits for maximum size of a request body and maximum number of GET/POST parameters. (>=Django 1.10) -# Limits are currently disabled - but can be used for finer-grained denial-of-service protection. -DATA_UPLOAD_MAX_MEMORY_SIZE = None -DATA_UPLOAD_MAX_NUMBER_FIELDS = None - # License for serving content in China ICP_LICENSE = None ICP_LICENSE_INFO = {} @@ -2443,3 +2941,15 @@ def should_send_learning_badge_events(settings): COURSE_LIVE_GLOBAL_CREDENTIALS = {} BEAMER_PRODUCT_ID = "" + +# For geolocation ip database +GEOIP_PATH = REPO_ROOT / "common/static/data/geoip/GeoLite2-Country.mmdb" + +# .. toggle_name: WIKI_ENABLED +# .. toggle_implementation: DjangoSetting +# .. toggle_default: True +# .. toggle_description: This setting allows us to have a collaborative tool to contribute or +# modify content of course related materials. +# .. toggle_use_cases: open_edx +# .. toggle_creation_date: 2012-07-13 +WIKI_ENABLED = True diff --git a/openedx/envs/test.py b/openedx/envs/test.py index 60561274564c..66558c2c8e26 100644 --- a/openedx/envs/test.py +++ b/openedx/envs/test.py @@ -90,7 +90,6 @@ def make_staticfile_dirs(settings): # Test theme TEST_THEME = Derived(lambda settings: settings.COMMON_ROOT / "test" / "test-theme") ENABLE_COMPREHENSIVE_THEMING = True -COMPREHENSIVE_THEME_DIRS = Derived(lambda settings: [settings.REPO_ROOT / "themes", settings.REPO_ROOT / "common/test"]) # Enable EdxNotes for tests ENABLE_EDXNOTES = True diff --git a/setup.cfg b/setup.cfg index 948121801525..ac0907b667c3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -85,11 +85,13 @@ ignore_imports = ############################################################################ # cms side imports that we are ignoring for now cms.djangoapps.contentstore.views.tests.test_block -> lms.djangoapps.lms_xblock.mixin - cms.envs.common -> lms.envs.common cms.djangoapps.contentstore.signals.handlers -> lms.djangoapps.grades.api cms.djangoapps.contentstore.course_group_config -> lms.lib.utils cms.djangoapps.contentstore.views.preview -> lms.djangoapps.lms_xblock.field_data - cms.envs.common -> lms.djangoapps.lms_xblock.mixin + # cms.envs.common + # -> openedx.envs.common + # -> lms.djangoapps.lms_xblock.mixin + openedx.envs.common -> lms.djangoapps.lms_xblock.mixin # cms.djangoapps.contentstore.views.tests.test_group_configurations # -> openedx.features.content_type_gating.helpers # -> lms.djangoapps.courseware.masquerade