Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
022cffe
task-88259: Create FB ai-contribution and update projects versions/de…
aycherif Jul 2, 2026
74354b6
feat: Relocate Document MCP tools into the Documents add-on
bmestrallet Jul 6, 2026
d9a3577
fix: Move Document MCP model records into the Documents add-on
bmestrallet Jul 6, 2026
2a61785
feat: Add v1 batch of Documents MCP tools (folders, versions, content…
bmestrallet Jul 6, 2026
5bfd7ac
fix: create_folder under root folder fails in Documents MCP tools
bmestrallet Jul 6, 2026
ca10f7e
feat: Add create_document/upload_document MCP tools using UploadToolU…
bmestrallet Jul 7, 2026
4c69e80
feat: upload_document accepts chat attachments + create_document hono…
bmestrallet Jul 7, 2026
5818f5a
revert: drop JCR mime-forcing in create_document; content-type follow…
bmestrallet Jul 7, 2026
d50cb77
refactor: use renamed UploadToolUtils.FetchedContent - EXO-88354
bmestrallet Jul 7, 2026
c19160e
feat: clear error messages for create_document/upload_document bad in…
bmestrallet Jul 7, 2026
5fd4b10
refactor: drop mime_type from create_document; require a filename wit…
bmestrallet Jul 7, 2026
cf7146a
feat: add version, share, public-link, shortcut and favorite Document…
bmestrallet Jul 7, 2026
868ae69
fix: correct integration bugs in Documents MCP tools (conflict values…
bmestrallet Jul 7, 2026
a3cc4bb
feat: add create_document_from_template MCP tool (delegates to ecms D…
bmestrallet Jul 7, 2026
262d0f8
fix: re-verify follow-ups — template extension match, share no-downgr…
bmestrallet Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What this is

eXo Documents is an add-on for the eXo Platform: the documents/files management feature
(drives, file timeline/folder browsing, trash, public access links, attachments, previews,
WebDAV access, search and notifications). It is a multi-module Maven project whose backend
runs inside the eXo Platform Kernel (an IoC container configured via XML) and whose frontend
is a set of Vue 2 portlet apps bundled with Webpack.

## Build & test

JDK 21 and Maven. The project inherits from `org.exoplatform:maven-exo-parent-pom` and depends
on `org.exoplatform.jcr:jcr-parent` (7.2.x-SNAPSHOT). Most dependencies resolve from
`https://repository.exoplatform.org/public`, so a network/Nexus connection is required.

```bash
# Full build (compiles Java, runs the frontend build via frontend-maven-plugin, runs tests)
mvn clean install

# Build skipping tests
mvn clean install -DskipTests

# CI build profiles (see .github/workflows/prbuild.yml)
mvn clean install -Pexo-release,coverage

# Run the test suite for one module
mvn test -pl documents-services

# Run a single test class / single method
mvn test -pl documents-services -Dtest=DocumentFileServiceTest
mvn test -pl documents-services -Dtest=DocumentFileServiceTest#methodName
```

Frontend (run inside `documents-webapp/`; also wired into the Maven build via frontend-maven-plugin):

```bash
npm run build # production webpack build -> target/documents-portlet/
npm run watch # webpack --watch
npm run lint # eslint --fix over src/main/webapp/vue-app
npm run eslint-check # eslint without fixing (CI check)
```

## Module architecture

The Maven reactor builds these modules in order; the dependency direction is API ← storage/services ← webapp:

- **documents-api** — pure contracts: service interfaces (`service/`), storage SPI interfaces
(`storage/`), and the domain model POJOs (`model/`) + constants. No implementations here.
Both other backend modules depend on this.
- **documents-storage-jcr** — implements the storage SPI against eXo JCR (Java Content Repository).
This is where documents physically live as JCR nodes. Contains bulk actions, JCR search,
WebDAV node handling, and the legacy search layer.
- **documents-services** — implements the service interfaces, REST endpoints (`rest/`), DAOs/entities
(RDBMS via JPA, e.g. for public-access links), event listeners (`listener/`), notification
plugins/providers (`notification/`), filters, and websocket service.
- **documents-webapp** — the portlet WAR: Vue frontend (`src/main/webapp/vue-app/`) plus the eXo
Kernel/portlet wiring under `WEB-INF/conf` and `WEB-INF/*.xml`.
- **documents-webdav-webapp** — separate WAR exposing documents over WebDAV.
- **documents-packaging** — assembles the deployable add-on zip.

### Backend wiring (important)

There is no Spring/CDI. Components are registered with the **eXo Kernel container** through XML in
`documents-webapp/src/main/webapp/WEB-INF/conf/documents/`. To add or change a backend service,
listener, REST resource, search connector, or filter, you must edit the corresponding
`*-configuration.xml` (chiefly `documents-service-configuration.xml`) — registering the class in
code alone does nothing. The pattern is `<component>` (key = interface, type = impl) and
`<external-component-plugins>` (attaching plugins/listeners to a target component such as
`ListenerService`, `SpaceService`, `SearchService`, `ExtensibleFilter`).

The layering at runtime: REST (`DocumentFileRest`) → Service impl (`DocumentFileServiceImpl`,
`PublicDocumentAccessServiceImpl`, `ExternalDownloadServiceImpl`) → Storage SPI
(`DocumentFileStorage`, `TrashStorage`, `JCRDeleteFileStorage`, `PublicDocumentAccessStorage`) →
JCR / JPA implementations in documents-storage-jcr and documents-services.

### Frontend architecture

Each subdirectory of `documents-webapp/src/main/webapp/vue-app/` is an independent Vue 2 app with a
`main.js` entry, registered as a separate Webpack bundle in `webpack.prod.js` (see the `entry` map —
e.g. `documents`, `attachment`, `trash-management`, `files-search`, `restrictedDrive`, the offline
and pwa extensions). Bundles output to `target/documents-portlet/js/[name].bundle.js` with
`libraryTarget: 'amd'`. `vue`, `vuetify`, and `jquery` are **externals** provided by the host
platform, not bundled. ESLint uses `eslint-config-meedsio` plus the Vue and vuejs-accessibility
plugins.

## Conventions

- Backend tests use JUnit/Mockito and live in `src/test/java` mirroring the package of the class
under test. They run inside a mocked Kernel context, not a live container.
- i18n: resource bundles are managed through Crowdin (`crowdin.yml`, `translations.properties`, and
the `*-crowdin*.yml` workflows). Edit the source English bundle; translations sync via CI.
- Version is `7.2.x-SNAPSHOT`; the default branch is `develop` and PRs target it.
- JIRA issue keys (e.g. `EXO-XXXXX`) are referenced in commit/PR titles following the
`type: summary - EXO-XXXXX` convention seen in git history.
2 changes: 1 addition & 1 deletion documents-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<groupId>org.exoplatform.documents</groupId>
<artifactId>documents-parent</artifactId>
<version>7.3.x-SNAPSHOT</version>
<version>7.3.x-ai-contribution-SNAPSHOT</version>
</parent>
<artifactId>documents-api</artifactId>
<name>eXo Documents - API</name>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,20 @@ void updatePermissions(String documentId,

AbstractNode createFolder(long ownerId,String folderId, String folderPath, String name, long authenticatedUserId) throws IllegalAccessException, ObjectAlreadyExistsException, ObjectNotFoundException;

/**
* Creates a new empty office document (Word/Excel/PowerPoint/ODF) from the
* platform's blank template, delegating to the ecms {@code DocumentService}.
*
* @param ownerId owner identity id (used when folderId is blank)
* @param folderId identifier of the parent folder node
* @param folderPath optional relative path under the parent folder
* @param title document title (should already include the file extension)
* @param documentType office document extension (docx, xlsx, pptx, odt, ods, odp)
* @param authenticatedUserId identity id of the user creating the document
* @return the created {@link AbstractNode}
*/
AbstractNode createDocumentFromTemplate(long ownerId, String folderId, String folderPath, String title, String documentType, long authenticatedUserId) throws IllegalAccessException, ObjectNotFoundException, ObjectAlreadyExistsException;

String getNewName(long ownerId, String folderId, String folderPath, String name) throws IllegalAccessException, ObjectAlreadyExistsException, ObjectNotFoundException;

void renameDocument(long ownerId, String documentID, String name, long authenticatedUserId) throws IllegalAccessException, ObjectAlreadyExistsException, ObjectNotFoundException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,30 @@ void moveDocument(Session session,
AbstractNode createFolder(long ownerId, String folderId, String folderPath, String title, Identity aclIdentity) throws IllegalAccessException, ObjectAlreadyExistsException,
ObjectNotFoundException;

/**
* Creates a new empty office document (Word/Excel/PowerPoint/ODF) from the
* platform's blank template, by delegating to the ecms {@code DocumentService}.
*
* @param ownerId owner identity id (used when folderId is blank to resolve the
* identity root folder)
* @param folderId identifier of the parent folder node
* @param folderPath optional relative path under the parent folder
* @param title document title (should already include the file extension)
* @param documentType office document extension (e.g. docx, xlsx, pptx, odt,
* ods, odp) matched against the installed template providers
* @param aclIdentity ACL identity of the user creating the document
* @return the created {@link AbstractNode}
* @throws IllegalAccessException when the user isn't allowed to edit the parent
* folder
* @throws ObjectNotFoundException when the parent folder or a matching template
* doesn't exist
* @throws ObjectAlreadyExistsException when a document with the same name
* already exists in the target folder
*/
AbstractNode createDocumentFromTemplate(long ownerId, String folderId, String folderPath, String title, String documentType, Identity aclIdentity) throws IllegalAccessException,
ObjectNotFoundException,
ObjectAlreadyExistsException;

String getNewName(long ownerId,
String folderId,
String folderPath,
Expand Down
2 changes: 1 addition & 1 deletion documents-packaging/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.exoplatform.documents</groupId>
<artifactId>documents-parent</artifactId>
<version>7.3.x-SNAPSHOT</version>
<version>7.3.x-ai-contribution-SNAPSHOT</version>
</parent>
<artifactId>documents-packaging</artifactId>
<packaging>pom</packaging>
Expand Down
34 changes: 33 additions & 1 deletion documents-services/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<parent>
<groupId>org.exoplatform.documents</groupId>
<artifactId>documents-parent</artifactId>
<version>7.3.x-SNAPSHOT</version>
<version>7.3.x-ai-contribution-SNAPSHOT</version>
</parent>
<artifactId>documents-services</artifactId>
<name>eXo Documents - Services</name>
Expand Down Expand Up @@ -41,6 +41,28 @@
<type>jar</type>
<scope>provided</scope>
</dependency>
<!-- MCP Server API (used by the document MCP tools exposed to the AI agent).
Pinned to the ai-contribution build that ships UploadToolUtils (create/upload
document tools); the platform-managed 7.3.x-SNAPSHOT does not have it yet. -->
<dependency>
<groupId>io.meeds.mcp-server</groupId>
<artifactId>mcp-server-tools</artifactId>
<version>7.3.x-ai-contribution-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- AttachmentService, used by attach_document_to_content -->
<dependency>
<groupId>org.exoplatform.ecms</groupId>
<artifactId>ecms-core-services</artifactId>
<version>${addon.exo.ecms.version}</version>
<scope>provided</scope>
</dependency>
<!-- NoteService, used by attach_document_to_content (note content type resolution) -->
<dependency>
<groupId>io.meeds.notes</groupId>
<artifactId>notes-service</artifactId>
<scope>provided</scope>
</dependency>
<!-- Test -->
<dependency>
<groupId>io.meeds.social</groupId>
Expand All @@ -62,6 +84,16 @@
<build>
<finalName>documents-services</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerArgs>
<!-- Required so MCP tool method parameter names are retained and bound by name -->
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>io.openapitools.swagger</groupId>
<artifactId>swagger-maven-plugin</artifactId>
Expand Down
Loading