import type { UpdateSettingsData, UpdateBlockDescriptionData, UpdateBlockInfoData, UpdateBlockSocialData, UpdateBlockLocalityData, GetNewsData, GetGalleryData, GlobalAutocompleteCostumData, CostumEventRequestActorsData, CostumEventRequestSubeventsData, CostumEventRequestDatesData, CostumEventRequestElementEventData, CostumEventRequestCategoriesData, CostumEventRequestEventData, CostumEventRequestLinkTlToEventData, CostumEventRequestLoadContextTagData, GetOrganizationsAdminData, GetOrganizationsNoAdminData, GetProjectsAdminData, GetProjectsNoAdminData, GetPoisAdminData, GetPoisNoAdminData, GetSubscribersData, GetBadgesData, CoformAnswersSearchData, SearchMemberAutocompleteData, GetEventsData, SearchEventsCostumData, CostumFilterCoformData, CostumFilterCoformByPathData, GetCountriesData, SearchZonesData, CoformAnswersByFormsData, FundingEnvelopeData, CoremuOperationData, UpdatePathValueData } from "./EndpointApi.types.js"; import type { CostumRuntimeContext } from "../costum/runtime.js"; import type { TransformsMap } from "../types/entities.js"; import type { CountryItem } from "./serverDataType/Country.js"; import type { ZoneItemNormalized } from "./serverDataType/Zone.js"; /** * Types pour les méthodes d'entité (organization, project, poi, event, badge, news) * Permettent soit de récupérer une entité existante (GET), soit de créer une nouvelle instance (CREATE) */ type OrganizationInput = { id: string; } | { slug: string; } | Record; type ProjectInput = { id: string; } | { slug: string; } | Record; type PoiInput = { id: string; } | { slug: string; } | Record; type EventInput = { id: string; } | { slug: string; } | Record; type ClassifiedInput = { id: string; } | Record; type BadgeInput = { id: string; } | Record; type NewsInput = { id: string; } | Record; type ActionInput = { id: string; } | Record; type FormInput = { id: string; } | Record; type AnswerInput = { id: string; } | Record; type ApiClient = import("../ApiClient.js").default; type EndpointApi = import("./EndpointApi.js").default; type User = import("./User.js").User; type Organization = import("./Organization.js").Organization; type Project = import("./Project.js").Project; type EventEntity = import("./Event.js").Event; type Poi = import("./Poi.js").Poi; type News = import("./News.js").News; type Badge = import("./Badge.js").Badge; type Comment = import("./Comment.js").Comment; type Answer = import("./Answer.js").Answer; type Form = import("./Form.js").Form; type Classified = import("./Classified.js").Classified; type Action = import("./Action.js").Action; type AnyEntity = User | Organization | Project | Poi | EventEntity | Badge | News | Comment | Answer | Form | Classified | Action; type ParentLike = BaseEntity & { apiClient: ApiClient; userContext?: User | null; }; /** * Variantes d'endpoint disponibles pour `searchCostum`. * Tous les variants doivent accepter le même schéma de requête/réponse que * `GLOBAL_AUTOCOMPLETE_COSTUM` (drop-in remplacement). * * - `default` → `/co2/search/globalautocomplete` * - `navigator-tl` → `/costum/navigator/gettl` * - `admin` → `/co2/search/globalautocompleteadmin/type/{type}/id/{id}/canSee/true` * (SearchNew::searchAdmin — réservé aux admins de l'entité porteuse : `fields` = projection * EXACTE, `preferences` NON strippé → badge/filtre `toBeValidated`, tri serveur via `sort`. * pathParams {type,id} posés automatiquement depuis l'entité appelante — cf. searchCostum.) */ export type SearchCostumVariant = "default" | "navigator-tl" | "admin"; /** * Types de coercion backend supportés par `UPDATE_PATH_VALUE.setType`. * * Référence côté backend : `string_set_type` (PHP) accepte ces 5 valeurs + * tout autre string (passthrough — pas de coercion). Le `& Record` * garde l'autocomplete des literals tout en acceptant des strings custom. * * - `"int"` → `intval()` (PHP) * - `"float"` → `floatval()` * - `"boolean"` / `"bool"` → `FILTER_VALIDATE_BOOLEAN` (alias) * - `"isoDate"` → parse multi-format → `MongoDate` * Formats acceptés : ISO 8601 natif, `m-d-Y`, `d-m-Y`, `{sec, usec}` legacy, * string `"now"` (date actuelle), slashes auto-convertis en tirets. * - `"timestamp"` → parse → Unix seconds (`int`) */ export type SetTypeValue = "int" | "float" | "boolean" | "bool" | "isoDate" | "timestamp" | (string & Record); /** * Magic values reconnues par `UPDATE_PATH_VALUE` côté backend. * Utilisable comme `value` ou `path` selon le cas. */ export declare const UPDATE_PATH_MAGIC: { /** * `value: "updatedTime"` → backend remplace par `time()` Unix seconds courant * (utile pour `lastEditedAt`, audit trail, etc. sans avoir à fournir un timestamp côté client). */ readonly CURRENT_TIME: "updatedTime"; /** * `value: "now"` avec `setType: "isoDate"` → résolu côté backend en `MongoDate(now)`. */ readonly NOW: "now"; /** * `path: "allToRoot"` → la `value` (qui doit être un objet) est exposée à la * racine de l'entité : chaque clé devient un champ racine. Combiner avec * `updatePartial: true` pour un merge avec préservation des autres champs. */ readonly PATH_ALL_TO_ROOT: "allToRoot"; }; type EndpointApiCtor = { new (apiClient: ApiClient): EndpointApi; }; type EndpointApiDep = EndpointApi | EndpointApiCtor; interface Deps { EndpointApi: EndpointApiDep; User?: any; Organization?: any; Project?: any; Poi?: any; Event?: any; Badge?: any; News?: any; Comment?: any; Answer?: any; Form?: any; Classified?: any; Action?: any; } interface BaseEntityConfig { entityTag?: string; /** Contexte costum (résolu par `CostumScope`) — ouvre les champs costum sur draft/extraction/AJV. */ costumCtx?: CostumRuntimeContext; } interface EntityTypeMap { citoyens: User; organizations: Organization; projects: Project; events: EventEntity; poi: Poi; news: News; badges: Badge; comments: Comment; answers: Answer; forms: Form; classifieds: Classified; actions: Action; } type ReadableWithMeta = import("stream").Readable & { path?: string; mimeType?: string; size?: number; }; type UploadInput = File | Blob | Buffer | import("stream").Readable; type ValidatedUpload = File | Buffer | ReadableWithMeta; export type FundingEnvelopeProjectItem = { project?: AnyEntity | object; [k: string]: unknown; }; export type FundingEnvelopeResult = { contextData?: AnyEntity | object; context?: AnyEntity | object; links?: object; form?: object; paymentMethods?: object; nopropProject?: AnyEntity[] | object; projects?: FundingEnvelopeProjectItem[]; userOrga?: AnyEntity[] | object; [k: string]: unknown; }; export type CostumContextFields = { costumSlug: string; contextId: string; contextType: EntityType; costumId: string; costumType: EntityType; sourceKey: string[]; }; export type WithCostumContext = T & CostumContextFields; /** * Exclut les items de FIL (`activityStream`/`activitypub`) d'une recherche `searchType: news`. * * Invariant de « news éditoriale » : la collection `news` contient AUSSI des items auto-générés * ("X a créé Y") que `globalautocomplete` remonte bruts ; un `searchType: ["news"]` veut dire ACTUALITÉS, * pour TOUT consommateur. Filtre posé au niveau REQUÊTE → honoré à l'identique par le legacy ET le backend * Node (`buildFilters`) → byte-parité préservée. Cohérent avec `getNews` (mur profil, exclut déjà activityStream). * * NO-OP si `news` absent des types, OU si un filtre `type` explicite est déjà posé (OVERRIDABLE : * un outil de modération voulant le fil brut passe son propre `filters.type`). */ export declare function withNewsActivityExclusion(data: T): T; /** * Bornes d'agenda acceptant un `Date` (coercé en ISO par la méthode avant l'envoi — le fil reste * `type:string`). Partagé par `searchEventsCostum` et `getEvents`. */ type AgendaDateInputs = { startDateUTC?: Date | string; endDateUTC?: Date | string; }; /** Entrée de `searchEventsCostum` : la request, mais les bornes acceptent un `Date`. */ export type SearchEventsCostumInput = Omit, keyof AgendaDateInputs> & AgendaDateInputs; /** Entrée de `getEvents` : la request, mais les bornes acceptent un `Date`. */ export type GetEventsInput = Omit, keyof AgendaDateInputs> & AgendaDateInputs; /** * Une valeur distincte d'une thématique CoForm (retournée par `coformFilterByPath`). */ export interface CoformThematicValue { /** Nom de la valeur de thématique (ex: "Cafés cantines solidaires"). */ name: string; /** URL de l'image associée (chaîne vide si aucune). */ image: string; /** Identifiants des éléments liés via le finder (orgas/projets/poi selon finderPath). */ orgaNameArray: string[]; } /** * Résultat normalisé de `coformFilterByPath` (endpoint COSTUM_FILTER_COFORM_BY_PATH). * Reconstruit proprement la réponse backend que le normalizer générique aplatit. */ export interface CoformFilterByPathResult { /** _id des answers concernées par la thématique (dédupliqués). */ distinctElements: string[]; /** Valeurs distinctes de la thématique, chacune avec image/name/éléments liés. */ values: CoformThematicValue[]; /** Compteurs renvoyés par le backend (ex: `{ answers: 14 }`). */ count: Record; } type PaginationCursor = { searchType?: string[]; searchBy?: string; countType?: string[]; ranges?: Record; indexMin?: number; indexMax?: number; indexStep?: number; [key: string]: unknown; }; /** * État interne du paginateur, utilisé pour la restauration. */ export type PaginatorState = { cursor: PaginationCursor | null; count: number; index: number; history: PaginationCursor[]; sizes: number[]; /** * Métadonnées libres survivant à la sérialisation JSON. Permet aux méthodes paginées * de stocker des paramètres spécifiques (ex. `variant` pour `searchCostum`) qui doivent * être restaurés lors d'un `restorePaginationFromJSON`. */ meta?: Record; }; export interface PaginatorPage { count: { total: number; }; results: T[]; pageIndex: number; pageNumber: number; hasNext: boolean; hasPrev: boolean; next?: () => Promise>; prev?: () => Promise>; _initialData?: Record; _state?: PaginatorState; _entity?: BaseEntity; _methodName?: string; } /** * Type helper pour extraire les noms de méthodes depuis un Map as const * * @example * static UPDATE_BLOCKS = new Map([ * ["UPDATE_BLOCK_DESCRIPTION", "updateDescription"], * ["UPDATE_BLOCK_INFO", "updateInfo"] * ] as const); * * type MethodNames = ExtractMethodNames; * // Result: "updateDescription" | "updateInfo" */ export type ExtractMethodNames = T extends Map ? V extends readonly (readonly [any, infer M])[] ? M : never : never; interface LinkMeta { linkType: "memberOf" | "projects" | "events" | "friends"; connectTypeConnect: "member" | "contributor" | "attendee" | "friend"; connectTypeDisconnect: "members" | "contributors" | "attendees" | "friends"; } type BaseEntityCtor = typeof BaseEntity & { entityTag: string; entityType: "citoyens" | "organizations" | "projects" | "events" | "poi" | "badges" | "news" | "comments" | "answers" | "forms" | "classifieds" | "actions"; SCHEMA_CONSTANTS: string | string[]; }; interface FilterValueExistsOnly { $exists: boolean; } interface FilterValueInOnly { $in: string[]; } interface FilterValueCombined { $in: string[]; $exists: boolean; } type FilterValue = FilterValueExistsOnly | FilterValueInOnly | FilterValueCombined; type LinkFilters = Record; interface MinimalUserLink { toBeValidated?: boolean; isInviting?: boolean; isAdminInviting?: boolean; isAdminPending?: boolean; isAdmin?: boolean; } type EntityFilterValue = string | boolean | RegExp | ((value: any) => boolean); type EntityFilters = Record; interface LinkEntitiesOptions { key?: string; mapFn?: (entity: any) => any; } type Deletable = { _isDeleted?: boolean; }; type ReactiveData = Record & { __isReactive?: boolean; __raw?: any; }; interface FinalizerResult { results: TOut[]; count: { total: number; } & Record; } export type EntityType = "citoyens" | "organizations" | "projects" | "events" | "poi" | "badges" | "news" | "comments" | "answers" | "forms" | "classifieds" | "actions"; /** * Classe de base pour toutes les entités métiers : utilisateurs, projets, organisations, etc. * Fournit un système de brouillon (draft), transformation, appel API sécurisé, * et gestion de données côté client avec support du mode offline. * @template object TServerData * @abstract */ /** * Identifie un document image dans une liste `document/list`, par `contentKey` : * 1. `d.contentKey === contentKey` (backend cocolight, qui projette contentKey) ; * 2. sinon par **nom de fichier** matché sur `imageUrl` (backend legacy : `document/list` ne * renvoie PAS de contentKey → on rapproche le `name` du doc du basename de l'URL image). * Pure (testable sans backend). Renvoie `undefined` si aucun doc identifiable. */ export declare function findImageDocument(docs: T[], contentKey: string, imageUrl?: string): T | undefined; /** Document image de PROFIL (`findImageDocument` avec contentKey="profil"). */ export declare function findProfilDocument(docs: T[], profilImageUrl?: string): T | undefined; export declare class BaseEntity { __entityTag: string; deps: Partial; apiClient: ApiClient; parent: ParentLike | null; userContext: User | null; endpointApi: EndpointApi; data: any; meta?: any; _draftData: ReactiveData; _initialDraftData: Record; _serverData: TServerData & ReactiveData; _calledFromSave: boolean; _syncReactiveDraft: boolean; _isDeleted: boolean; /** Contexte costum porté par cette instance (null hors d'un `CostumScope`). Posé au constructeur. */ _costumCtx: CostumRuntimeContext | null; _add?: ((payload: any) => Promise) | undefined; _update?: ((payload: any) => Promise) | undefined; _allowedFieldsCache?: string[]; _allowedFieldsMetadata?: { combinedSchema: any; transforms: TransformsMap; removeFields: string[]; }; static entityTag: string; static entityType?: string; static SCHEMA_CONSTANTS?: string | string[]; static VIRTUAL_SCHEMAS?: Record; static CUSTOM_FIELD_HANDLERS?: Map; /** Constante de schéma du bloc de CRÉATION (ADD_*) où injecter les champs costum à la validation * d'extraction. Définie par les entités créables sous costum (Poi/Organization/Event/Project). */ static COSTUM_ADD_CONSTANT?: string; /** * Vérifie que l'objet n'a pas été supprimé. * @throws {ApiError} - Si l'objet a été supprimé. */ protected _checkNotDeleted(): void; /** * Constructeur de l'entité. * * `parent` peut être : * - une instance d'ApiClient * - une instance d'entité (User, Organization, Project, Poi, Event, Badge, News) * * @param parent * @param data - Données initiales. * @param deps - Dépendances injectées (EndpointApi, autres entités). * @param config - Configuration optionnelle (ex. `entityTag`). * @throws {ApiError} Si `parent` est invalide ou si `deps.EndpointApi` n'est ni une instance ni un constructeur. */ constructor(parent: ApiClient | ParentLike, data?: object, deps?: Partial, config?: BaseEntityConfig); /** * Permet de récupérer le constructeur typé correctement. */ protected _getCtor(): BaseEntityCtor; getEntityTag: (__entityTag: string | undefined) => string | undefined; /** @returns Identifiant de l'entité */ get id(): string | null; /** @returns Slug de l'entité */ get slug(): string | null; /** * Définit un ID (utilisé en interne) * @param newId - Nouvel ID à définir. */ _id(newId: string): void; /** @returns Indique si l'utilisateur est connecté */ get isConnected(): boolean; /** @returns Identifiant utilisateur associé */ get userId(): string | null; /** @returns Données de brouillon courantes (réactif) */ get draftData(): ReactiveData; /** @returns Données de brouillon initiales (non-réactif) */ get initialDraftData(): Record; /** @returns Données brutes du serveur */ get serverData(): TServerData; /** @returns Indique si cette entité représente l'utilisateur connecté */ get isMe(): boolean; /** @returns Type de l'entité (ex: 'citoyens') */ getEntityType(): "citoyens" | "organizations" | "projects" | "events" | "poi" | "badges" | "news" | "comments" | "answers" | "forms" | "classifieds" | "actions"; /** * Indique si le draft contient des modifications par rapport aux données initiales. * @returns {boolean} */ hasChanges(): boolean; /** Chaîne EJSON canonique d'un objet nettoyé, pour comparaison d'égalité (cf. hasChanges). */ private _ejsonForCompare; /** * Rafraîchit l'entité en rechargeant ses données depuis le serveur. * @returns Données mises à jour */ refresh(): Promise>; /** * Helper pour les entités enfants : refresh automatique APRÈS une mutation directe * (méthode appelée hors flow `save()`). Skip si l'appel vient de `save()` qui refresh * lui-même à la fin — détecté via le flag `_calledFromSave`. * * À appeler dans les méthodes publiques de mutation (ex: `Action.cancel()`, * `Organization.updateOpeningHours()`) après l'opération réussie, pour que le caller * trouve `entity.serverData` à jour sans avoir à appeler `refresh()` manuellement. * * @example * async myMutation(): Promise { * const result = await this.endpointApi.someEndpoint({ ... }); * await this._refreshIfDirect(); * return result; * } */ protected _refreshIfDirect(): Promise; /** * Sauvegarde les modifications locales vers le serveur (add ou update). * @returns Données serveur mises à jour (après éventuel `refresh()`) */ save(): Promise>; /** * UPDATE d'une entité costum-scopée — pendant EXACT de l'ADD. Au lieu de N `updatepathvalue` champ-par-champ * (route « édition inline » legacy : AUCUN hook, AUCUNE validation), on envoie UN SEUL `element/save` portant * tous les champs MODIFIÉS (standard + costum) + l'id + l'enveloppe costum (contexte + schémas des champs * envoyés, `isUpdate:true`). Côté backend, `element/save` branche update déclenche prepData / elementBeforeSave / * elementAfterUpdate / elementAfterSave / validation costum (cf. legacy `Element::save` : ce sont les SEULES * routes qui exécutent ces hooks ; updatepathvalue/updateblock n'en exécutent aucun de dérivation). Atomique * (un seul `updated`/recalcul slug). Effacement (`null`/`""`/`[]`) → `""` (survit à `stripNulls`, backend → `$unset`). * * L'univers des champs éditables = `_allowedFieldsCache` (union writable des SCHEMA_CONSTANTS + schéma costum, * calculée au build du draft) ∪ champs DATA costum déclarés (robuste si le cache est antérieur au costum). * @returns true si au moins un champ a été envoyé (donc refresh ensuite). */ protected _saveCostumViaElementSave(payload: Record): Promise; /** Recherche le schéma d'une propriété dans un schéma combiné `{ allOf: [...] }` (1er match). */ private _lookupPropSchema; /** * Crée une nouvelle instance d'entité à partir des données du serveur. * * @param data - Données du serveur. * @param parent - Instance parente (ApiClient ou BaseEntity). * @param deps - Dépendances injectées (classes d'entités disponibles). * @returns Nouvelle instance d'entité. */ static fromServerData(data: object, parent: ApiClient | ParentLike, deps: object): BaseEntity; /** * Hook pour transformer les données du serveur avant qu'elles ne soient appliquées. * Peut être overridé dans les classes filles pour transformer des champs imbriqués en instances d'entités. * * @param data - Les données brutes du serveur. * @returns Les données transformées. * @protected */ protected _transformServerData(data: TServerData): TServerData; /** * Met à jour les données de l'entité avec de nouvelles données. * * @param newData - Les nouvelles données à appliquer. * @param {{ forceInitialDraftReset?: boolean }} [options] * @protected */ protected _setData(newData: TServerData, { forceInitialDraftReset }?: { forceInitialDraftReset?: boolean; }): void; _updateDraftPreservingUserChanges(draft: Record): void; _resetInitialDraftData(): void; _toRawDeep(obj: any): any; /** * Champs à ajouter automatiquement à chaque draft (ex: `typeElement`). * Souvent utilisés dans les conditions `if/then` du JSON Schema. */ defaultFields: { [key: string]: any; }; /** * Champs à exclure explicitement du draft et des payloads. */ removeFields: string[]; /** * Transformations à appliquer à certains champs lors de la lecture depuis le draft. * Clé = champ, valeur = fonction (val, full) => valeur transformée. */ transforms: TransformsMap; /** * ─────────────────────────────── * JSON * ─────────────────────────────── */ /** * Convertit l'instance en JSON pour l'envoi au serveur. * * @returns Représentation JSON de l'instance. */ toJSON(): { __entityTag: string; __isSerializedEntity: boolean; serverData: any; parent: any; }; _serialize(obj: any): any; /** * Supprime les propriétés non sérialisables d'un objet. * * @param obj - L'objet à nettoyer. * @param seen - Ensemble pour éviter les références circulaires. * @returns L'objet nettoyé. * @private */ private _removeUnserializables; /** * Restaure les données sérialisées en un objet d'origine. * Désérialise récursivement les entités imbriquées qui ont le marqueur __isSerializedEntity. * * @param obj - L'objet à restaurer. * @param parent - Instance parente optionnelle pour désérialiser les entités imbriquées. * @returns L'objet restauré. */ static _revive(obj: object, parent?: ApiClient | BaseEntity | null): any; /** * Parcourt récursivement un objet et désérialise les entités imbriquées. * * @param obj - L'objet à parcourir. * @param parent - Instance parente pour les entités. * @returns L'objet avec les entités désérialisées. * @private */ private static _deserializeNestedEntities; /** * Crée une instance d'entité à partir de données JSON. * * @param json - Données JSON à utiliser. * @param parent - Instance parente. * @param deps - Dépendances injectées. * @returns Nouvelle instance d'entité. */ static fromJSON(json: unknown, parent?: ApiClient | BaseEntity | null, deps?: object): BaseEntity; /** * ─────────────────────────────── * UtilMixin * ─────────────────────────────── */ /** * Appelle une méthode de l'API. * * @param constant - Le nom de la méthode à appeler. * @param data - Les données à passer à la méthode. * @returns - `response.data` transformé ; peut être `null` si la requête est **mise en file** (offline/breaker). * @throws {ApiValidationError} - Si une erreur se produit lors de l'appel de l'API. * @throws {ApiResponseError} - Si une erreur se produit lors de l'appel de l'API. * @throws {ApiClientError} - Si l'utilisateur n'est pas authentifié. */ call(constant: string, data?: object): Promise; /** * Appelle une méthode de l'API si l'utilisateur n'est pas connecté. * * @param constant - Le nom de la méthode à appeler. * @param data - Les données à passer à la méthode. * @returns - `response.data` ou `null` si enqueue offline/breaker. * @throws {ApiAuthenticationError} - Si l'utilisateur est connecté. * @throws {ApiValidationError} - Si une erreur se produit lors de l'appel de l'API. * @throws {ApiResponseError} - Si une erreur se produit lors de l'appel de l'API. * @throws {ApiClientError} - Si une erreur se produit lors de l'appel de l'API. */ callNoConnected(constant: string, data?: object): Promise; /** * Appelle une méthode de l'API si l'utilisateur est connecté. * * @param param - Le nom de la méthode à appeler ou une fonction de rappel. * @param data - Les données à passer à la méthode. * @returns - `response.data` ou `null` si enqueue offline/breaker. * @throws {ApiAuthenticationError} - Si l'utilisateur n'est pas connecté. * @throws {ApiValidationError} - Si une erreur se produit lors de l'appel de l'API. * @throws {ApiResponseError} - Si une erreur se produit lors de l'appel de l'API. * @throws {ApiClientError} - Si une erreur se produit lors de l'appel de l'API. */ callIsConnected(param: string | (() => Promise), data?: object): Promise; /** * Appelle une méthode de l'API si l'utilisateur est lui-même. * * @param param - Le nom de la méthode à appeler ou une fonction de rappel. * @param data - Les données à passer à la méthode. * @returns - `response.data` ou `null` si enqueue offline/breaker. * @throws {ApiAuthenticationError} - Si l'utilisateur n'est pas lui-même. * @throws {ApiValidationError} - Si une erreur se produit lors de l'appel de l'API. * @throws {ApiResponseError} - Si une erreur se produit lors de l'appel de l'API. * @throws {ApiClientError} - Si une erreur se produit lors de l'appel de l'API. */ callIsMe(param: string | (() => Promise), data?: object): Promise; /** * Valide une image d'entrée. * * @param imageInput - L'image à valider. * @returns - L'image validée. * @private */ protected _validateImage(imageInput: UploadInput): Promise; /** * Valide un fichier d'entrée. * * @param fileInput - Le fichier à valider. * @returns - Le fichier validé. * @private */ protected _validateFile(fileInput: UploadInput): Promise; /** * Prépare un fichier pour un upload multipart **sans restriction MIME**. * * Diffère de `_validateImage` / `_validateFile` (liste MIME blanche stricte) : * accepte tout MIME et garantit la conversion `Buffer → annotated stream` en * Node (le multipart encoder du package `form-data` ignore les Buffer bruts ; * il a besoin de `path` + `mimeType` sur un Readable). * * Pensé pour les endpoints d'upload large-spectre (ex: coform où l'utilisateur * peut uploader PNG, GIF, WebP, PDF, DOCX, XLSX, etc. — c'est le backend qui * filtre selon la config du Form, pas le client). * * Compatible browser + Node : * - Browser `File` → pass-through * - Browser `Blob` → wrap en `File` (génère nom + extension via MIME) * - Node `Buffer` → détecte MIME via `file-type`, convertit en `Readable` * annoté (`path`, `mimeType`) * - Node `Readable` → pass-through si annoté, sinon utilise `fallbackName` * * @param input - Fichier à uploader (Buffer/File/Blob/Readable) * @param fallbackName - Nom à utiliser si l'input n'en porte pas. Défaut `upload-`. * @returns `{ qqfile, qqfilename, qqtotalfilesize }` prêt pour multipart. * @throws {ApiError} si le type de l'input n'est pas reconnu. * * @protected */ protected _prepareUploadFile(input: UploadInput, fallbackName?: string): Promise<{ qqfile: ValidatedUpload; qqfilename: string; qqtotalfilesize: number; }>; /** * Supprime un document (fichier ou image) par son `docId` MongoDB via * `DELETE_DOCUMENT_BY_ID`. * * Méthode générique exposée sur toutes les entités — le endpoint backend * opère par docId seul, sans contexte parent requis. Disponible ici pour * être découvrable et **override-able** par les entités qui veulent un * cleanup local après suppression (ex: `News.deleteFile` pourrait retirer * l'id de `mediaImg.images` côté serverData). * * Ne met PAS à jour `serverData` après suppression — le caller doit * invalider son cache local s'il en a un (cas typique : React Query). * * @param docId - ID MongoDB du document (24 hex) * @throws {ApiError} 400 si `docId` invalide. * * @example * // Depuis une Answer (suppression d'un fichier uploader) * const files = await answer.getFiles({ subKey: "sf.input", docType: "image" }); * await answer.deleteFile(files[0].docId); * * @example * // Depuis n'importe quelle entité qui possède un docId * await news.deleteFile(imageDocId); */ deleteFile(docId: string): Promise; /** * Champs de contexte costum à joindre à un upload multipart (profil/bannière/galerie) pour que le * backend stampe `source.key` (Document.php:228-232). Dérivés de `_costumCtx` (résolu du `source.key` * de l'élément) → un POI scopé "parent62" envoie `costumSlug="parent62"`. `{}` hors contexte costum. */ protected _costumUploadFields(): { costumSlug?: string; costumId?: string; costumType?: string; }; /** * Uploade un document (image/fichier) rattaché à CET élément, avec `contentKey`/`docType` * PARAMÉTRABLES — ex. `contentKey:"slider"` pour une galerie (Option C). La provenance costum * (`source.key`) est injectée automatiquement depuis `_costumCtx` (résolu du `source.key` de * l'élément). L'élément DOIT préexister (pas d'auto-création, contraste avec `Answer.uploadFile`). * Endpoint générique `UPLOAD_DOCUMENT` (backend `co2/document/upload-save`, route wildcard). * * STATELESS : ne rafraîchit PAS `serverData` (pas de push local façon `News.addImage`). Pour refléter * la nouvelle image dans `getGalleryImages()`, re-fetch l'élément via `about` après upload (le * consommateur peut afficher optimistement via le `docPath` retourné en attendant). Cf. patron * `Answer.processUploads` : la galerie s'uploade APRÈS `save()` (l'élément doit avoir un `id`). * * @returns `{ docId, docPath }` — id du document créé + URL relative servie. * @throws {ApiError} 404 si l'entité n'est pas enregistrée (pas d'`id`). */ uploadDocument(file: UploadInput, opts: { contentKey: string; docType?: "image" | "file"; subKey?: string; folder?: string; ownerId?: string; }): Promise<{ docId: string; docPath: string; }>; /** * Images d'une galerie de CET élément, lues depuis le champ `images` fusionné par `about`/getById * (backend `enrichAbout` — {Poi,Classified}::getById), filtrées par `contentKey` (défaut `"slider"`). * SYNCHRONE : nécessite que l'entité ait été chargée via `about` (sinon `[]`). Forme = getListOfImage * (`{ id, _id, contentKey, imagePath, imageThumbPath, imageMediumPath, subKey, name, ... }`). */ getGalleryImages(contentKey?: string): Array>; /** * Liste les documents (images/fichiers) attachés à CETTE entité (`id` + `type`) via `DOCUMENT_LIST` * (port legacy `document/list`). Renvoie un tableau plat `{ id, name, path, author, contentKey }`. * Utile pour récupérer le `docId` d'une image (ex. profil) — non exposé sur l'élément — afin de la * supprimer (`deleteFile`). */ getDocuments(): Promise>; /** * Liste les FICHIERS (documents non-image, `doctype:"file"`) attachés à CETTE entité via `GET_GALLERY` * (`co2/gallery/index`, branche fichier : `docs` = OBJET keyé par `_id`, records BRUTS + `docPath` * absolutisé par `_imageFields`). Source UNIQUE et uniforme tous types (org/projet/citoyen/poi/events/ * classifieds — l'`IndexAction` legacy est type-agnostique). Contraste `getGalleryImages` (images, * synchrone via `about`) et `getDocuments` (DOCUMENT_LIST, forme réduite sans `size`/`docType`, `path` * = dossier seul). Les fichiers rangés en dossier (`folderId`) sont exclus par défaut (parité * `IndexAction` : `folderId:{$exists:false}`). * * @returns `{ docId, docPath, name, size, contentKey }[]` — `contentKey` = valeur DÉRIVÉE stockée * (`pdf`/`spreadsheet`/`text`/`presentation`), utilisable pour choisir l'icône. * @throws {ApiError} 404 si l'entité n'est pas enregistrée. */ getGalleryFiles(): Promise>; /** * Supprime l'image de PROFIL de cette entité : récupère le `docId` du document `contentKey:"profil"` * (via `getDocuments`) puis `deleteFile`. No-op (`false`) s'il n'y a pas d'image. Pendant exact de * `updateImageProfil` côté suppression. */ removeProfilImage(): Promise; /** * Supprime l'image de BANNIÈRE de cette entité : récupère le `docId` du document `contentKey:"banner"` * (via `getDocuments`) puis `deleteFile`. No-op (`false`) s'il n'y a pas de bannière. Pendant de * `updateImageBanner` côté suppression (le backend désassigne `profilBannerUrl`/`profilRealBannerUrl`). */ removeBannerImage(): Promise; /** * Vide les objets réactifs et marque l'entité comme supprimée (tombstone), * sans casser la réactivité. Pattern partagé par tous les `delete()`. * * Après appel : `serverData`/`draftData` vidés et `_isDeleted = true` → toute * opération serveur ultérieure lève le tombstone (cf. `_checkNotDeleted`). * @protected */ protected _markDeletedLocally(): void; /** Droit d'ADMIN DE COSTUM pré-résolu par l'appelant (ex. site-json /admin via `me.isCostumAdmin(slug)`). */ protected _costumAdminAuthorized: boolean; /** * Marque cette instance comme éditable/supprimable au titre du droit-parapluie COSTUM. À poser par * l'appelant qui a DÉJÀ résolu `me.isCostumAdmin(slug)` (async) — les gardes client d'édition (`_update`) * et de suppression (`_deleteViaElement`) le respectent alors. Le backend reste la source de vérité * (canEditItem/canDeleteElement portent isCostumAdmin), ceci n'ouvre QUE le pré-check client. */ setCostumAdminAuthorized(v?: boolean): this; /** * Droit de gestion « élevé » côté client : super-admin (via userContext) OU admin de costum pré-résolu. * Utilisé par les gardes `_update`/`_deleteViaElement` pour ne pas bloquer une action que le backend autorise. */ protected _hasElevatedManageRight(): boolean; /** * Suppression générique d'un élément via `DELETE_ELEMENT` * (`/co2/element/delete/type/{type}/id/{id}`). * * **Garde client** : `isAuthorOrAdmin({ checkHierarchy })` — auteur (`creator`) * OU administrateur (lien direct, ou via la hiérarchie parent org/projet * résolue par `_isAdminViaHierarchy`). Le serveur reste la source de vérité. * * Après succès : `serverData`/`draftData` vidés + `_isDeleted = true` * (via `_markDeletedLocally`). * * @param type - Type d'élément backend (typiquement `this.getEntityType()`). * @param reason - Raison transmise au backend (audit). * @param options.checkHierarchy - Propage la vérif admin à la hiérarchie parent. Défaut `true`. * @throws {ApiError} 400 si l'entité n'a pas d'id. * @throws {ApiError} 403 si ni auteur ni admin (selon la garde). * @protected */ protected _deleteViaElement(type: string, reason: string, { checkHierarchy }?: { checkHierarchy?: boolean; }): Promise; /** * Met à jour un champ unique (ou multi-fields via `updatePartial`) sur cette * entité via `UPDATE_PATH_VALUE`. * * **Cas d'usage couverts** : * - `$set` simple : `entity.updateField("name", "Nouveau nom")` * - `$unset` : `entity.updateField("description", null)` * - `$push` array : `entity.updateField("tags", "x", { arrayForm: true })` * - `$pull` array : `entity.updateField("tags.3", null, { pull: "tags" })` * - `$pullAll` array : `entity.updateField("tags", ["a","b"], { arrayForm: true, pull: "tags" })` * - Coercion type : `entity.updateField("credits", 100, { setType: "int" })` * - Date auto : `entity.updateField("startDate", new Date())` → setType "isoDate" + ISO string * - Multi-field merge : `entity.updateField("address", {...}, { updatePartial: true })` * - Auth sub-form : `entity.updateField("...", value, { formParentId: "..." })` * * **Magic values** : * - `value: "updatedTime"` → backend remplace par `time()` Unix seconds * - `value: "now"` + `setType: "isoDate"` → résout en date actuelle * - `path: "allToRoot"` → value exposée à la racine de l'entité * * Le payload est normalisé via `_normalizeUpdatePathPayload` avant envoi * (cf. les 4 règles documentées sur cette méthode). * * @param path - Chemin MongoDB dot-notation (ex: `"address.street"`, `"answers.aapStep1.depense.3"`). * @param value - Valeur à poser. Accepte `Date` (auto-convertie en ISO + setType isoDate). * @param opts - Options : `setType`, `arrayForm`, `pull`, `updatePartial`, `formParentId`, `edit`. * @returns Réponse brute de l'API. * @throws {ApiError} 404 si l'entité n'a pas d'id. * @throws {ApiError} 400 si `value === undefined`, `path` vide, ou conflit `arrayForm` + `pull` sans value array, ou `updatePartial` sans value object. * * @example * // Update simple * await org.updateField("name", "Nouvelle organisation"); * * @example * // Date instance auto-convertie * await action.updateField("startDate", new Date()); * * @example * // Multi-field merge (préserve les autres clés de address) * await org.updateField("address", { * street: "123 rue X", * city: "Paris", * }, { updatePartial: true }); * * @example * // Push dans array * await project.updateField("tags", "permaculture", { arrayForm: true }); * * @example * // Pull from array (avec normalisation R2 : null → "") * await project.updateField("oceco.milestones.3", null, { pull: "oceco.milestones" }); */ updateField(path: string, value: unknown, opts?: { setType?: SetTypeValue | Array<{ path: string; type: SetTypeValue; }>; arrayForm?: boolean; pull?: string; updatePartial?: boolean; formParentId?: string; edit?: boolean; }): Promise; /** * Normalise le payload `updatePathValue` avant envoi au backend. * * Règles appliquées (4) : * - **R0** : `value instanceof Date` → `value.toISOString()` + `setType: "isoDate"` * (override d'un setType existant — l'intent `Date` est prioritaire). * - **R1** : `setType: [{type:"isoDate"}, ...]` uniforme + `value: string` → * collapse en `setType: "isoDate"` (forme scalaire préférée backend). * - **R2** : `pull` présent + `value: ""|null|undefined` → force `value: ""` * (le backend exige `value: string` quand `pull` est présent — contrainte * schema `allOf`). * - **R6** : `setType: []` array vide → drop silencieusement (évite wire * format inutile, sécurise contre filtres qui ont tout retiré). * * Pour traiter une string ISO comme date, le caller doit passer `setType` * explicitement (`"isoDate"` ou `[{path, type:"isoDate"}]`). Pas d'heuristique * sur les strings pour éviter les faux positifs sur champs texte libre. * * @protected */ protected _normalizeUpdatePathPayload(payload: UpdatePathValueData): UpdatePathValueData; /** * Valide les entrées d'upload de fichiers. * * @param input - Le fichier à valider. * @param {{ allowedMimeTypes?: string[], expectedType?: "image"|"file"|"any" }} options - Options de validation. * @returns - Le fichier validé. * @throws {ApiValidationError} - Si le type de fichier est invalide. * @throws {Error} - Si le type de fichier est inconnu. * @private */ private _validateUploadInput; /** * Transforme un Buffer en ReadableStream équivalent à fs.createReadStream. * * Note : passé `protected` car réutilisé par `_prepareUploadFile()` (Answer). * * @param buffer - Le buffer contenant les données binaires * @param [filename="file.bin"] - Nom de fichier (utilisé dans FormData) * @param [mimeType="application/octet-stream"] - Type MIME (utilisé dans FormData) * @returns - Readable doté de `path` et `mimeType` * @protected */ protected _createReadStreamFromBuffer(buffer: Buffer, filename?: string, mimeType?: string): Promise; /** * Transforme un Buffer en ReadableStream. * * @param buffer - Le buffer à transformer. * @returns - Un ReadableStream. * @throws {Error} - Si appelé dans le navigateur. * @private */ private _bufferToReadable; /** * Crée un PassThrough stream pour le traitement des fichiers. * * @returns - Un PassThrough stream. * @throws {Error} - Si appelé dans le navigateur. * @private */ private _passThrough; /** * Supprime les propriétés d'un objet en fonction d'une liste de clés. * * @param obj - L'objet source. * @param propsToRemove - Liste des clés à supprimer * @returns - Un nouvel objet sans les propriétés supprimées. * @private */ private _omitProps; /** * Extrait les propriétés d'un objet en fonction d'une liste de clés. * * @param obj - L'objet source. * @param keys - Liste des clés à extraire. * @param [transforms={}] - Transformations à appliquer aux valeurs. * @returns - Un nouvel objet contenant les propriétés extraites. * @private */ private _pickProps; /** * Vérifie si au moins une clé est présente dans l'objet et n'est pas nulle. * * @param obj - L'objet à vérifier. * @param keys - Liste des clés à vérifier. * @returns - true si au moins une clé est présente et non nulle, sinon false. * @private */ private _hasAtLeastOne; /** * Crée un proxy filtré pour une liste d'entités. * @private */ _createFilteredProxy(list: T[]): T[]; /** * Génère un nouvel identifiant unique. * @returns Un identifiant unique. * @protected */ protected _newId(): string; /** * ─────────────────────────────── * DraftStateMixin * ─────────────────────────────── */ /** * Recalcule les champs autorisés en fonction de l'état actuel du draft. * Utilisé pour validation dynamique dans le proxy this.data. * @returns Liste des champs autorisés * @private */ private _getAllowedFieldsForCurrentState; /** * Crée un proxy combinant draft + serveur, avec transformations facultatives. * @param apiClient * @param [server={}] * @param [draft={}] * @param _allowedFields - champs autorisés dans le draft (fallback si pas de metadata) * @param [transforms={}] - transformateurs de lecture * @param {{ throwOnError?: boolean }} [options={}] - options * @param [entity=this] - référence à l'entité pour recalcul dynamique * @returns {object} * @private */ private _createDraftProxy; /** * Extrait les champs modifiables d'un schéma JSON. * * @param schema - Le schéma JSON à analyser. * @param data - Les données à comparer. * @param ctx - Contexte d'extraction (pour la récursion). * @param ctx.defs - Définitions de schéma. * @param ctx.visited - Ensemble des schémas déjà visités. * @returns Liste des champs modifiables (propriétés `writeable` implicites). * @private */ private _extractWritableFields; /** * Construit un brouillon et un proxy à partir des données et du schéma. * * @param options - Options de construction. * @param options.data - Données à utiliser pour le brouillon. * @param options.serverData - Données du serveur. * @param options.previousDraft - Brouillon précédent pour la réactivité. * @param options.constant - Nom de la constante ou tableau de constantes. * @param options.apiClient - Instance de l'API. * @param options.transforms - Transformations à appliquer. * @param options.throwOnError - Si vrai, lève une erreur en cas de problème. * @param options.removeFields - Liste des champs à ignorer. * @returns Draft réactif, proxy combiné, snapshot initial. * @private */ private _buildDraftAndProxy; /** * Invoque une méthode de bloc de manière type-safe. * Élimine le besoin de casts `as any` dans les classes dérivées. * * @param blocks - Le Map contenant les noms de méthodes (doit être `as const`) * @param methodName - Le nom de la méthode à invoquer * @param blockData - Les données à passer à la méthode * @returns La valeur retournée par la méthode * @protected * * @example * // Dans une classe dérivée : * await this._invokeBlockMethod(User.UPDATE_BLOCKS, methodName, blockData); */ protected _invokeBlockMethod>(blocks: T, methodName: string, blockData: any): Promise; /** * Extrait les champs modifiés du schéma. * * @param apiClient - Instance de l'API. * @param constant - Nom de la constante. * @param data - Données à comparer. * @param getInitialDraft - Fonction pour obtenir le brouillon initial. * @param removeFields - Liste des champs à ignorer. * @returns - Champs modifiés ou `null` si aucun changement. * @protected */ /** * Détecte si une valeur est un type d'upload (Buffer, File, Blob, Stream) * @param obj - La valeur à tester * @returns true si c'est un type d'upload * @private */ private _isUploadType; /** * Compare deux valeurs de manière intelligente : * - Types d'upload (Buffer, File, etc.) : comparaison par référence * - Autres types : comparaison JSON * @param current - Valeur actuelle * @param initial - Valeur initiale * @returns true si les valeurs sont différentes * @private */ private _compareValues; /** * Merge deux schémas JSON en combinant leurs propriétés. * @param base - Schéma de base (peut être null/undefined) * @param virtual - Schéma virtuel à fusionner * @returns Schéma combiné * @private */ private _mergeSchemas; /** * Vérifie si un champ a changé par rapport à l'état initial. * @param fieldName - Nom du champ * @returns true si le champ a changé * @protected */ protected _hasFieldChanged(fieldName: string): boolean; /** * Invoque un handler personnalisé pour un champ spécifique. * @param methodName - Nom de la méthode à appeler * @param value - Valeur du champ * @param fieldName - Nom du champ (pour les messages d'erreur) * @returns Résultat de l'appel de la méthode * @protected */ protected _invokeCustomFieldHandler(methodName: string, value: any, fieldName: string): Promise; _extractChangedFieldsFromSchema(apiClient: ApiClient, constant: string, data: Record | undefined, getInitialDraft: () => Record | void, removeFields?: string[]): Record | null; /** * Extrait tous les champs valides selon le schéma, et retourne uniquement ceux qui ont changé par rapport au draft initial. * Contrairement à `_extractChangedFieldsFromSchema`, cette méthode retourne l'ensemble des champs valides (`updated`) * uniquement s'il y a au moins un champ modifié (`changed`). * * ⚠️ Les champs sont filtrés en fonction : * - des champs définis comme modifiables dans le schéma (`writeable`) * - des champs non exclus dans `removeFields` * - des champs définis dans `data` (les `undefined` sont ignorés) * * @param apiClient - L'instance de client API contenant les schémas. * @param constant - Le nom de la constante de schéma (ex: "ADD_EVENT"). * @param data - Les nouvelles données à comparer avec le draft initial. * @param getInitialDraft - Fonction qui retourne le draft initial (souvent `this.initialDraftData`). * @param removeFields - Champs à ignorer même s'ils sont valides. * @returns - Un objet `updated` avec les champs valides si au moins un champ a changé, sinon `null`. * @private */ _extractAllValidFieldsFromSchema(apiClient: ApiClient, constant: string, data: Record | undefined, getInitialDraft: () => Record, removeFields?: string[]): Record | null; /** * ─────────────────────────────── * MutualEntityMixin * ─────────────────────────────── */ /** * Résout l'identifiant de l'entité si seul le slug est fourni. * @param type - Le type d'entité (ex : "citoyens", "organizations", "projects"). * @returns L'identifiant résolu. * @throws {ApiResponseError} - Si le slug ne correspond pas à l'entité attendue. * @throws {ApiError} - Si l'identifiant ne peut pas être résolu. * @throws {ApiResponseError} - Si une erreur se produit lors de la récupération * @private */ private _resolveId; /** * Récupère le profil public de l'entité. * * @returns - Les données du profil public. * @protected */ protected _getPublicProfile(): Promise>; /** * Récupère la classe d'entité et ses dépendances à partir du type d'entité. * * @private */ private _getEntityMeta; /** * Lier des données d'entité à une instance d'entité. * * @param entityType - Le type d'entité (ex : "citoyens", "organisations", "projets"). * @param entityData - Les données de l'entité à lier. * @return L'entité liée ou les données brutes si la metadata n'existe pas. * @private */ _linkEntity(entityType: string, entityData: object): AnyEntity | object; /** * Transforme un objet imbriqué avec {id, type} en instance d'entité. * Utilisé pour transformer des champs comme author, target, etc. * * @param obj - L'objet à transformer (doit avoir au minimum {id, type}). * @returns L'instance d'entité ou l'objet original si la transformation échoue. * @protected */ protected _linkNestedEntity(obj: any): T | any; /** * Récupère et lie une entité à partir de son ID. * * @param entityType - Le type d'entité. * @param entityId - L'identifiant de l'entité. * @param options - Options supplémentaires : * @param options.skipGet - Si true, ne pas appeler `get()` sur l'entité. * @return L'entité liée ou null. * @private */ _linkEntityById(entityType: string, entityId: string, options?: { skipGet?: boolean; }): Promise; /** * Lie une liste d'entités à partir de leurs données. * * @param results - Liste de données d'entités. * @return Liste d'entités liées. * @private */ protected _linkEntities(results: any[]): any[]; /** * Lie des entités présentes dans `this.serverData` à partir de leurs IDs, * en les filtrant dynamiquement et en optionnellement les transformant. * * @param entityType - Le type d'entité (ex : "badges", "citoyens", etc.). * @param filters - Clés/valeurs de filtres dynamiques. Les valeurs peuvent être : * - un littéral (comparaison stricte ou intelligente selon le type), * - une chaîne (utilise `includes` insensible à la casse), * - une RegExp (appliquée si la valeur est une chaîne), * - une fonction `(value) => boolean`. * @param options - Options supplémentaires : * @param options.key - Le champ de `this.serverData` à utiliser (par défaut = entityType). * @param options.mapFn - Fonction de transformation `(entity) => any` appliquée au résultat. * * @return Liste des entités liées, filtrées et éventuellement transformées. * * @example * // Tous les badges avec `name` contenant "codev" * const badges = await this.linkEntitiesFromServerData("badges", { name: "codev" }); * * @example * // Badges non expirés et visibles * const badges = await this.linkEntitiesFromServerData("badges", { * expiredOn: false, * show: "true" * }); * * @example * // Badges émis après 2023 * const badges = await this.linkEntitiesFromServerData("badges", { * issuedOn: (v) => new Date(v) >= new Date("2023-01-01") * }); * * @example * // Extraire uniquement les noms des badges * const namesOnly = await this.linkEntitiesFromServerData("badges", {}, { * mapFn: (badge) => badge.meta.name * }); */ linkEntitiesFromServerData(entityType: string, filters?: EntityFilters, options?: LinkEntitiesOptions): Promise; /** * Crée une instance d'entité à partir des données fournies. * @template keyof EntityTypeMap T * @param entityType * @param entityData * @return {EntityTypeMap[T]} * @private */ _entityInstanceData(entityType: T, entityData: object, config?: BaseEntityConfig): EntityTypeMap[T]; /** * Crée une instance d'entité, et déclenche get() si certaines propriétés sont présentes. * @template keyof EntityTypeMap T * @param entityType * @param [entityData={}] * @return {Promise} */ entity(entityType: T, entityData?: object, config?: BaseEntityConfig): Promise; /** * Récupère et lie une entité à partir de son slug. * @param slug - Le slug de l'entité. * @return L'entité liée. * @throws {ApiError} Si le slug est vide. * @throws {ApiResponseError} Si le slug n'existe pas ou est invalide. */ entityBySlug(slug: string): Promise; /** * Corriger le userContext d'une entité si elle a un vrai parent différent * @param entity - L'entité à corriger * @private */ private _fixEntityUserContext; /** * Extraire l'info du vrai parent depuis les serverData * @param entity - L'entité à analyser * @private */ private _extractRealParent; /** * Trouver ou créer le parent réel avec ses données complètes * @param parentInfo - Info du parent à créer * @private */ private _findOrCreateParent; /** * Extraire le bon userContext du parent * @param parent - Le parent dont on veut extraire le userContext * @private */ private _extractUserContext; /** * Vérifie si l'utilisateur est admin via la hiérarchie parent (logique généraliste). * * Note : passé `protected` pour permettre l'override par les entités dont la * hiérarchie n'est pas un simple champ `serverData.parent` ou `.organizer` * (cf. `Answer._isAdminViaHierarchy` qui remonte via son parent instance Form). * * @protected */ protected _isAdminViaHierarchy(): boolean; /** * Vérifie si l'utilisateur est admin d'un parent spécifique. * * Note : passé `protected` pour être utilisable par les overrides de * `_isAdminViaHierarchy()` (cf. `Answer._isAdminViaHierarchy`). * * @protected */ protected _isAdminOfParent(parentId: string, parentType: string): boolean; /** * Construit dynamiquement des filtres sur un lien entre entités * * @param id - L'ID de l'entité cible. * @param options - Options de filtrage. * @param options.linkType - Le type de lien (ex: "memberOf", "projects", etc.). * @param options.isAdmin - Si défini, filtre selon l'existence de isAdmin. * @param options.isAdminPending - Si défini, filtre selon l'existence de isAdminPending. * @param options.isInviting - Si défini, filtre selon l'existence de isInviting. * @param options.toBeValidated - Si défini, filtre selon l'existence de toBeValidated. * @param options.roles - Rôles à filtrer. * @returns - Un objet de filtres à passer à une requête. * @throws {TypeError} - Si les types des paramètres ne sont pas valides. * @protected */ _buildLinkFilters(id: string | null, { linkType, isAdmin, isAdminPending, isInviting, toBeValidated, roles }: { linkType: "memberOf" | "projects" | "events"; isAdmin?: boolean; isAdminPending?: boolean; isInviting?: boolean; toBeValidated?: boolean; roles?: string[]; }): LinkFilters; /** * Retourne les métadonnées de lien pour une entité : * - `linkType` : clé dans `serverData.links` * - `connectTypeConnect` : valeur envoyée pour `connect()` * - `connectTypeDisconnect` : valeur envoyée pour `disconnect()` * @throws {ApiError} - Si le type d'entité est inconnu. */ _getLinkMeta(): LinkMeta; /** * Vérifie si l'entité prend en charge les opérations de lien (`connect`, `disconnect`, etc.). * Utilise `_getLinkMeta()` comme source unique de vérité. * * @throws {ApiError} - Si l'entité ne supporte pas les liens. * @protected */ protected _checkLinkableEntity(): void; /** * Retourne le lien de l'utilisateur connecté avec l'entité cible (dans `parent.serverData.links`) * @protected */ protected _getLinkFromConnectedUser(): import("./serverDataType/common.js").LinkProjectRef | import("./serverDataType/common.js").LinkEventsRef | import("./serverDataType/common.js").LinkfriendsRef | import("./serverDataType/common.js").LinkMemberOfUserRef | null; /** * Soumet une demande de connexion à une entité (ex : membre, contributeur), * ou valide l'invitation si elle existe déjà. * * @returns - Résultat de l'API * @throws {ApiError} - En cas d'erreur de connexion ou d'invitation. * @private */ private _submitLinkRequest; /** * Soumet une demande pour devenir administrateur d'une entité. * * @returns {Promise} * @throws {ApiError} * @private */ private _submitLinkRequestAdmin; /** * Accepte une demande de lien (ex : invitation à rejoindre un groupe). * * @returns - Résultat de l'API * @throws {ApiError} - En cas d'erreur de connexion ou d'invitation. * @private */ private _acceptLinkRequest; /** * Annule une demande de lien ou se déconnecte d'une entité. * * @returns - Résultat de l'API * @throws {ApiError} - En cas d'erreur de connexion ou d'invitation. * @private */ private _leaveLinkRequest; /** * ─────────────────────────────── * EntityMixin * ─────────────────────────────── */ /** * Récupérer le profil public de l'entité * * @returns - Les données de réponse. */ get(): Promise>; /** * Mettre à jour les paramètres d'un élément : Mise à jour des paramètres spécifiques d'un élément. * Constant : UPDATE_SETTINGS * @param data * @returns - Les données de réponse. * @throws {ApiResponseError} - En cas d'erreur détectée dans la réponse. * @throws {ApiAuthenticationError} - En cas d'erreur d'authentification. * @throws {Error} - En cas d'erreur inattendue. */ updateSettings(data: Omit): Promise; /** * Mettre à jour la description d'un élément : Permet de mettre à jour la description courte et complète d'un élément. * Constant : UPDATE_BLOCK_DESCRIPTION * @param data - Les données à envoyer. * @returns - Les données de réponse. * @throws {ApiResponseError} - En cas d'erreur détectée dans la réponse. * @throws {ApiAuthenticationError} - En cas d'erreur d'authentification. * @throws {Error} - En cas d'erreur inattendue. */ updateDescription(data: Omit): Promise; /** * Mettre à jour les informations d'un élément : Permet de mettre à jour les informations générales d'un élément (nom, contacts, etc.). * Constant : UPDATE_BLOCK_INFO * @param data - Les données à envoyer. * @returns - Les données de réponse. * @throws {ApiResponseError} - En cas d'erreur détectée dans la réponse. * @throws {ApiAuthenticationError} - En cas d'erreur d'authentification. * @throws {Error} - En cas d'erreur inattendue. */ updateInfo(data: Omit): Promise; /** * Mettre à jour les réseaux sociaux d'un élément : Permet de mettre à jour les liens vers les réseaux sociaux d'un élément. * Constant : UPDATE_BLOCK_SOCIAL * @param data - Les données à envoyer. * @returns - Les données de réponse. * @throws {ApiResponseError} - En cas d'erreur détectée dans la réponse. * @throws {ApiAuthenticationError} - En cas d'erreur d'authentification. * @throws {Error} - En cas d'erreur inattendue. */ updateSocial(data: Omit): Promise; /** * @private */ private _isPostalAddress; /** * @private */ private _isGeo; /** * @private */ private _isGeoPosition; /** * Mettre à jour les localités d'un élément : Permet de mettre à jour l'adresse et les informations géographiques d'un élément. * Constant : UPDATE_BLOCK_LOCALITY * @param data - Les données à envoyer. * @returns - Les données de réponse. * @throws {ApiResponseError} - En cas d'erreur détectée dans la réponse. * @throws {ApiAuthenticationError} - En cas d'erreur d'authentification. * @throws {Error} - En cas d'erreur inattendue. */ updateLocality(data: Omit): Promise; /** * Mettre à jour le slug d'un élément : Permet de mettre à jour le slug pour une URL simplifiée. * Constant : UPDATE_BLOCK_SLUG * @param data - Les données à envoyer. * @param data.slug - Le nouveau slug à appliquer. * @returns - Les données de réponse. */ updateSlug({ slug }: { slug: string; }): Promise; /** * Mettre à jour l'image de profil : Permet de mettre à jour l'image de profil d'un utilisateur ou d'une entité. * Constant : PROFIL_IMAGE * @param data - Les données à envoyer. * @param data.profil_avatar - L'image de profil à mettre à jour. * @returns - Les données de réponse. */ updateImageProfil({ profil_avatar: image }: { profil_avatar: UploadInput; }): Promise; /** * Mettre à jour l'image de bannière : Permet de mettre à jour l'image de bannière d'un utilisateur ou d'une entité. * Constant : PROFIL_BANNER * @param data - Les données à envoyer. * @param data.banner - L'image de bannière à mettre à jour. * @param data.cropW - Largeur du recadrage. * @param data.cropH - Hauteur du recadrage. * @param data.cropX - Position X du recadrage. * @param data.cropY - Position Y du recadrage. * @returns - Les données de réponse. */ updateImageBanner({ banner: image, cropW, cropH, cropX, cropY }: { banner: UploadInput; cropW: number; cropH: number; cropX: number; cropY: number; }): Promise; /** * Crée une instance d'organisation et récupère son profil si nécessaire. * * @param organizationData - Les données pour initialiser l'organisation. * - Si { id } ou { slug } : récupère l'organisation existante (GET) * - Sinon : crée une nouvelle instance (CREATE) * @returns Une promesse qui résout l'objet Organisation créé. * @throws {Error} Si une erreur se produit lors de la création de l'organisation. */ organization(organizationData?: OrganizationInput): Promise; /** * Crée une instance de projet et récupère son profil si nécessaire. * * @param projectData - Les données pour initialiser le projet. * - Si { id } ou { slug } : récupère le projet existant (GET) * - Sinon : crée une nouvelle instance (CREATE) * @returns Une promesse qui résout l'objet Projet créé. * @throws {Error} Si une erreur se produit lors de la création du projet. */ project(projectData?: ProjectInput): Promise; /** * Crée une instance de POI et la récupère si nécessaire. * * @param poiData - Les données pour initialiser le POI. * - Si { id } ou { slug } : récupère le POI existant (GET) * - Sinon : crée une nouvelle instance (CREATE) * @returns Une promesse qui résout l'objet POI créé. * @throws {Error} Si une erreur se produit lors de la création du POI. */ poi(poiData?: PoiInput): Promise; /** * Crée une instance de ressource classifiée (besoin ou offre). * * @param classifiedData - Les données pour initialiser la ressource. * - Si { id } : récupère la ressource existante (GET) * - Sinon : crée une nouvelle instance (CREATE) * @returns Une promesse qui résout l'objet Classified créé. * @throws {Error} Si une erreur se produit lors de la création. */ classified(classifiedData?: ClassifiedInput): Promise; /** * Crée une instance d'événement et la récupère si nécessaire. * * @param eventData - Les données pour initialiser l'événement. * - Si { id } ou { slug } : récupère l'événement existant (GET) * - Sinon : crée une nouvelle instance (CREATE) * @returns Une promesse qui résout l'objet Événement créé. * @throws {Error} Si une erreur se produit lors de la création de l'événement. */ event(eventData?: EventInput): Promise; /** * Crée une instance de badge et la récupère si nécessaire. * * @param badgeData - Les données pour initialiser le badge. * - Si { id } : récupère le badge existant (GET) * - Sinon : crée une nouvelle instance (CREATE) * @returns Une promesse qui résout l'objet Badge créé. * @throws {Error} Si une erreur se produit lors de la création du badge. */ badge(badgeData?: BadgeInput): Promise; /** * Crée une instance de news et la récupère si nécessaire. * * @param newsData - Les données pour initialiser la news. * - Si { id } : récupère la news existante (GET) * - Sinon : crée une nouvelle instance (CREATE) * @returns Une promesse qui résout l'objet News créé. * @throws {Error} Si une erreur se produit lors de la création de la news. */ news(newsData?: NewsInput): Promise; /** * Crée une instance d'Action et la récupère si nécessaire. * * Par défaut une action ne peut être créée que depuis un Project — les autres * entités lèvent une erreur. Override sur Project pour activer le comportement. * * @param actionData - Les données pour initialiser l'action. * @returns Une promesse qui résout l'objet Action créé. * @throws {ApiError} Sur les entités non supportées. */ action(_actionData?: ActionInput): Promise; /** * Crée une instance de Form **dans le contexte costum de l'entité courante**. * * Méthode autorisée uniquement sur `Organization` et `Project` (les entités qui * portent un costumContext). Les autres entités la bloquent via un override * qui throw — cf. `User.form()`, `Event.form()`, `Poi.form()`, etc. * * @param formData - `{ id: string }` (Form n'a pas de slug) * @returns Le Form, déjà fetché (`get()` est appelé par `entity()` quand `id` est fourni). * * @example * const org = await api.organization({ slug: "navigatorDesTierslieux" }); * const form = await org.form({ id: "6925e2b05dd63b02ca70d6d9" }); * console.log(form.serverData.name); // "Coworking" * const answers = await form.getAnswers(); // utilise le costumContext de l'org */ form(formData?: FormInput): Promise
; /** * Crée une instance d'Answer rattachée à l'entité courante. * * Méthode autorisée uniquement sur `Form` (la seule entité qui porte un * `id` de formulaire pré-rempli pour les drafts). Les autres entités la * bloquent via cet override par défaut qui throw. * * Pour fetch une Answer existante sans contexte Form, passer par * `EntityRegistry.createEntityFromData` ou un endpoint adhoc. * * @param _answerData - `{ id: string }` (fetch) ou objet partiel (draft). * @returns Une Answer. * @throws {ApiError} 501 sur les entités non supportées. */ answer(_answerData?: AnswerInput): Promise; /** * Récupérer les organisations d'une entitée : la liste des organisations dont l'entité est membre ou admin valide. * Constant : GET_ORGANIZATIONS_ADMIN | GET_ORGANIZATIONS_NO_ADMIN * @param data - Paramètres (partiels) de recherche/pagination. * @returns - Les données de réponse. */ getOrganizations(data?: Partial, options?: { restoredState?: PaginatorState; }): Promise>; /** * Récupérer les projets d'une entitée : liste des projets de l'entité ou elle est "parent" ou "contributeur". * Constant : GET_PROJECTS_ADMIN | GET_PROJECTS_NO_ADMIN * @param data - Paramètres (partiels) de recherche/pagination. * @returns - Les données de réponse. */ getProjects(data?: Partial, options?: { restoredState?: PaginatorState; }): Promise>; /** * Récupérer les événements d'une entitée : liste des événements de l'entité ou elle est "organizer" ou "attendee". * Constant : GET_EVENTS * `startDateUTC`/`endDateUTC` acceptent un `Date` ou une string ISO (un `Date` est coercé en ISO). * `recurrency` est typé `false` (garde parité, comme `searchEventsCostum`) : omettre (récurrents inclus * en mode calendrier) ou passer `false` (exclure) ; jamais `true` (le legacy `=== true` l'exclurait → divergence). * @param data - Paramètres (partiels) de recherche/pagination. * @returns - Les données de réponse. */ getEvents(data?: GetEventsInput, options?: { restoredState?: PaginatorState; }): Promise>; /** * Récupérer les POIs d'une entité : liste des POIs de l'entité ou elle est "parent". * Constant : GET_POIS_NO_ADMIN / GET_POIS_ADMIN * @param data - Paramètres (partiels) de recherche/pagination. * @returns - Les données de réponse. */ getPois(data?: Partial, options?: { restoredState?: PaginatorState; }): Promise>; /** * Récupérer les abonnés d'une entité * Constant : GET_SUBSCRIBERS * @param data - Paramètres (partiels) de recherche/pagination. * @returns - Les données de réponse. */ getSubscribers(data?: Partial, options?: { restoredState?: PaginatorState; }): Promise>; /** * Liste des badges créés par l'entité * Constant : GET_BADGES * @param data - Paramètres (partiels) de recherche/pagination. * @returns - Les données de réponse. */ getBadgesIssuer(data?: Partial, options?: { restoredState?: PaginatorState; }): Promise>; /** * Récupérer les actualités : Récupère la liste d'actualités selon plusieurs critères. * Constant : GET_NEWS * @param data - Paramètres (partiels) de recherche/pagination. * @returns - Les données de réponse. * @throws {ApiResponseError} - En cas d'erreur détectée dans la réponse. * @throws {Error} - En cas d'erreur inattendue. */ getNews(data?: Partial>): Promise; /** * Récupérer la galerie d'une entité. * Constant : GET_GALLERY * @param data - Paramètres (partiels) de recherche. * @returns - Les données de réponse. * @throws {ApiResponseError} - En cas d'erreur détectée dans la réponse. * @throws {Error} - En cas d'erreur inattendue. */ getGallery(data?: Partial> & { pathParams?: { docType?: "image" | "file"; }; }): Promise>; /** * Recherche des membres (utilisateurs ou organisations) liés à l'entité courante. * * Cette méthode effectue une recherche par autocomplétion parmi les membres de l'entité * (organisation, projet, événement). Les résultats sont filtrés selon le mode de recherche * et retournés sous forme d'entités User ou Organization liées à l'entité parente. * * @param {SearchMemberAutocompleteData} data - Paramètres de recherche * @param {string} data.search - Terme recherché (nom, slug, etc.) * @param {"personOnly" | "organizationOnly" | "mixte"} data.searchMode - Mode de recherche : * - `"personOnly"` : recherche uniquement parmi les utilisateurs * - `"organizationOnly"` : recherche uniquement parmi les organisations * - `"mixte"` : recherche parmi les utilisateurs et organisations * @returns {Promise<(User | Organization)[]>} Liste des entités correspondantes, liées à l'entité parente * @throws {ApiError} Si le type d'entité n'est pas supporté (badges, news, poi, comments) * * @example * ```typescript * // Rechercher des utilisateurs dans une organisation * const org = await me.organization({ slug: "myOrg" }); * const users = await org.searchMembers({ search: "jean", searchMode: "personOnly" }); * users.forEach(user => console.log(user.data.name)); * * // Rechercher des organisations membres d'un projet * const project = await me.project({ slug: "myProject" }); * const orgs = await project.searchMembers({ search: "asso", searchMode: "organizationOnly" }); * ``` * * @remarks * Les entités retournées sont automatiquement liées à l'entité parente via `_linkEntities`, * ce qui permet d'utiliser les méthodes comme `sendRequestToJoinParent()`, `isAdminPending()`, etc. */ searchMembers(data: SearchMemberAutocompleteData): Promise<(User | Organization)[]>; /** * Soumet une demande pour rejoindre l'entité courante (ex. organisation, projet, événement...). * Si une invitation est en attente, elle est automatiquement acceptée. * * @returns - Résultat de la demande ou de la validation. * @throws {ApiError} - Si l'entité ne supporte pas l'action ou si une demande est déjà en cours. */ requestToJoin(): Promise; /** * Soumet une demande pour devenir administrateur de l'entité courante. * Si une invitation est en attente, elle est automatiquement validée. * * @returns - Résultat de la demande ou de la validation. * @throws {ApiError} - Si l'entité ne supporte pas l'action ou si une demande est déjà en cours. */ requestToJoinAdmin(): Promise; /** * Envoie une demande de promotion en tant qu'administrateur pour l'utilisateur connecté. * * L'utilisateur doit déjà être membre de l'entité (organisation, projet, événement). * Cette méthode envoie une demande pour obtenir les droits d'administration. * * @returns - Résultat de l'API. * @throws {ApiError} 401 - Si l'utilisateur n'est pas connecté. * @throws {ApiError} 404 - Si l'entité n'est pas enregistrée. * @throws {ApiError} 400 - Si l'utilisateur n'est pas membre de l'entité. * @throws {ApiError} 400 - Si l'utilisateur est déjà en cours d'invitation admin. * @throws {ApiError} 400 - Si l'utilisateur est déjà administrateur. * @throws {ApiError} 400 - Si le type d'entité ne supporte pas cette opération. */ requestPromoteToAdmin(): Promise; /** * Accepte une invitation à rejoindre l'entité courante. * Ne fonctionne que si un lien avec `isInviting` est détecté. * * @returns - Résultat de l'acceptation de l'invitation. * @throws {ApiError} - Si aucune invitation n'est en attente ou si l'entité ne supporte pas cette action. */ acceptInvitation(): Promise; /** * Se désengage de l'entité courante : quitte un rôle (membre, contributeur, etc.). * * @returns - Résultat de la déconnexion. * @throws {ApiError} - Si l'entité ne supporte pas l'action ou si l'utilisateur n'est pas lié à cette entité. */ leave(): Promise; /** * S'abonne à l'entité courante. * * @returns - Résultat de l'abonnement. * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'entité n'est pas enregistrée. */ follow(): Promise; /** * Se désabonne de l'entité courante. * * @returns - Résultat de la désinscription. * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'entité n'est pas enregistrée. */ unfollow(): Promise; /** * Invite des personnes PAR EMAIL à rejoindre CETTE entité (organisation, projet ou événement). * * Méthode de haut niveau à PRÉFÉRER à un appel direct `endpointApi.inviteMembers`. * Wrappe INVITE_MEMBERS (POST /co2/link/multiconnect, `listInvite.invites`) : le backend * crée un citoyen « pending » par email puis envoie un mail d'invitation. Les clés des * invités sont des UUID v4 générés ici (le contrat l'exige). * * @param invites - Liste d'invités `{ name, mail, msg?, isAdmin?, roles? }` (clé `mail`, * comme le legacy `Link::invite` ; `isAdmin:"admin"` pour inviter en admin). * @returns Réponse de l'API (par invité : citoyens/invites/organizations). * @throws {ApiError} Si l'entité n'a pas d'id, n'est pas une orga/projet/événement, ou liste vide. */ inviteByEmail(invites: Array<{ name: string; mail: string; msg?: string; isAdmin?: "" | "admin"; roles?: string[]; }>): Promise; /** * L'entité porte-t-elle un contexte costum COMPLET (slug + costumId + costumType) ? * Vrai pour une entité chargée avec un `source.key` connu, ou après `setCostumScope()`. */ hasCostumScope(): boolean; /** * Pose EXPLICITEMENT le scope costum de l'entité — l'API OFFICIELLE pour rattacher une entité * HÔTE de costum (qui n'a pas de `source.key` : c'est elle la source, la lib ne lui auto-dérive * donc aucun `_costumCtx`) aux méthodes qui l'exigent (`_requireCostumCtx` : `previewImport`, * `importElements`, `exportElements`, `addReference`…). * * Résolution (`resolveCostumCtxFromSource`, LIVE-FIRST) : * 1. cache costum LIVE frais (via un `prefetchCostums`/`loadCostumScope` préalable) — champs inclus ; * 2. sinon le REGISTRE bundlé (warm-start) ; * 3. sinon un contexte NU construit depuis `opts` (costum hors cache/registre — schema vide, fields:[]) ; * 4. sinon throw (identité costum introuvable). NB : pour un costum hors registre AVEC ses champs, * préférer `loadCostumScope(slug)` (async) qui fetche `getcostumjson`. * * Remplace la mutation externe de `_costumCtx` pratiquée par les fronts (champ INTERNE non * contractuel) — cf. site-json `ensureCostumScope`, à migrer sur cette méthode. * * @param slug - Slug du costum (ex. `"equipementsSportifs974"`). * @param opts.costumId / opts.costumType - Identité de l'élément hôte, requise si le costum * n'est pas dans le registre bundlé (résolue par le front via slug/getinfo). */ setCostumScope(slug: string, opts?: { costumId?: string; costumType?: string; }): void; /** * Charge EN LIVE (via `getcostumjson`) le scope costum d'un slug HORS registre bundlé et pose le * contexte COMPLET (champs/presets/hidden) sur cette entité — pour l'ÉDITER avec ses champs costum * sans re-publier la lib (RFC fil C / option 3bis, comble l'édition vide). Contrairement à * `setCostumScope` (sync, registre/ctx nu), digère le typeObj du costum. No-op si le costum ne * couvre pas la collection de l'entité. Idempotent (cache session par slug). */ loadCostumScope(slug: string): Promise; /** * Contexte costum de CETTE entité (résolu de son `source.key`) — requis par l'import/export admin. * @throws {ApiError} si l'entité n'est pas rattachée à un costum. * @private */ private _requireCostumCtx; /** * PRÉVISUALISE un import (GEOCODAGE / Import::Geocodage) : géocode + valide chaque ligne SANS rien * insérer. À appeler AVANT `importElements` pour montrer à l'utilisateur les adresses résolues et les * erreurs/warnings, puis confirmer. `data` est JSON-encodé en interne ; la réponse (map par rowIndex) * est normalisée en tableau ordonné. (Le géocodage ne dépend pas du type d'élément.) * * @param rows - Lignes à prévisualiser (objets plats `{ name, address?, … }`). * @returns `{ rows: [{ rowIndex, success, data?, warnings?, msgErrorAddress? }] }`. */ previewImport(rows: Array>): Promise<{ rows: Array<{ rowIndex: string; success: boolean; data?: Record; warnings?: string[]; msgErrorAddress?: string; }>; }>; /** * Importe en masse des éléments SOUS le costum de cette entité (IMPORT_ELEMENTS / importData). * Méthode de haut niveau à PRÉFÉRER à `endpointApi.importElements`. `costumSlug/costumId/costumType` * sont dérivés du `_costumCtx` (source.key) — pas de paramètre costum à fournir. Les lignes sont * découpées en lots (`chunkSize`) pour la progression ; `data` est JSON-encodé en interne. Le backend * (re)vérifie + déduplique (update si le nom existe déjà) + insère + relie au costum. * * @param type - Collection cible (poi/organizations/projects/events/citoyens). * @param rows - Lignes à importer (objets plats `{ name, …champs }`). * @param opts.chunkSize - Taille de lot (def. 50). @param opts.onProgress - `(current,total)` par lot. * @returns `{ result, elements (par rowIndex), attributesWithTypes, progress, summary:{created,updated,errors} }`. */ importElements(type: "poi" | "organizations" | "projects" | "events" | "citoyens", rows: Array>, opts?: { chunkSize?: number; onProgress?: (current: number, total: number) => void; }): Promise<{ result: boolean; elements: Record; attributesWithTypes: Record; progress: { current: number; total: number; }; summary: { created: number; updated: number; errors: number; }; }>; /** Agrège la réponse d'import en `{créés, mis à jour, erreurs}` pour l'UI. @private */ private _summarizeImport; /** * Exporte les éléments du costum de cette entité (EXPORT_ELEMENTS / getCsvAdmin). Méthode haut niveau. * Scope automatiquement sur `source.key` = costum de l'entité (`_costumCtx.slug`). RÉSERVÉ aux * super-admins côté backend (gater l'UI via `me.isSuperAdmin()`). Renvoie `{ results, fields }` ; * utiliser le helper `toCsv(results, fields)` pour produire le CSV téléchargeable. * * @param params.searchType - Types/collections à exporter (+ name/filters/fields/labels/multicolumn optionnels). * @returns `{ results, fields }`. */ exportElements(params: { searchType: string[]; name?: string; filters?: Record; fields?: string[]; labels?: Record; multicolumn?: boolean; }): Promise<{ results: unknown[]; fields: unknown; }>; /** * (Dé)VALIDE un élément POUR le costum de cette entité (VALIDATE_GROUP / ValidateGroupAction). Méthode * haut niveau : `costumSlug` est dérivé du `_costumCtx` (source.key) — pas de paramètre costum à fournir. * (Dé)pose `preferences.toBeValidated[slug]` sur l'élément AVEC CASCADE sur les enfants liés * (`source.keys ∋ slug`, hors citoyens) et les classifieds `parent.`. * * @param type - Collection de l'élément (organizations/projects/poi/events/citoyens…). * @param id - ID de l'élément à (dé)valider. * @param valid - `true` = valider (retire le flag) ; `false` = dévalider (repose le flag). * @returns `{ result, elt (doc mis à jour), sub (map key→write-result des enfants) }`. */ validateGroup(type: string, id: string, valid: boolean): Promise<{ result: boolean; elt: unknown; sub: Record; }>; /** * Rattache l'élément (type,id) au costum de cette entité : ajoute son slug dans `reference.costum` * (référencement, en attente de validation). Port de SetSourceAction (add/reference). costumSlug dérivé du ctx. */ addReference(type: string, id: string): Promise<{ result: boolean; msg?: string; }>; /** Retire l'élément (type,id) de `reference.costum` du costum de cette entité + nettoie les liens. */ removeReference(type: string, id: string): Promise<{ result: boolean; msg?: string; }>; /** Détache l'élément (type,id) de `source.keys` du costum de cette entité (dé-rattachement) + nettoie les liens. */ removeFromSource(type: string, id: string): Promise<{ result: boolean; msg?: string; }>; /** * File de modération (news + commentaires signalés `reportAbuse`, non déjà modérés par l'utilisateur * courant). Port de ModerateAction (vue HTML → JSON). Niveau plateforme (ne dépend pas du costum). */ getModerationQueue(): Promise<{ result: boolean; news: unknown[]; comments: unknown[]; }>; /** * Détail consolidé des signalements d'un élément de la file de modération (NEWS_MODERATE / * COMMENT_MODERATE, subAction consolidateModerate{News,Comment}). * ⚠ Réponse legacy : `{ result: {text, reason, detail?} }` SANS booléen (clé PHP dupliquée écrasée) — * `reason` est `{}` → `[]` (array vide) quand il n'y a aucun signalement. * * @param context - `"news"` ou `"comments"`. * @param id - ID de l'élément signalé. */ consolidateModeration(context: "news" | "comments", id: string): Promise<{ result: { text: string | null; reason: Record | unknown[]; detail?: Record; }; }>; /** * Vote de modération sur un élément signalé (saveModerate) : pose `moderate.` + incrémente * `moderateCount`. Pour les NEWS, au seuil de 3 votes le backend tranche `isAnAbuse` à la * majorité (>49%) — une news « abus » est exclue des flux (pas supprimée). Les COMMENTS n'ont * pas de seuil (parité legacy : consolidation commentée). * * @param context - `"news"` ou `"comments"`. * @param id - ID de l'élément signalé. * @param isAnAbuse - Le vote (transmis en STRING 'true'|'false' — parité legacy). */ saveModeration(context: "news" | "comments", id: string, isAnAbuse: boolean): Promise<{ result: boolean; msg: string; }>; /** UUID v4 compatible browser + Node (`crypto.randomUUID()` si dispo, sinon fallback). @private */ private _uuidV4; /** * Vérifie si l'utilisateur est connecté et a accès à l'entité. * * @param action - Action à effectuer. * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si l'entité n'est pas enregistrée. * @private */ private _checkAccess; /** * Vérifie si l'utilisateur a un lien valide avec l'entité. * * @param userLink * @returns - `true` si le lien est valide, `false` sinon. * @protected */ protected _validateUserLink(userLink: MinimalUserLink | null | undefined): boolean; /** * Vérifie si l'entité est d'un type spécifique. * * @param types - Types d'entité attendus. * @throws {ApiError} - Si l'entité n'est pas du type attendu. * @protected */ protected _assertEntityType(...types: string[]): void; /** * Vérifie que l'entité N'EST PAS d'un type spécifique. * * @param methodName - Nom de la méthode (pour le message d'erreur). * @param excludedTypes - Types d'entité NON supportés (arguments individuels ou tableau). * @throws {ApiError} - Si l'entité est d'un type exclu. * @protected * @example * this._assertNotEntityType("method", "poi", "badges", "news"); * this._assertNotEntityType("method", ["poi", "badges", "news"]); */ protected _assertNotEntityType(methodName: string, ...excludedTypes: (string | string[])[]): void; /** * Valide les préconditions et retourne le lien utilisateur. * * @param methodName - Nom de la méthode appelante (pour les messages d'erreur). * @param expectedTypes - Types d'entité autorisés. * @param options - Options de validation. * @param options.silent - Si `true`, retourne `null` au lieu de lever une exception. Par défaut `true`. * @returns - Le lien de l'utilisateur connecté avec l'entité, ou `null` si validation échoue. * @throws {ApiError} - Si `silent` est `false` et que les préconditions ne sont pas remplies. * @protected */ protected _getValidatedUserLink(methodName: string, expectedTypes: string[], options?: { silent?: boolean; }): MinimalUserLink | null; /** * Vérifie si l'entité est liée à un type de lien spécifique. * * @param linkType - Type de lien à vérifier. * @returns - `true` si l'entité est liée, `false` sinon. * @protected */ protected _isLinked(linkType: string): boolean; /** * Vérifie si l'utilisateur est l'auteur de l'entité. * @param options - Options de vérification. * @param options.silent - Si `true`, retourne `false` au lieu de lever une exception. Par défaut `true`. * @returns - `true` si l'utilisateur est l'auteur, `false` sinon. * @throws {ApiError} - Si l'utilisateur n'est pas connecté ou si les données du serveur ne sont pas disponibles. */ isAuthor(options?: { silent?: boolean; }): boolean; /** * Vérifie si l'utilisateur est administrateur de l'entité. * * @param options - Options de vérification. * @param options.checkHierarchy - Si `true`, vérifie également si l'utilisateur est admin via la hiérarchie parent. Par défaut `false`. * @param options.silent - Si `true`, retourne `false` au lieu de lever une exception. Par défaut `true`. * @returns - `true` si l'utilisateur est administrateur, `false` sinon. * @throws {ApiError} - Si `silent` est `false` et que les préconditions ne sont pas remplies. */ isAdmin(options?: { checkHierarchy?: boolean; silent?: boolean; }): boolean; /** * Vérifie si l'utilisateur est soit l'auteur, soit administrateur de l'entité. * * @param options - Options de vérification. * @param options.silent - Si `true`, retourne `false` au lieu de lever une exception. Par défaut `true`. * @param options.checkHierarchy - Si `true`, propagé à `isAdmin` pour vérifier * également la hiérarchie parent (ex: `Answer.isAdmin({checkHierarchy: true})` * remonte vers org/project du Form parent). * @returns - `true` si l'utilisateur est l'auteur ou administrateur, `false` sinon. * @throws {ApiError} - Si `silent` est `false` et que les préconditions ne sont pas remplies. */ isAuthorOrAdmin(options?: { silent?: boolean; checkHierarchy?: boolean; }): boolean; /** * Vérifie si l'utilisateur est membre de l'entité. * * @param options - Options de vérification. * @param options.silent - Si `true`, retourne `false` au lieu de lever une exception. Par défaut `true`. * @returns - `true` si l'utilisateur est membre, `false` sinon. * @throws {ApiError} - Si `silent` est `false` et que les préconditions ne sont pas remplies. */ isMember(options?: { silent?: boolean; }): boolean; /** * Vérifie si l'utilisateur est contributeur de l'entité. * * @param options - Options de vérification. * @param options.silent - Si `true`, retourne `false` au lieu de lever une exception. Par défaut `true`. * @returns - `true` si l'utilisateur est contributeur, `false` sinon. * @throws {ApiError} - Si `silent` est `false` et que les préconditions ne sont pas remplies. */ isContributor(options?: { silent?: boolean; }): boolean; /** * Vérifie si l'utilisateur est participant de l'entité. * * @param options - Options de vérification. * @param options.silent - Si `true`, retourne `false` au lieu de lever une exception. Par défaut `true`. * @returns - `true` si l'utilisateur est participant, `false` sinon. * @throws {ApiError} - Si `silent` est `false` et que les préconditions ne sont pas remplies. */ isAttendee(options?: { silent?: boolean; }): boolean; /** * Vérifie si l'utilisateur suit l'entité. * * @param options - Options de vérification. * @param options.silent - Si `true`, retourne `false` au lieu de lever une exception. Par défaut `true`. * @returns - `true` si l'utilisateur suit l'entité, `false` sinon. * @throws {ApiError} - Si `silent` est `false` et que les préconditions ne sont pas remplies. */ isFollower(options?: { silent?: boolean; }): boolean; /** * Vérifie si l'utilisateur est abonné à l'entité. * * @param options - Options de vérification. * @param options.silent - Si `true`, retourne `false` au lieu de lever une exception. Par défaut `true`. * @returns - `true` si l'utilisateur est abonné, `false` sinon. * @throws {ApiError} - Si `silent` est `false` et que les préconditions ne sont pas remplies. */ isFollowing(options?: { silent?: boolean; }): boolean; /** * Vérifie si le lien de l'utilisateur connecté est en attente de validation. * * @param options - Options de vérification. * @param options.silent - Si `true`, retourne `false` au lieu de lever une exception. Par défaut `true`. * @returns - `true` si le lien est à valider, `false` sinon. * @throws {ApiError} - Si `silent` est `false` et que les préconditions ne sont pas remplies. */ isToBeValidated(options?: { silent?: boolean; }): boolean; /** * Vérifie si l'utilisateur connecté a été invité à rejoindre l'entité. * * @param options - Options de vérification. * @param options.silent - Si `true`, retourne `false` au lieu de lever une exception. Par défaut `true`. * @returns - `true` si l'utilisateur est invité, `false` sinon. * @throws {ApiError} - Si `silent` est `false` et que les préconditions ne sont pas remplies. */ isInviting(options?: { silent?: boolean; }): boolean; /** * Vérifie si l'utilisateur connecté a été invité en tant qu'administrateur. * * @param options - Options de vérification. * @param options.silent - Si `true`, retourne `false` au lieu de lever une exception. Par défaut `true`. * @returns - `true` si l'utilisateur est invité admin, `false` sinon. * @throws {ApiError} - Si `silent` est `false` et que les préconditions ne sont pas remplies. */ isInvitingAdmin(options?: { silent?: boolean; }): boolean; /** * Vérifie si l'utilisateur connecté est administrateur en attente de confirmation. * * @param options - Options de vérification. * @param options.silent - Si `true`, retourne `false` au lieu de lever une exception. Par défaut `true`. * @returns - `true` si l'utilisateur est admin en attente, `false` sinon. * @throws {ApiError} - Si `silent` est `false` et que les préconditions ne sont pas remplies. */ isAdminPending(options?: { silent?: boolean; }): boolean; /** * Récupère le JSON personnalisé de l'entité * * @returns - Le JSON personnalisé de l'entité. * @throws {ApiError} - Si le slug de l'entité n'est pas défini. */ getCostumJson(): Promise; /** * Génère des plages d'index pour la pagination. * * @param searchType - Types de recherche. * @param indexStep - Pas d'index. * @param previousRanges - Plages précédentes. * @returns - Plages d'index générées. * @private */ private _generateRanges; /** * Normalise le compte des résultats. * * @param count - Compte des résultats. * @returns - Compte normalisé. * @private */ private _normalizeCount; /** * Récupère une valeur par défaut depuis un endpoint donné. * * @param constant - Le nom unique de l'endpoint (ex: "GET_ORGANIZATIONS_NO_ADMIN") * @param path - Le chemin vers la propriété (ex: "searchType") * @returns La valeur par défaut, ou undefined si non trouvée */ _getDefaultFromEndpoint(constant: string, path: string): unknown; /** * Coeur de pagination stateless et réutilisable. */ _createPaginatorEngine, TOut>({ initialData, finalizer, methodName, restoredState }: { initialData: Partial; finalizer: (data: TData) => Promise>; methodName: string; restoredState?: PaginatorState; }): { next: () => Promise>; }; /** * Injection de contexte Communecter dans une requête finalizer. */ _withCostumContext, TOut>(baseFinalizer: (data: TIn & CostumContextFields) => Promise): (data: TIn) => Promise; /** * ─────────────────────────────── * custom * ─────────────────────────────── */ /** * Recherche globale liée au *costum* (stateless). * Injecte automatiquement le contexte (slug/id/type) via `_withCostumContext`, * puis pagine via `_createPaginatorEngine`. Cette méthode renvoie **directement * la première page** (rétro-compatible) avec `.results`, `.count`, et des helpers * de pagination si disponibles. * * Remarque : les éléments de `results` peuvent être hétérogènes (multi-collections) * et éventuellement « liés » en entités par le moteur interne de pagination. * * @param data * Paramètres de recherche (partiels — les valeurs manquantes sont complétées * par le moteur de pagination et le contexte). * @returns * Première page paginée : `results` (T[]), `count.total`, `hasNext`, etc. * @throws {ApiResponseError} * @throws {Error} * * @example * // Recherche simple (première page) * const page = await entity.searchCostum({ name: "marseille", searchType: ["projects"], indexStep: 12, indexMin: 0 }); * console.log(page.results, page.count.total); * * @example * // Paginer * if (page.hasNext) { * const nextPage = await page.next(); * console.log(nextPage.pageNumber, nextPage.results.length); * } * * @example * // Cibler l'endpoint navigator/gettl (mêmes paramètres et même format de réponse) * const page = await entity.searchCostum( * { name: "marseille", searchType: ["projects"] }, * { variant: "navigator-tl" } * ); */ searchCostum(data?: Partial, options?: { restoredState?: PaginatorState; variant?: SearchCostumVariant; }): Promise>; /** * Agenda costum-aware : events scopés au costum (`source.key`) — pendant de `searchCostum` pour les events. * `_withCostumContext` injecte `costumSlug`/`contextId`/`contextType` + `sourceKey=[serverData.slug]` → les * events DU site/hôte costum (au lieu du filtre attendees/organizer FORCÉ de `getEvents`). À appeler sur * l'entité HÔTE du costum (ex. l'entité du déploiement). `sourceKey` surchargeable (agenda multi-sites / réseau). * * **Deux modes** (endpoint `/co2/search/agenda`), selon que tu passes une date ou non : * * 1. **CALENDRIER** — déclenché par `startDateUTC` et/ou `endDateUTC` (l'agenda d'une période) : * ramène **TOUT** ce qui matche la plage — ponctuels **+ récurrents** (`openingHours`, sauf `recurrency:false`) — * trié par **occurrence** (`startDateSort`/`startDateSortFormat` calculés côté serveur), résultat = **tableau**. * **Pas de pagination** serveur : une seule page (`next()`/`prev()` sans effet). * 2. **LISTE** — déclenché par l'**absence** de date (flux paginé) : * **ponctuels uniquement** (récurrents exclus), triés par `startDate` **décroissant** (plus récents d'abord), * paginés via `indexMin`/`indexStep` (défaut 30) — `next()`/`prev()` fonctionnent. (Backend : objet keyé `_id`, * hydraté en tableau d'`Event` côté lib.) * * Le scope costum (`sourceKey`) s'applique dans les **deux** modes. * * `recurrency` est typé `false` (garde parité) : **omettre** (mode calendrier → récurrents inclus) ou passer * `false` (exclure) ; **jamais `true`** — le legacy fait un test strict `=== true` qui exclurait les récurrents * (divergence), valeur donc interdite au type **et** à l'AJV. * * `startDateUTC`/`endDateUTC` acceptent un **`Date` ou une string ISO** : un `Date` est coercé en ISO * (`toISOString()`) avant l'envoi (le fil reste `type:string`). Évite le piège de l'ISO partielle * (`"2026-06-01"` n'est pas un `date-time` AJV valide). * // Mode CALENDRIER — agenda du mois, avec de vrais Date * const debut = new Date("2026-06-01T00:00:00Z"); * const page = await org.searchEventsCostum({ startDateUTC: debut, endDateUTC: new Date("2026-06-30T23:59:59Z") }); * page.results.forEach((e) => console.log(e.serverData.name, e.serverData.startDateSort)); * // Mode LISTE — flux paginé (ponctuels, du plus récent) * let p = await org.searchEventsCostum({ indexStep: 20 }); * if (p.hasNext) p = await p.next(); */ searchEventsCostum(data?: SearchEventsCostumInput, options?: { restoredState?: PaginatorState; }): Promise>; /** * @param data * Paramètres de recherche (partiels — les valeurs manquantes sont complétées * par le moteur de pagination et le contexte). * @returns * Première page paginée : `results` (T[]), `count.total`, `hasNext`, etc. * @throws {ApiResponseError} * @throws {Error} */ costumEventRequestActors(data?: Partial>): Promise; /** * @param data * Paramètres de recherche (partiels — les valeurs manquantes sont complétées * par le moteur de pagination et le contexte). * Première page paginée : `results` (T[]), `count.total`, `hasNext`, etc. * @throws {ApiResponseError} * @throws {Error} */ costumEventRequestSubevents(data?: Partial>): Promise; /** * @param data * Paramètres de recherche (partiels — les valeurs manquantes sont complétées * par le moteur de pagination et le contexte). * @returns * Première page paginée : `results` (T[]), `count.total`, `hasNext`, etc. * @throws {ApiResponseError} * @throws {Error} */ costumEventRequestDates(data?: Partial>): Promise; /** * @param data * Paramètres de recherche (partiels — les valeurs manquantes sont complétées * par le moteur de pagination et le contexte). * @returns * Première page paginée : `results` (T[]), `count.total`, `hasNext`, etc. * @throws {ApiResponseError} * @throws {Error} */ costumEventRequestElementEvent(data?: Partial>): Promise; /** * @param data * Paramètres de recherche (partiels — les valeurs manquantes sont complétées * par le moteur de pagination et le contexte). * @returns * Première page paginée : `results` (T[]), `count.total`, `hasNext`, etc. * @throws {ApiResponseError} * @throws {Error} */ costumEventRequestCategories(data?: Partial>): Promise; /** * @param data * Paramètres de recherche (partiels — les valeurs manquantes sont complétées * par le moteur de pagination et le contexte). * @returns * Première page paginée : `results` (T[]), `count.total`, `hasNext`, etc. * @throws {ApiResponseError} * @throws {Error} */ costumEventRequestEvent(data?: Partial>): Promise; /** * @param data * Paramètres de recherche (partiels — les valeurs manquantes sont complétées * par le moteur de pagination et le contexte). * @returns * Première page paginée : `results` (T[]), `count.total`, `hasNext`, etc. * @throws {ApiResponseError} * @throws {Error} */ costumEventRequestLinkTlToEvent(data: Pick & Partial>): Promise; /** * @param data * Paramètres de recherche (partiels — les valeurs manquantes sont complétées * par le moteur de pagination et le contexte). * @returns * Première page paginée : `results` (T[]), `count.total`, `hasNext`, etc. * @throws {ApiResponseError} * @throws {Error} */ costumEventRequestLoadContextTag(data: Partial> & Pick): Promise; /** * Recherche paginée des réponses CoForm liées à l'entité courante. * Injecte automatiquement le contexte (costumSlug, contextId, contextType) via `_withCostumContext`, * puis pagine via `_createPaginatorEngine`. * * `searchType` est pré-rempli à `["answers"]` si non fourni. * Les autres champs required (`indexStep`, `count`, `initType`, `fediverse`) * sont injectés automatiquement par AJV via les `default` du schema. * * @param data - Paramètres de recherche (partiels). Seuls `filters` et `fields` sont * généralement nécessaires côté appelant. * @param options - Options de pagination * @param options.restoredState - État de pagination à restaurer (pour reprendre une navigation) * @returns Première page paginée avec `results` (Answer[]), `count`, `hasNext`, `hasPrev`, etc. * * @example * const page = await organization.coformAnswersSearch({ * filters: { form: "6927e9f0db22da724360303c" }, * fields: ["vote", "voteCount", "user"], * sortBy: { created: -1 } * }); * page.results; // Answer[] * page.count.total; // nombre total de résultats * page.hasNext; // true si une page suivante existe */ coformAnswersSearch(data?: Partial, options?: { restoredState?: PaginatorState; }): Promise>; /** * Récupère les réponses pour plusieurs formulaires CoForm. * Chaque entrée du résultat contient les `answers` liées en entités * et les `documents` associés. * * @param data - Paramètres de recherche * @param data.forms - Objet `{ [formId]: pathDeRecherche }` — au moins une entrée requise. * Chaque clé est un ObjectID de formulaire, chaque valeur est un chemin * dot-notation vers le champ answers (ex: `"answers.xxx.finderxxx.orgId"`). * @returns Tableau d'items par formulaire, avec `answers` transformées en entités * @throws {ApiError} Si `forms` est absent, vide, ou n'est pas un objet * * @example * const results = await organization.searchAnswersByForms({ * forms: { * "646498a5f2b3d6423d74b7f6": "answers.formName.finderPath.orgId", * "645b300b6d70bb35426fc0e3": "answers.formName.finderPath.orgId" * } * }); */ searchAnswersByForms(data?: Partial): Promise<{ answers: Answer[]; documents: any[]; [k: string]: unknown; }[]>; /** * Récupère les informations d'enveloppes de financement pour l'entité courante. * Injecte automatiquement le contexte (costumSlug, contextId, contextType) via `_withCostumContext`. * * Les champs `contextData`, `context`, `nopropProject`, `projects` et `userOrga` * de la réponse sont automatiquement liés en entités quand possible. * * @param data - Paramètres optionnels de recherche * @param data.action - Action à effectuer (`"getEnvelopeData"`, `"getFormData"`, etc.) * @param data.formId - ID du formulaire cible (pour `action: "getFormData"`) * @param data.financerId - ID du financeur pour filtrer * @param data.financerType - Type du financeur (ex: `"citoyens"`) * @param data.params - Paramètres supplémentaires (ex: `{ project: "all" }`) * @returns Données d'enveloppe avec entités liées * * @example * const envelope = await organization.fundingEnvelope({ * action: "getEnvelopeData" * }); * envelope.form; // données du formulaire * envelope.projects; // Project[] liés */ fundingEnvelope(data?: Partial>): Promise; /** * Récupère les filtres disponibles basés sur les réponses CoForm de l'entité courante. * Utilise le contexte Communecter (costumSlug, contextId, contextType) de l'entité. * * La réponse est un objet indexé par nom de filtre, chaque entrée contenant : * - `count` : nombre de résultats par catégorie * - `results` : valeurs disponibles pour ce filtre (libellé, image, etc.) * * @param data - Paramètres optionnels de recherche * @param data.searchedData - Filtres actifs : clé → `{ label, forms, path, finderPath }` * @returns Objet `Record` des filtres disponibles * * @example * const filters = await project.coformFiltersSearch({ * searchedData: { * "activités": { * label: "Activités", * forms: "6486d24e9cad105cbf29a777", * path: "lesCommunsDesTierslieux...", * finderPath: "answers.lesCommunsDesTierslieux..." * } * } * }); * filters["activités"].count; // { "Atelier": 12, "Coworking": 8 } */ coformFiltersSearch(data?: Partial): Promise; /** * Récupère les valeurs distinctes d'**une seule** thématique CoForm, query pilotée * côté client via `params.thematicPath` (+ `params.finderPath` optionnel). * * Distinct de {@link coformFiltersSearch} (qui passe `searchedData` et fait le batch * multi-filtres piloté serveur) : ici on cible UN filtre précis et on construit * soi-même la projection (`fields`) et les `filters`. Côté backend, c'est la branche * `isset($_POST["params"])` du controller AutoGlobalThematicNtwrkAction. * * Utile pour un **filtre par réseau régional/thématique** : on combine `params` avec * `notSourceKey: true` (cherche dans tout le réseau) ou `sourceKey: [""]` * (scope un réseau précis — cf. `_withCostumContext`). * * @param data - `params.thematicPath` est requis ; le reste (fields/filters/searchType/ * sortBy/indexMin/indexStep/notSourceKey/locality) est optionnel. * @returns {@link CoformFilterByPathResult} : `{ distinctElements, values, count }`. * Reconstruit proprement la map backend que le normalizer générique aplatit * (la clé `results` est traitée comme wrapper de liste par `_transformData`). * @throws {ApiError} 400 si `params.thematicPath` est absent. * * @example * const res = await project.coformFilterByPath({ * params: { * thematicPath: "lesCommunsDesTierslieux17102023_108_0.multiCheckboxPlus...", * finderPath: "answers.lesCommunsDesTierslieux17102023_108_0.finder...", * }, * fields: [ * "answers.lesCommunsDesTierslieux17102023_108_0.multiCheckboxPlus...", * "answers.lesCommunsDesTierslieux17102023_108_0.finder...", * ], * filters: { * "answers.lesCommunsDesTierslieux17102023_108_0.multiCheckboxPlus...": { $exists: true }, * }, * searchType: ["answers"], * notSourceKey: true, * }); * res.count.answers; // 14 * res.values[0].name; // "Cafés cantines solidaires" * res.values[0].orgaNameArray; // ["5b740..."] * res.distinctElements; // string[] des _id concernés */ coformFilterByPath(data: Partial & { params: CostumFilterCoformByPathData["params"]; }): Promise; /** * Recherche des zones géographiques selon un pays et un niveau administratif. * Utilise le contexte Communecter de l'entité courante. * * @param data - Paramètres de recherche * @param data.countryCode - Code(s) pays ISO (ex: `["FR"]`) * @param data.level - Niveau(x) administratif(s) (ex: `["1"]`) * @returns Liste des zones correspondantes * @throws {ApiError} Si `countryCode` ou `level` sont absents */ searchZone(data: SearchZonesData): Promise; /** * Récupère la liste de tous les pays (endpoint public, sans authentification). * * @param data - Paramètres optionnels (costumSlug, costumId, costumType) * @returns Liste des pays `{ name, countryCode, level, geo, ... }` * * @example * const countries = await organization.getCountries(); * countries[0].name; // "France" * countries[0].countryCode; // "FR" */ getCountries(data?: Partial): Promise; /** * Génère côté backend une nouvelle Answer placeholder rattachée au formulaire * donné (insert en BDD avec nouvel ObjectId) et retourne l'instance Answer * connectée (parent = `this`). * * **Note workflow** : l'Answer renvoyée est en état "placeholder" — pas de * `user` posé côté backend tant que `save()` n'a pas été appelé. Pour * récupérer plus tard via `coformAnswersById`, faire `save()` avec au moins * `answers` posés. * * @param formId - ID du formulaire (24 hex) * @returns Instance `Answer` connectée à `this` (parent = entité courante) * @throws {ApiError} 400 si `formId` est absent ou invalide * * @example * const org = await api.organization({ slug: "monOrga" }); * const answer = await org.generateNewAnswerId(formId); * await answer.updateField(`${finder}.${org.id}`, { id: org.id, type: "organizations", name: org.serverData.name }); */ generateNewAnswerId(formId: string): Promise; /** * Associe un compte Discourse à l'utilisateur courant dans le contexte de l'instance. * Injecte automatiquement le `costumSlug` via `_withCostumContext`. * * @param username - Nom d'utilisateur Discourse à associer * @returns Résultat de la liaison avec les informations du compte Discourse trouvé * @throws {ApiError} Si `username` est absent ou invalide * * @example * const result = await user.linkDiscourseAccount("john_doe"); * if (result.result) { * console.log(result.profileUrl); // URL du profil Discourse * } else { * console.error(result.error); * } */ linkDiscourseAccount(username: string): Promise<{ result: boolean; error?: string; username?: string; profileUrl?: string; }>; /** * Dissocie le compte Discourse de l'utilisateur courant dans le contexte de l'instance. * Injecte automatiquement le `costumSlug` via `_withCostumContext`. * * @returns Résultat du délien du compte Discourse * * @example * const result = await user.unlinkDiscourseAccount(); * result.result; // true si succès */ unlinkDiscourseAccount(): Promise<{ result: boolean; error?: string; }>; /** * Récupère le profil Discourse d'un utilisateur dans le contexte de l'instance. * Injecte automatiquement le `costumSlug` via `_withCostumContext`. * * @param username - Nom d'utilisateur Discourse cible * @returns Profil Discourse avec `summary` et `profileUrl`, ou `error` en cas d'échec * @throws {ApiError} Si `username` est absent ou invalide * * @example * const result = await user.getDiscourseProfile("john_doe"); * result.profileUrl; // "https://forum.example.org/u/john_doe/summary" */ getDiscourseProfile(username: string): Promise<{ summary?: Record; profileUrl?: string; error?: string; }>; /** * Vérifie si l'email de l'utilisateur connecté correspond à un compte Discourse. * Injecte automatiquement le `costumSlug` via `_withCostumContext`. * L'email utilisé est celui de l'utilisateur connecté (géré côté serveur). * * @returns `{ found: true, user }` si un compte correspondant existe, `{ found: false }` sinon * * @example * const result = await user.checkDiscourseEmailMatch(); * if (result.found) { * console.log(result.user); // infos du compte Discourse trouvé * } */ checkDiscourseEmailMatch(): Promise<{ found: boolean; user?: Record; }>; /** * Ignore la suggestion de liaison de compte Discourse pour l'instance courante. * Persiste le refus côté serveur (`interop.discourse[costumSlug] = false`). * Injecte automatiquement le `costumSlug` via `_withCostumContext`. * * @returns `{ result: true }` si le dismiss a réussi * * @example * const result = await user.dismissDiscourseLink(); * result.result; // true */ dismissDiscourseLink(): Promise<{ result: boolean; error?: string; }>; /** * Associe un compte MediaWiki à l'entité courante dans le contexte de l'instance. * Injecte automatiquement le `costumSlug` via `_withCostumContext`. * * @param username - Nom d'utilisateur MediaWiki à associer * @returns Résultat de la liaison avec les informations du compte MediaWiki trouvé * @throws {ApiError} Si `username` est absent ou invalide * * @example * const result = await user.linkMediaWikiAccount("John_Doe"); * if (result.result) { * console.log(result.username); // nom d'utilisateur confirmé * } else { * console.error(result.error); * } */ linkMediaWikiAccount(username: string): Promise<{ result: boolean; error?: string; username?: string; msg?: string; }>; /** * Dissocie le compte MediaWiki de l'entité courante dans le contexte de l'instance. * Injecte automatiquement le `costumSlug` via `_withCostumContext`. * * @returns Résultat du délien du compte MediaWiki * * @example * const result = await user.unlinkMediaWikiAccount(); * result.result; // true si succès */ unlinkMediaWikiAccount(): Promise<{ result: boolean; error?: string; msg?: string; }>; /** * Récupère les contributions MediaWiki d'un utilisateur dans le contexte de l'instance. * Injecte automatiquement le `costumSlug` via `_withCostumContext`. * * @param username - Nom d'utilisateur MediaWiki cible * @param limit - Nombre maximum de contributions à retourner (optionnel) * @returns Liste des contributions MediaWiki, ou `error` en cas d'échec * @throws {ApiError} Si `username` est absent ou invalide * * @example * const result = await user.getMediaWikiContributions("John_Doe", 10); * result.contribs; // objet contenant les contributions */ getMediaWikiContributions(username: string, limit?: number): Promise<{ result: boolean; contribs?: Record; }>; /** * Wrapper pour l'endpoint COREMU_OPERATION : génère une proposition à partir * d'un formulaire et d'un projet. Cette méthode injecte les `const` du schéma * (`answer: "new"`, `action: "generateproposition"`) et délègue à l'EndpointApi. * * @param data.form - ID Mongo (24 hex) du formulaire * @param data.project - ID Mongo (24 hex) du projet * @returns Résultat brut de l'API (ou entité liée si `collection` est présent) * @throws {ApiError} Si les paramètres sont manquants ou invalides */ coremuOperation(data: Pick): Promise; /** * ─────────────────────────────── * Pagination restoration methods * ─────────────────────────────── */ /** * Restaure une pagination depuis des données JSON sérialisées. * Ne fait PAS d'appel API - utilise les results déjà présents. * Les fonctions next() et prev() font des appels API quand appelées. * * @param paginationJson - Les données JSON sérialisées d'une pagination * @param apiClientOrEntity - ApiClient ou entité parente pour la reconstruction des entités * @returns Une page de pagination hydratée avec next() et prev() fonctionnels * * @example * // Sérialiser une pagination * const firstPage = await user.getPois(); * const json = JSON.stringify(firstPage); * * // Restaurer la pagination (sans appel API) - passer l'ApiClient * const restored = BaseEntity.restorePaginationFromJSON(JSON.parse(json), api.getClientInstance()); * * // next() fait un appel API pour récupérer la page suivante * const secondPage = await restored.next?.(); */ static restorePaginationFromJSON(paginationJson: any, apiClientOrEntity: ApiClient | BaseEntity): PaginatorPage; } export default BaseEntity;