diff --git a/cms/auth_backends.py b/cms/auth_backends.py new file mode 100644 index 00000000..d903e8ad --- /dev/null +++ b/cms/auth_backends.py @@ -0,0 +1,10 @@ +from django.conf import settings +from django.contrib.auth.backends import ModelBackend + + +class ApprovalBackend(ModelBackend): + def user_can_authenticate(self, user): + can_authenticate = super().user_can_authenticate(user) + if can_authenticate and settings.USERS_NEEDS_TO_BE_APPROVED and not user.is_superuser: + return getattr(user, 'is_approved', False) + return can_authenticate diff --git a/cms/middleware.py b/cms/middleware.py new file mode 100644 index 00000000..c4089720 --- /dev/null +++ b/cms/middleware.py @@ -0,0 +1,23 @@ +from django.conf import settings +from django.http import JsonResponse +from django.shortcuts import redirect +from django.urls import reverse + + +class ApprovalMiddleware: + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + if settings.USERS_NEEDS_TO_BE_APPROVED and request.user.is_authenticated and not request.user.is_superuser and not getattr(request.user, 'is_approved', False): + allowed_paths = [ + reverse('approval_required'), + reverse('account_logout'), + ] + if request.path not in allowed_paths: + if request.path.startswith('/api/'): + return JsonResponse({'detail': 'User account not approved.'}, status=403) + return redirect('approval_required') + + response = self.get_response(request) + return response diff --git a/cms/settings.py b/cms/settings.py index c28ed8da..9f61e32d 100644 --- a/cms/settings.py +++ b/cms/settings.py @@ -128,6 +128,10 @@ USERS_CAN_SELF_REGISTER = True RESTRICTED_DOMAINS_FOR_USER_REGISTRATION = ["xxx.com", "emaildomainwhatever.com"] +# by default users do not need to be approved. If this is set to True, then new users +# will have to be approved before they can login successfully +USERS_NEEDS_TO_BE_APPROVED = False + # Comma separated list of domains: ["organization.com", "private.organization.com", "org2.com"] # Empty list disables. ALLOWED_DOMAINS_FOR_USER_REGISTRATION = [] @@ -501,6 +505,10 @@ ALLOW_CUSTOM_MEDIA_URLS = False # Whether to allow anonymous users to list all users ALLOW_ANONYMOUS_USER_LISTING = True +# Who can see the members page +# valid choices are all, editors, admins +CAN_SEE_MEMBERS_PAGE = "all" + # Maximum number of media a user can upload NUMBER_OF_MEDIA_USER_CAN_UPLOAD = 100 @@ -517,6 +525,9 @@ USER_CAN_TRANSCRIBE_VIDEO = True # Whisper transcribe options - https://github.com/openai/whisper WHISPER_MODEL = "base" +# show a custom text in the sidebar footer, otherwise the default will be shown if this is empty +SIDEBAR_FOOTER_TEXT = "" + try: # keep a local_settings.py file for local overrides from .local_settings import * # noqa @@ -558,3 +569,12 @@ except ImportError: if GLOBAL_LOGIN_REQUIRED: auth_index = MIDDLEWARE.index("django.contrib.auth.middleware.AuthenticationMiddleware") MIDDLEWARE.insert(auth_index + 1, "django.contrib.auth.middleware.LoginRequiredMiddleware") + + +if USERS_NEEDS_TO_BE_APPROVED: + AUTHENTICATION_BACKENDS = ( + 'cms.auth_backends.ApprovalBackend', + 'allauth.account.auth_backends.AuthenticationBackend', + ) + auth_index = MIDDLEWARE.index("django.contrib.auth.middleware.AuthenticationMiddleware") + MIDDLEWARE.insert(auth_index + 1, "cms.middleware.ApprovalMiddleware") diff --git a/cms/version.py b/cms/version.py index 4877e36d..03ef4fc0 100644 --- a/cms/version.py +++ b/cms/version.py @@ -1 +1 @@ -VERSION = "6.5.2" +VERSION = "6.6.0" diff --git a/docs/admins_docs.md b/docs/admins_docs.md index 130c4701..3fd931d9 100644 --- a/docs/admins_docs.md +++ b/docs/admins_docs.md @@ -519,6 +519,20 @@ ALLOW_ANONYMOUS_USER_LISTING = False When set to False, only logged-in users will be able to access the user listing API endpoint. +### 5.27 Control who can see the members page + +By default `CAN_SEE_MEMBERS_PAGE = "all"` means that all registered users can see the members page. Other valid options are: + +- **editors**, only MediaCMS editors can view the page +- **admins**, only MediaCMS admins can view the page + + +### 5.28 Require user approval on registration + +By default, users do not require approval, so they can login immediately after registration (if registration is open). However, if the parameter `USERS_NEEDS_TO_BE_APPROVED` is set to `True`, they will first have to have their accounts approved by an administrator before they can successfully sign in. +Administrators can approve users through the following ways: 1. through Django administration, 2. through the users management page, 3. through editing the profile page directly. In all cases, set 'Is approved' to True. + + ## 6. Manage pages to be written diff --git a/files/context_processors.py b/files/context_processors.py index a6c822f7..fcd0be89 100644 --- a/files/context_processors.py +++ b/files/context_processors.py @@ -26,10 +26,22 @@ def stuff(request): ret["UPLOAD_MAX_SIZE"] = settings.UPLOAD_MAX_SIZE ret["UPLOAD_MAX_FILES_NUMBER"] = settings.UPLOAD_MAX_FILES_NUMBER ret["PRE_UPLOAD_MEDIA_MESSAGE"] = settings.PRE_UPLOAD_MEDIA_MESSAGE + ret["SIDEBAR_FOOTER_TEXT"] = settings.SIDEBAR_FOOTER_TEXT ret["POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY"] = settings.POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY ret["IS_MEDIACMS_ADMIN"] = request.user.is_superuser ret["IS_MEDIACMS_EDITOR"] = is_mediacms_editor(request.user) ret["IS_MEDIACMS_MANAGER"] = is_mediacms_manager(request.user) + ret["USERS_NEEDS_TO_BE_APPROVED"] = settings.USERS_NEEDS_TO_BE_APPROVED + + can_see_members_page = False + if request.user.is_authenticated: + if settings.CAN_SEE_MEMBERS_PAGE == "all": + can_see_members_page = True + elif settings.CAN_SEE_MEMBERS_PAGE == "editors" and is_mediacms_editor(request.user): + can_see_members_page = True + elif settings.CAN_SEE_MEMBERS_PAGE == "admins" and request.user.is_superuser: + can_see_members_page = True + ret["CAN_SEE_MEMBERS_PAGE"] = can_see_members_page ret["ALLOW_RATINGS"] = settings.ALLOW_RATINGS ret["ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY"] = settings.ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY ret["VIDEO_PLAYER_FEATURED_VIDEO_ON_INDEX_PAGE"] = settings.VIDEO_PLAYER_FEATURED_VIDEO_ON_INDEX_PAGE diff --git a/files/management_views.py b/files/management_views.py index 61959ee2..8339ba1f 100644 --- a/files/management_views.py +++ b/files/management_views.py @@ -1,3 +1,5 @@ +from django.conf import settings +from django.db.models import Q from drf_yasg import openapi as openapi from drf_yasg.utils import swagger_auto_schema from rest_framework import status @@ -219,6 +221,13 @@ class UserList(APIView): elif role == "editor": qs = qs.filter(is_editor=True) + if settings.USERS_NEEDS_TO_BE_APPROVED: + is_approved = request.GET.get("is_approved") + if is_approved == "true": + qs = qs.filter(is_approved=True) + elif is_approved == "false": + qs = qs.filter(Q(is_approved=False) | Q(is_approved__isnull=True)) + users = qs.order_by(f"{ordering}{sort_by}") paginator = pagination_class() diff --git a/files/urls.py b/files/urls.py index ab05e848..3486dc0d 100644 --- a/files/urls.py +++ b/files/urls.py @@ -110,6 +110,9 @@ urlpatterns = [ re_path(r"^manage/users$", views.manage_users, name="manage_users"), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) +if settings.USERS_NEEDS_TO_BE_APPROVED: + urlpatterns.append(re_path(r"^approval_required", views.approval_required, name="approval_required")) + if hasattr(settings, "USE_SAML") and settings.USE_SAML: urlpatterns.append(re_path(r"^saml/metadata", views.saml_metadata, name="saml-metadata")) diff --git a/files/views/__init__.py b/files/views/__init__.py index 9d8c1231..e61a3e39 100644 --- a/files/views/__init__.py +++ b/files/views/__init__.py @@ -1,4 +1,5 @@ # Import all views for backward compatibility + from .auth import custom_login_view, saml_metadata # noqa: F401 from .categories import CategoryList, TagList # noqa: F401 from .comments import CommentDetail, CommentList # noqa: F401 @@ -10,6 +11,7 @@ from .media import MediaList # noqa: F401 from .media import MediaSearch # noqa: F401 from .pages import about # noqa: F401 from .pages import add_subtitle # noqa: F401 +from .pages import approval_required # noqa: F401 from .pages import categories # noqa: F401 from .pages import contact # noqa: F401 from .pages import edit_chapters # noqa: F401 diff --git a/files/views/pages.py b/files/views/pages.py index 2f9886c4..a0ada0e8 100644 --- a/files/views/pages.py +++ b/files/views/pages.py @@ -54,6 +54,11 @@ def about(request): return render(request, "cms/about.html", context) +def approval_required(request): + """User needs approval view""" + return render(request, "cms/user_needs_approval.html", {}) + + def setlanguage(request): """Set Language view""" @@ -517,6 +522,12 @@ def manage_comments(request): def members(request): """List members view""" + if settings.CAN_SEE_MEMBERS_PAGE == "editors" and not is_mediacms_editor(request.user): + return HttpResponseRedirect("/") + + if settings.CAN_SEE_MEMBERS_PAGE == "admins" and not request.user.is_superuser: + return HttpResponseRedirect("/") + context = {} return render(request, "cms/members.html", context) diff --git a/frontend/src/static/js/components/management-table/ManageItem/ManageUsersItem.js b/frontend/src/static/js/components/management-table/ManageItem/ManageUsersItem.js index 6ca8218e..0c339ddf 100644 --- a/frontend/src/static/js/components/management-table/ManageItem/ManageUsersItem.js +++ b/frontend/src/static/js/components/management-table/ManageItem/ManageUsersItem.js @@ -1,7 +1,8 @@ import React, { useRef, useState, useEffect, useCallback } from 'react'; import PropTypes from 'prop-types'; -import { usePopup } from '../../../utils/hooks/'; +import { usePopup, useUser } from '../../../utils/hooks/'; import { PageStore } from '../../../utils/stores/'; +import { csrfToken } from '../../../utils/helpers/'; import { PopupMain } from '../../_shared'; import { MaterialIcon } from '../../_shared/material-icon/MaterialIcon.jsx'; import { ManageItemDate } from './ManageMediaItem'; @@ -38,34 +39,86 @@ function ManageItemUsername(props) { return N/A; } -function ManageItemCommentActions(props) { - const [popupContentRef, PopupContent, PopupTrigger] = usePopup(); - const [isOpenPopup, setIsOpenPopup] = useState(false); +function ManageUsersItemActions(props) { + const { userCan } = useUser(); + const [deletePopupRef, DeletePopupContent, DeletePopupTrigger] = usePopup(); + const [passwordPopupRef, PasswordPopupContent, PasswordPopupTrigger] = usePopup(); + const [approvePopupRef, ApprovePopupContent, ApprovePopupTrigger] = usePopup(); - function onPopupShow() { - setIsOpenPopup(true); - } + const [newPassword, setNewPassword] = useState(''); - function onPopupHide() { - setIsOpenPopup(false); - } + const [isDeleteOpen, setDeleteOpen] = useState(false); + const [isPasswordOpen, setPasswordOpen] = useState(false); + const [isApproveOpen, setApproveOpen] = useState(false); - function onCancel() { - popupContentRef.current.tryToHide(); - if ('function' === typeof props.onCancel) { - props.onCancel(); - } - } - - function onProceed() { - popupContentRef.current.tryToHide(); + function onProceedDelete() { + deletePopupRef.current.tryToHide(); if ('function' === typeof props.onProceed) { props.onProceed(); } } + function onCancelDelete() { + deletePopupRef.current.tryToHide(); + } + + function handlePasswordChangeSubmit(e) { + e.preventDefault(); + props.setMessage({ type: '', text: '' }); + + const formData = new FormData(); + formData.append('action', 'change_password'); + formData.append('password', newPassword); + + fetch(`/api/v1/users/${props.username}`, { + method: 'PUT', + body: formData, + headers: { 'X-CSRFToken': csrfToken() }, + }) + .then((res) => { + if (res.ok) { + return res.json(); + } + return res.json().then((data) => { + throw new Error(data.detail || 'Failed to change password.'); + }); + }) + .then(() => { + sessionStorage.setItem('user-management-message', JSON.stringify({ type: 'success', text: 'Password changed successfully.' })); + window.location.reload(); + }) + .catch((err) => { + props.setMessage({ type: 'error', text: err.message }); + }); + } + + function handleApproveUser() { + props.setMessage({ type: '', text: '' }); + const formData = new FormData(); + formData.append('action', 'approve_user'); + + fetch(`/api/v1/users/${props.username}`, { + method: 'PUT', + body: formData, + headers: { 'X-CSRFToken': csrfToken() }, + }) + .then((res) => { + if (res.ok) { + return res.json(); + } + return res.json().then((data) => { + throw new Error(data.detail || 'Failed to approve user.'); + }); + }) + .then(() => { + sessionStorage.setItem('user-management-message', JSON.stringify({ type: 'success', text: 'User approved successfully.' })); + window.location.reload(); + }) + .catch((err) => { + props.setMessage({ type: 'error', text: err.message }); + }); + } const positionState = { updating: false, pending: 0 }; - const onWindowResize = useCallback(function () { if (positionState.updating) { positionState.pending = positionState.pending + 1; @@ -98,6 +151,8 @@ function ManageItemCommentActions(props) { } }, []); + const isOpenPopup = isDeleteOpen || isPasswordOpen || isApproveOpen; + useEffect(() => { if (isOpenPopup) { PageStore.on('window_scroll', onWindowResize); @@ -111,11 +166,94 @@ function ManageItemCommentActions(props) { return (
{Object.defineProperty(exports,"__esModule",{value:!0}),exports.build=exports.RenderTask=exports.PDFWorkerUtil=exports.PDFWorker=exports.PDFPageProxy=exports.PDFDocumentProxy=exports.PDFDocumentLoadingTask=exports.PDFDataRangeTransport=exports.LoopbackPort=exports.DefaultStandardFontDataFactory=exports.DefaultCanvasFactory=exports.DefaultCMapReaderFactory=void 0,exports.getDocument=getDocument,exports.version=void 0;var _util=__w_pdfjs_require__(1),_annotation_storage=__w_pdfjs_require__(3),_display_utils=__w_pdfjs_require__(6),_font_loader=__w_pdfjs_require__(9),_canvas=__w_pdfjs_require__(11),_worker_options=__w_pdfjs_require__(14),_is_node=__w_pdfjs_require__(10),_message_handler=__w_pdfjs_require__(15),_metadata=__w_pdfjs_require__(16),_optional_content_config=__w_pdfjs_require__(17),_transport_stream=__w_pdfjs_require__(18),_xfa_text=__w_pdfjs_require__(19);const DEFAULT_RANGE_CHUNK_SIZE=65536,RENDERING_CANCELLED_TIMEOUT=100;let DefaultCanvasFactory=_display_utils.DOMCanvasFactory;exports.DefaultCanvasFactory=DefaultCanvasFactory;let DefaultCMapReaderFactory=_display_utils.DOMCMapReaderFactory;exports.DefaultCMapReaderFactory=DefaultCMapReaderFactory;let DefaultStandardFontDataFactory=_display_utils.DOMStandardFontDataFactory,createPDFNetworkStream;if(exports.DefaultStandardFontDataFactory=DefaultStandardFontDataFactory,_is_node.isNodeJS){const{NodeCanvasFactory:e,NodeCMapReaderFactory:t,NodeStandardFontDataFactory:n}=__w_pdfjs_require__(20);exports.DefaultCanvasFactory=DefaultCanvasFactory=e,exports.DefaultCMapReaderFactory=DefaultCMapReaderFactory=t,exports.DefaultStandardFontDataFactory=DefaultStandardFontDataFactory=n}if(_is_node.isNodeJS){const{PDFNodeStream:e}=__w_pdfjs_require__(21);createPDFNetworkStream=t=>new e(t)}else{const{PDFNetworkStream:e}=__w_pdfjs_require__(24),{PDFFetchStream:t}=__w_pdfjs_require__(25);createPDFNetworkStream=n=>(0,_display_utils.isValidFetchUrl)(n.url)?new t(n):new e(n)}function getDocument(e){if("string"==typeof e||e instanceof URL)e={url:e};else if((0,_util.isArrayBuffer)(e))e={data:e};else if(e instanceof PDFDataRangeTransport)(0,_display_utils.deprecated)("`PDFDataRangeTransport`-instance, please use a parameter object with `range`-property instead."),e={range:e};else if("object"!=typeof e)throw new Error("Invalid parameter in getDocument, need either string, URL, TypedArray, or parameter object.");if(!e.url&&!e.data&&!e.range)throw new Error("Invalid parameter object: need either .data, .range or .url");const t=new PDFDocumentLoadingTask,n=e.url?getUrlProp(e.url):null,r=e.data?getDataProp(e.data):null,i=e.httpHeaders||null,o=!0===e.withCredentials,a=e.password??null,s=e.range instanceof PDFDataRangeTransport?e.range:null,l=Number.isInteger(e.rangeChunkSize)&&e.rangeChunkSize>0?e.rangeChunkSize:DEFAULT_RANGE_CHUNK_SIZE;let c=e.worker instanceof PDFWorker?e.worker:null;const u=e.verbosity,d="string"!=typeof e.docBaseUrl||(0,_display_utils.isDataScheme)(e.docBaseUrl)?null:e.docBaseUrl,h="string"==typeof e.cMapUrl?e.cMapUrl:null,p=!1!==e.cMapPacked,f=e.CMapReaderFactory||DefaultCMapReaderFactory,m="string"==typeof e.standardFontDataUrl?e.standardFontDataUrl:null,g=e.StandardFontDataFactory||DefaultStandardFontDataFactory,v=!0!==e.stopAtErrors,y=Number.isInteger(e.maxImageSize)&&e.maxImageSize>-1?e.maxImageSize:-1,b=!1!==e.isEvalSupported,E="boolean"==typeof e.isOffscreenCanvasSupported?e.isOffscreenCanvasSupported:!_is_node.isNodeJS,S="boolean"==typeof e.disableFontFace?e.disableFontFace:_is_node.isNodeJS,w=!0===e.fontExtraProperties,_=!0===e.enableXfa,k=e.ownerDocument||globalThis.document,C=!0===e.disableRange,P=!0===e.disableStream,x=!0===e.disableAutoFetch,A=!0===e.pdfBug,M=s?s.length:e.length??NaN,T="boolean"==typeof e.useSystemFonts?e.useSystemFonts:!_is_node.isNodeJS&&!S,R="boolean"==typeof e.useWorkerFetch?e.useWorkerFetch:f===_display_utils.DOMCMapReaderFactory&&g===_display_utils.DOMStandardFontDataFactory&&(0,_display_utils.isValidFetchUrl)(h,document.baseURI)&&(0,_display_utils.isValidFetchUrl)(m,document.baseURI);(0,_util.setVerbosityLevel)(u);const O=R?null:{cMapReaderFactory:new f({baseUrl:h,isCompressed:p}),standardFontDataFactory:new g({baseUrl:m})};if(!c){const e={verbosity:u,port:_worker_options.GlobalWorkerOptions.workerPort};c=e.port?PDFWorker.fromPort(e):new PDFWorker(e),t._worker=c}const I=t.docId,D={docId:I,apiVersion:"3.4.120",data:r,password:a,disableAutoFetch:x,rangeChunkSize:l,length:M,docBaseUrl:d,enableXfa:_,evaluatorOptions:{maxImageSize:y,disableFontFace:S,ignoreErrors:v,isEvalSupported:b,isOffscreenCanvasSupported:E,fontExtraProperties:w,useSystemFonts:T,cMapUrl:R?h:null,standardFontDataUrl:R?m:null}},L={ignoreErrors:v,isEvalSupported:b,disableFontFace:S,fontExtraProperties:w,enableXfa:_,ownerDocument:k,disableAutoFetch:x,pdfBug:A,styleElement:null};return c.promise.then((function(){if(t.destroyed)throw new Error("Loading aborted");const e=_fetchDocument(c,D),a=new Promise((function(e){let t;s?t=new _transport_stream.PDFDataTransportStream({length:M,initialData:s.initialData,progressiveDone:s.progressiveDone,contentDispositionFilename:s.contentDispositionFilename,disableRange:C,disableStream:P},s):r||(t=createPDFNetworkStream({url:n,length:M,httpHeaders:i,withCredentials:o,rangeChunkSize:l,disableRange:C,disableStream:P})),e(t)}));return Promise.all([e,a]).then((function([e,n]){if(t.destroyed)throw new Error("Loading aborted");const r=new _message_handler.MessageHandler(I,e,c.port),i=new WorkerTransport(r,t,n,L,O);t._transport=i,r.send("Ready",null)}))})).catch(t._capability.reject),t}async function _fetchDocument(e,t){if(e.destroyed)throw new Error("Worker was destroyed");const n=await e.messageHandler.sendWithPromise("GetDocRequest",t,t.data?[t.data.buffer]:null);if(e.destroyed)throw new Error("Worker was destroyed");return n}function getUrlProp(e){if(e instanceof URL)return e.href;try{return new URL(e,window.location).href}catch(t){if(_is_node.isNodeJS&&"string"==typeof e)return e}throw new Error("Invalid PDF url data: either string or URL-object is expected in the url property.")}function getDataProp(e){if(_is_node.isNodeJS&&void 0!==Buffer&&e instanceof Buffer)return(0,_display_utils.deprecated)("Please provide binary data as `Uint8Array`, rather than `Buffer`."),new Uint8Array(e);if(e instanceof Uint8Array&&e.byteLength===e.buffer.byteLength)return e;if("string"==typeof e)return(0,_util.stringToBytes)(e);if("object"==typeof e&&!isNaN(e?.length)||(0,_util.isArrayBuffer)(e))return new Uint8Array(e);throw new Error("Invalid PDF binary data: either TypedArray, string, or array-like object is expected in the data property.")}class PDFDocumentLoadingTask{static#e=0;#t=null;constructor(){this._capability=(0,_util.createPromiseCapability)(),this._transport=null,this._worker=null,this.docId="d"+PDFDocumentLoadingTask.#e++,this.destroyed=!1,this.onPassword=null,this.onProgress=null}get onUnsupportedFeature(){return this.#t}set onUnsupportedFeature(e){(0,_display_utils.deprecated)("The PDFDocumentLoadingTask onUnsupportedFeature property will be removed in the future."),this.#t=e}get promise(){return this._capability.promise}async destroy(){this.destroyed=!0,await(this._transport?.destroy()),this._transport=null,this._worker&&(this._worker.destroy(),this._worker=null)}}exports.PDFDocumentLoadingTask=PDFDocumentLoadingTask;class PDFDataRangeTransport{constructor(e,t,n=!1,r=null){this.length=e,this.initialData=t,this.progressiveDone=n,this.contentDispositionFilename=r,this._rangeListeners=[],this._progressListeners=[],this._progressiveReadListeners=[],this._progressiveDoneListeners=[],this._readyCapability=(0,_util.createPromiseCapability)()}addRangeListener(e){this._rangeListeners.push(e)}addProgressListener(e){this._progressListeners.push(e)}addProgressiveReadListener(e){this._progressiveReadListeners.push(e)}addProgressiveDoneListener(e){this._progressiveDoneListeners.push(e)}onDataRange(e,t){for(const n of this._rangeListeners)n(e,t)}onDataProgress(e,t){this._readyCapability.promise.then((()=>{for(const n of this._progressListeners)n(e,t)}))}onDataProgressiveRead(e){this._readyCapability.promise.then((()=>{for(const t of this._progressiveReadListeners)t(e)}))}onDataProgressiveDone(){this._readyCapability.promise.then((()=>{for(const e of this._progressiveDoneListeners)e()}))}transportReady(){this._readyCapability.resolve()}requestDataRange(e,t){(0,_util.unreachable)("Abstract method PDFDataRangeTransport.requestDataRange")}abort(){}}exports.PDFDataRangeTransport=PDFDataRangeTransport;class PDFDocumentProxy{constructor(e,t){this._pdfInfo=e,this._transport=t}get annotationStorage(){return this._transport.annotationStorage}get numPages(){return this._pdfInfo.numPages}get fingerprints(){return this._pdfInfo.fingerprints}get isPureXfa(){return(0,_util.shadow)(this,"isPureXfa",!!this._transport._htmlForXfa)}get allXfaHtml(){return this._transport._htmlForXfa}getPage(e){return this._transport.getPage(e)}getPageIndex(e){return this._transport.getPageIndex(e)}getDestinations(){return this._transport.getDestinations()}getDestination(e){return this._transport.getDestination(e)}getPageLabels(){return this._transport.getPageLabels()}getPageLayout(){return this._transport.getPageLayout()}getPageMode(){return this._transport.getPageMode()}getViewerPreferences(){return this._transport.getViewerPreferences()}getOpenAction(){return this._transport.getOpenAction()}getAttachments(){return this._transport.getAttachments()}getJavaScript(){return this._transport.getJavaScript()}getJSActions(){return this._transport.getDocJSActions()}getOutline(){return this._transport.getOutline()}getOptionalContentConfig(){return this._transport.getOptionalContentConfig()}getPermissions(){return this._transport.getPermissions()}getMetadata(){return this._transport.getMetadata()}getMarkInfo(){return this._transport.getMarkInfo()}getData(){return this._transport.getData()}saveDocument(){return this._transport.saveDocument()}getDownloadInfo(){return this._transport.downloadInfoCapability.promise}cleanup(e=!1){return this._transport.startCleanup(e||this.isPureXfa)}destroy(){return this.loadingTask.destroy()}get loadingParams(){return this._transport.loadingParams}get loadingTask(){return this._transport.loadingTask}getFieldObjects(){return this._transport.getFieldObjects()}hasJSActions(){return this._transport.hasJSActions()}getCalculationOrderIds(){return this._transport.getCalculationOrderIds()}}exports.PDFDocumentProxy=PDFDocumentProxy;class PDFPageProxy{constructor(e,t,n,r,i=!1){this._pageIndex=e,this._pageInfo=t,this._ownerDocument=r,this._transport=n,this._stats=i?new _display_utils.StatTimer:null,this._pdfBug=i,this.commonObjs=n.commonObjs,this.objs=new PDFObjects,this.cleanupAfterRender=!1,this.pendingCleanup=!1,this._intentStates=new Map,this.destroyed=!1}get pageNumber(){return this._pageIndex+1}get rotate(){return this._pageInfo.rotate}get ref(){return this._pageInfo.ref}get userUnit(){return this._pageInfo.userUnit}get view(){return this._pageInfo.view}getViewport({scale:e,rotation:t=this.rotate,offsetX:n=0,offsetY:r=0,dontFlip:i=!1}={}){return new _display_utils.PageViewport({viewBox:this.view,scale:e,rotation:t,offsetX:n,offsetY:r,dontFlip:i})}getAnnotations({intent:e="display"}={}){const t=this._transport.getRenderingIntent(e);return this._transport.getAnnotations(this._pageIndex,t.renderingIntent)}getJSActions(){return this._transport.getPageJSActions(this._pageIndex)}get isPureXfa(){return(0,_util.shadow)(this,"isPureXfa",!!this._transport._htmlForXfa)}async getXfa(){return this._transport._htmlForXfa?.children[this._pageIndex]||null}render({canvasContext:e,viewport:t,intent:n="display",annotationMode:r=_util.AnnotationMode.ENABLE,transform:i=null,canvasFactory:o=null,background:a=null,optionalContentConfigPromise:s=null,annotationCanvasMap:l=null,pageColors:c=null,printAnnotationStorage:u=null}){this._stats?.time("Overall");const d=this._transport.getRenderingIntent(n,r,u);this.pendingCleanup=!1,s||(s=this._transport.getOptionalContentConfig());let h=this._intentStates.get(d.cacheKey);h||(h=Object.create(null),this._intentStates.set(d.cacheKey,h)),h.streamReaderCancelTimeout&&(clearTimeout(h.streamReaderCancelTimeout),h.streamReaderCancelTimeout=null);const p=o||new DefaultCanvasFactory({ownerDocument:this._ownerDocument}),f=!!(d.renderingIntent&_util.RenderingIntentFlag.PRINT);h.displayReadyCapability||(h.displayReadyCapability=(0,_util.createPromiseCapability)(),h.operatorList={fnArray:[],argsArray:[],lastChunk:!1,separateAnnots:null},this._stats?.time("Page Request"),this._pumpOperatorList(d));const m=e=>{h.renderTasks.delete(g),(this.cleanupAfterRender||f)&&(this.pendingCleanup=!0),this._tryCleanup(),e?(g.capability.reject(e),this._abortOperatorList({intentState:h,reason:e instanceof Error?e:new Error(e)})):g.capability.resolve(),this._stats?.timeEnd("Rendering"),this._stats?.timeEnd("Overall")},g=new InternalRenderTask({callback:m,params:{canvasContext:e,viewport:t,transform:i,background:a},objs:this.objs,commonObjs:this.commonObjs,annotationCanvasMap:l,operatorList:h.operatorList,pageIndex:this._pageIndex,canvasFactory:p,useRequestAnimationFrame:!f,pdfBug:this._pdfBug,pageColors:c});(h.renderTasks||=new Set).add(g);const v=g.task;return Promise.all([h.displayReadyCapability.promise,s]).then((([e,t])=>{this.pendingCleanup?m():(this._stats?.time("Rendering"),g.initializeGraphics({transparency:e,optionalContentConfig:t}),g.operatorListChanged())})).catch(m),v}getOperatorList({intent:e="display",annotationMode:t=_util.AnnotationMode.ENABLE,printAnnotationStorage:n=null}={}){const r=this._transport.getRenderingIntent(e,t,n,!0);let i,o=this._intentStates.get(r.cacheKey);return o||(o=Object.create(null),this._intentStates.set(r.cacheKey,o)),o.opListReadCapability||(i=Object.create(null),i.operatorListChanged=function(){o.operatorList.lastChunk&&(o.opListReadCapability.resolve(o.operatorList),o.renderTasks.delete(i))},o.opListReadCapability=(0,_util.createPromiseCapability)(),(o.renderTasks||=new Set).add(i),o.operatorList={fnArray:[],argsArray:[],lastChunk:!1,separateAnnots:null},this._stats?.time("Page Request"),this._pumpOperatorList(r)),o.opListReadCapability.promise}streamTextContent({disableCombineTextItems:e=!1,includeMarkedContent:t=!1}={}){return this._transport.messageHandler.sendWithStream("GetTextContent",{pageIndex:this._pageIndex,combineTextItems:!0!==e,includeMarkedContent:!0===t},{highWaterMark:100,size(e){return e.items.length}})}getTextContent(e={}){if(this._transport._htmlForXfa)return this.getXfa().then((e=>_xfa_text.XfaText.textContent(e)));const t=this.streamTextContent(e);return new Promise((function(e,n){const r=t.getReader(),i={items:[],styles:Object.create(null)};!function t(){r.read().then((function({value:n,done:r}){r?e(i):(Object.assign(i.styles,n.styles),i.items.push(...n.items),t())}),n)}()}))}getStructTree(){return this._transport.getStructTree(this._pageIndex)}_destroy(){this.destroyed=!0;const e=[];for(const t of this._intentStates.values())if(this._abortOperatorList({intentState:t,reason:new Error("Page was destroyed."),force:!0}),!t.opListReadCapability)for(const n of t.renderTasks)e.push(n.completed),n.cancel();return this.objs.clear(),this.pendingCleanup=!1,Promise.all(e)}cleanup(e=!1){return this.pendingCleanup=!0,this._tryCleanup(e)}_tryCleanup(e=!1){if(!this.pendingCleanup)return!1;for(const{renderTasks:e,operatorList:t}of this._intentStates.values())if(e.size>0||!t.lastChunk)return!1;return this._intentStates.clear(),this.objs.clear(),e&&this._stats&&(this._stats=new _display_utils.StatTimer),this.pendingCleanup=!1,!0}_startRenderPage(e,t){const n=this._intentStates.get(t);n&&(this._stats?.timeEnd("Page Request"),n.displayReadyCapability?.resolve(e))}_renderPageChunk(e,t){for(let n=0,r=e.length;n 0){const e=1e3*s.measureText(o).width/a*l;if(k60?n[0]+(""===t?"":t+"\n ")+" "+e.join(",\n ")+" "+n[1]:n[0]+t+" "+e.join(", ")+" "+n[1]}(c,u,h)):h[0]+u+h[1]}function f(e){return"["+Error.prototype.toString.call(e)+"]"}function m(e,t,n,r,i,o){var a,s,l;if((l=Object.getOwnPropertyDescriptor(t,i)||{value:t[i]}).get?s=l.set?e.stylize("[Getter/Setter]","special"):e.stylize("[Getter]","special"):l.set&&(s=e.stylize("[Setter]","special")),T(r,i)||(a="["+i+"]"),s||(e.seen.indexOf(l.value)<0?(s=y(n)?p(e,l.value,null):p(e,l.value,n-1)).indexOf("\n")>-1&&(s=o?s.split("\n").map((function(e){return" "+e})).join("\n").slice(2):"\n"+s.split("\n").map((function(e){return" "+e})).join("\n")):s=e.stylize("[Circular]","special")),S(a)){if(o&&i.match(/^\d+$/))return s;(a=JSON.stringify(""+i)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(a=a.slice(1,-1),a=e.stylize(a,"name")):(a=a.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),a=e.stylize(a,"string"))}return a+": "+s}function g(e){return Array.isArray(e)}function v(e){return"boolean"==typeof e}function y(e){return null===e}function b(e){return"number"==typeof e}function E(e){return"string"==typeof e}function S(e){return void 0===e}function w(e){return _(e)&&"[object RegExp]"===x(e)}function _(e){return"object"==typeof e&&null!==e}function k(e){return _(e)&&"[object Date]"===x(e)}function C(e){return _(e)&&("[object Error]"===x(e)||e instanceof Error)}function P(e){return"function"==typeof e}function x(e){return Object.prototype.toString.call(e)}function A(e){return e<10?"0"+e.toString(10):e.toString(10)}t.debuglog=function(e){if(e=e.toUpperCase(),!s[e])if(l.test(e)){var n=r.pid;s[e]=function(){var r=t.format.apply(t,arguments);i.error("%s %d: %s",e,n,r)}}else s[e]=function(){};return s[e]},t.inspect=u,u.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]},u.styles={special:"cyan",number:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",date:"magenta",regexp:"red"},t.types=n(7213),t.isArray=g,t.isBoolean=v,t.isNull=y,t.isNullOrUndefined=function(e){return null==e},t.isNumber=b,t.isString=E,t.isSymbol=function(e){return"symbol"==typeof e},t.isUndefined=S,t.isRegExp=w,t.types.isRegExp=w,t.isObject=_,t.isDate=k,t.types.isDate=k,t.isError=C,t.types.isNativeError=C,t.isFunction=P,t.isPrimitive=function(e){return null===e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||"symbol"==typeof e||void 0===e},t.isBuffer=n(6098);var M=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function T(e,t){return Object.prototype.hasOwnProperty.call(e,t)}t.log=function(){var e,n;i.log("%s - %s",(n=[A((e=new Date).getHours()),A(e.getMinutes()),A(e.getSeconds())].join(":"),[e.getDate(),M[e.getMonth()],n].join(" ")),t.format.apply(t,arguments))},t.inherits=n(8365),t._extend=function(e,t){if(!t||!_(t))return e;for(var n=Object.keys(t),r=n.length;r--;)e[n[r]]=t[n[r]];return e};var R="undefined"!=typeof Symbol?Symbol("util.promisify.custom"):void 0;function O(e,t){if(!e){var n=new Error("Promise was rejected with a falsy value");n.reason=e,e=n}return t(e)}t.promisify=function(e){if("function"!=typeof e)throw new TypeError('The "original" argument must be of type Function');if(R&&e[R]){var t;if("function"!=typeof(t=e[R]))throw new TypeError('The "util.promisify.custom" argument must be of type Function');return Object.defineProperty(t,R,{value:t,enumerable:!1,writable:!1,configurable:!0}),t}function t(){for(var t,n,r=new Promise((function(e,r){t=e,n=r})),i=[],o=0;on?98:n,(function(){e(!0)})),Gi(97s&&(n=s-l),o=n;o>=0;o--){let n=!0;for(let r=0;r>>=0,isFinite(n)?(n>>>=0,void 0===r&&(r="utf8")):(r=n,n=void 0)}const i=this.length-t;if((void 0===n||n>i)&&(n=i),e.length>0&&(n<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");let o=!1;for(;;)switch(r){case"hex":return S(this,e,t,n);case"utf8":case"utf-8":return w(this,e,t,n);case"ascii":case"latin1":case"binary":return _(this,e,t,n);case"base64":return k(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return C(this,e,t,n);default:if(o)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),o=!0}},c.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};const A=4096;function M(e,t,n){let r="";n=Math.min(e.length,n);for(let i=t;i>1,u=-7,d=n?i-1:0,p=n?-1:1,h=e[t+d];for(d+=p,a=h&(1<<-u)-1,h>>=-u,u+=s;u>0;a=256*a+e[t+d],d+=p,u-=8);for(o=a&(1<<-u)-1,a>>=-u,u+=r;u>0;o=256*o+e[t+d],d+=p,u-=8);if(0===a)a=1-c;else{if(a===l)return o?NaN:1/0*(h?-1:1);o+=Math.pow(2,r),a-=c}return(h?-1:1)*o*Math.pow(2,a-r)},t.write=function(e,t,n,r,i,a){var o,s,l,c=8*a-i-1,u=(1<