6262import requests
6363
6464from django .conf import settings
65- from django .contrib .auth .models import AbstractUser , Group
65+ from django .contrib .auth .models import AbstractUser , AnonymousUser , Group
6666from django .core .exceptions import ObjectDoesNotExist , PermissionDenied
6767from django .core .validators import validate_unicode_slug
6868from django .db import IntegrityError , transaction
116116from openedx .core .types import User as UserType
117117from xmodule .modulestore .django import modulestore
118118
119- from . import permissions , tasks
120- from .constants import ALL_RIGHTS_RESERVED
121- from .models import ContentLibrary , ContentLibraryPermission , ContentLibraryBlockImportTask
119+ from .. import permissions , tasks
120+ from .. constants import ALL_RIGHTS_RESERVED
121+ from .. models import ContentLibrary , ContentLibraryPermission , ContentLibraryBlockImportTask
122122
123123log = logging .getLogger (__name__ )
124124
125+ # The public API is only the following symbols:
126+ __all__ = [
127+ # Exceptions - maybe move them to a new file?
128+ "ContentLibraryNotFound" ,
129+ "ContentLibraryCollectionNotFound" ,
130+ "ContentLibraryBlockNotFound" ,
131+ "LibraryAlreadyExists" ,
132+ "LibraryCollectionAlreadyExists" ,
133+ "LibraryBlockAlreadyExists" ,
134+ "BlockLimitReachedError" ,
135+ "IncompatibleTypesError" ,
136+ "InvalidNameError" ,
137+ "LibraryPermissionIntegrityError" ,
138+ # Library Models
139+ "ContentLibrary" , # Should this be public or not?
140+ "ContentLibraryMetadata" ,
141+ "AccessLevel" ,
142+ "ContentLibraryPermissionEntry" ,
143+ "CollectionMetadata" ,
144+ # Library API methods
145+ "user_can_create_library" ,
146+ "get_libraries_for_user" ,
147+ "get_metadata" ,
148+ "require_permission_for_library_key" ,
149+ "get_library" ,
150+ "create_library" ,
151+ "get_library_team" ,
152+ "get_library_user_permissions" ,
153+ "set_library_user_permissions" ,
154+ "set_library_group_permissions" ,
155+ "update_library" ,
156+ "delete_library" ,
157+ "get_allowed_block_types" ,
158+ "publish_changes" ,
159+ "revert_changes" ,
160+ # Collections - TODO: move to a new file
161+ "create_library_collection" ,
162+ "update_library_collection" ,
163+ "update_library_collection_components" ,
164+ "set_library_component_collections" ,
165+ "get_library_collection_usage_key" ,
166+ "get_library_collection_from_usage_key" ,
167+ # Import - TODO: move to a new file
168+ "EdxModulestoreImportClient" ,
169+ "EdxApiImportClient" ,
170+ "import_blocks_create_task" ,
171+ ]
172+
125173
126174# Exceptions
127175# ==========
@@ -416,6 +464,7 @@ def get_library(library_key: LibraryLocatorV2) -> ContentLibraryMetadata:
416464 """
417465 ref = ContentLibrary .objects .get_by_key (library_key )
418466 learning_package = ref .learning_package
467+ assert learning_package is not None # Shouldn't happen - this is just for the type checker
419468 num_blocks = authoring_api .get_all_drafts (learning_package .id ).count ()
420469 last_publish_log = authoring_api .get_last_publish (learning_package .id )
421470 last_draft_log = authoring_api .get_entities_with_unpublished_changes (learning_package .id ) \
@@ -455,7 +504,7 @@ def get_library(library_key: LibraryLocatorV2) -> ContentLibraryMetadata:
455504 return ContentLibraryMetadata (
456505 key = library_key ,
457506 title = learning_package .title ,
458- description = ref . learning_package .description ,
507+ description = learning_package .description ,
459508 num_blocks = num_blocks ,
460509 version = version ,
461510 last_published = None if last_publish_log is None else last_publish_log .published_at ,
@@ -557,6 +606,8 @@ def get_library_user_permissions(library_key: LibraryLocatorV2, user: UserType)
557606 Fetch the specified user's access information. Will return None if no
558607 permissions have been granted.
559608 """
609+ if isinstance (user , AnonymousUser ):
610+ return None # Mostly here for the type checker
560611 ref = ContentLibrary .objects .get_by_key (library_key )
561612 grant = ref .permission_grants .filter (user = user ).first ()
562613 if grant is None :
@@ -574,6 +625,8 @@ def set_library_user_permissions(library_key: LibraryLocatorV2, user: UserType,
574625
575626 access_level should be one of the AccessLevel values defined above.
576627 """
628+ if isinstance (user , AnonymousUser ):
629+ raise TypeError ("Invalid user type" ) # Mostly here for the type checker
577630 ref = ContentLibrary .objects .get_by_key (library_key )
578631 current_grant = get_library_user_permissions (library_key , user )
579632 if current_grant and current_grant .access_level == AccessLevel .ADMIN_LEVEL :
@@ -633,6 +686,8 @@ def update_library(
633686 return
634687
635688 content_lib = ContentLibrary .objects .get_by_key (library_key )
689+ learning_package_id = content_lib .learning_package_id
690+ assert learning_package_id is not None
636691
637692 with transaction .atomic ():
638693 # We need to make updates to both the ContentLibrary and its linked
@@ -643,12 +698,12 @@ def update_library(
643698 if allow_public_read is not None :
644699 content_lib .allow_public_read = allow_public_read
645700 if library_license is not None :
646- content_lib .library_license = library_license
701+ content_lib .license = library_license
647702 content_lib .save ()
648703
649704 if learning_pkg_changed :
650705 authoring_api .update_learning_package (
651- content_lib . learning_package_id ,
706+ learning_package_id ,
652707 title = title ,
653708 description = description ,
654709 )
@@ -675,7 +730,8 @@ def delete_library(library_key: LibraryLocatorV2) -> None:
675730 # TODO: We should eventually detach the LearningPackage and delete it
676731 # asynchronously, especially if we need to delete a bunch of stuff
677732 # on the filesystem for it.
678- learning_package .delete ()
733+ if learning_package :
734+ learning_package .delete ()
679735
680736 CONTENT_LIBRARY_DELETED .send_event (
681737 content_library = ContentLibraryData (
@@ -709,6 +765,7 @@ def get_library_components(
709765 """
710766 lib = ContentLibrary .objects .get_by_key (library_key ) # type: ignore[attr-defined]
711767 learning_package = lib .learning_package
768+ assert learning_package is not None
712769 components = authoring_api .get_components (
713770 learning_package .id ,
714771 draft = True ,
@@ -860,7 +917,8 @@ def validate_can_add_block_to_library(
860917 content_library = ContentLibrary .objects .get_by_key (library_key ) # type: ignore[attr-defined]
861918
862919 # If adding a component would take us over our max, return an error.
863- component_count = authoring_api .get_all_drafts (content_library .learning_package .id ).count ()
920+ assert content_library .learning_package_id is not None
921+ component_count = authoring_api .get_all_drafts (content_library .learning_package_id ).count ()
864922 if component_count + 1 > settings .MAX_BLOCKS_PER_CONTENT_LIBRARY :
865923 raise BlockLimitReachedError (
866924 _ ("Library cannot have more than {} Components" ).format (
@@ -1356,7 +1414,7 @@ def publish_changes(library_key: LibraryLocatorV2, user_id: int | None = None):
13561414 Publish all pending changes to the specified library.
13571415 """
13581416 learning_package = ContentLibrary .objects .get_by_key (library_key ).learning_package
1359-
1417+ assert learning_package is not None # shouldn't happen but it's technically possible.
13601418 authoring_api .publish_all_drafts (learning_package .id , published_by = user_id )
13611419
13621420 CONTENT_LIBRARY_UPDATED .send_event (
@@ -1398,6 +1456,7 @@ def revert_changes(library_key: LibraryLocatorV2) -> None:
13981456 last published version.
13991457 """
14001458 learning_package = ContentLibrary .objects .get_by_key (library_key ).learning_package
1459+ assert learning_package is not None # shouldn't happen but it's technically possible.
14011460 authoring_api .reset_drafts_to_published (learning_package .id )
14021461
14031462 CONTENT_LIBRARY_UPDATED .send_event (
@@ -1652,6 +1711,7 @@ def get_library_collection_from_usage_key(
16521711 library_key = collection_usage_key .library_key
16531712 collection_key = collection_usage_key .collection_id
16541713 content_library = ContentLibrary .objects .get_by_key (library_key ) # type: ignore[attr-defined]
1714+ assert content_library .learning_package_id is not None # shouldn't happen but it's technically possible.
16551715 try :
16561716 return authoring_api .get_collection (
16571717 content_library .learning_package_id ,
0 commit comments