Skip to content

Commit

Permalink
#26556 include in 23.10.24
Browse files Browse the repository at this point in the history
  • Loading branch information
erickgonzalez committed Feb 26, 2024
1 parent a25a92e commit cb116f6
Show file tree
Hide file tree
Showing 14 changed files with 186 additions and 46 deletions.
8 changes: 8 additions & 0 deletions core-web/apps/dotcms-ui/.storybook/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ module.exports = function expressMiddleware(router) {
})
);

router.use(
'/api/v1/ai/image/test',
createProxyMiddleware({
target: 'http://localhost:8080',
changeOrigin: true
})
);

// Publish the image_temp generated
router.use(
'/api/v1/workflow/actions/default/fire/PUBLISH',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { ButtonModule } from 'primeng/button';
import { CheckboxModule } from 'primeng/checkbox';
import { TabViewModule } from 'primeng/tabview';

import { DotApiLinkModule } from '@components/dot-api-link/dot-api-link.module';
import { DotMessageService } from '@dotcms/data-access';
import { DotApiLinkComponent } from '@dotcms/ui';
import { MockDotMessageService } from '@dotcms/utils-testing';

import { DotPortletBaseComponent } from './dot-portlet-base.component';
Expand All @@ -35,7 +35,7 @@ export default {
ButtonModule,
CheckboxModule,
DotPortletBaseModule,
DotApiLinkModule,
DotApiLinkComponent,
TabViewModule
]
}),
Expand Down
24 changes: 23 additions & 1 deletion core-web/libs/block-editor/src/lib/block-editor.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,23 @@ import {
} from './extensions';
import { AssetFormModule } from './extensions/asset-form/asset-form.module';
import { ContentletBlockComponent } from './nodes';
import { DotAiService, DotUploadFileService, EditorDirective } from './shared';
import {
AI_PLUGIN_INSTALLED_TOKEN,
DotAiService,
DotUploadFileService,
EditorDirective
} from './shared';
import { PrimengModule } from './shared/primeng.module';
import { DotBlockEditorInitService } from './shared/services/dot-block-editor-init/dot-block-editor-init.service';
import { SharedModule } from './shared/shared.module';

const initTranslations = (dotMessageService: DotMessageService) => {
return () => dotMessageService.init();
};

const initializeBlockEditor = (appInitService: DotBlockEditorInitService) => {
return () => appInitService.initializeBlockEditor();
};
@NgModule({
imports: [
CommonModule,
Expand Down Expand Up @@ -69,11 +78,24 @@ const initTranslations = (dotMessageService: DotMessageService) => {
StringUtils,
DotAiService,
DotMessageService,
DotBlockEditorInitService,
{
provide: APP_INITIALIZER,
useFactory: initTranslations,
deps: [DotMessageService],
multi: true
},

{
provide: APP_INITIALIZER,
useFactory: initializeBlockEditor,
deps: [DotBlockEditorInitService],
multi: true
},
{
provide: AI_PLUGIN_INSTALLED_TOKEN,
useFactory: (service: DotBlockEditorInitService) => service.isPluginInstalled,
deps: [DotBlockEditorInitService]
}
],
exports: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
} from '../../shared';
import { DotMessageServiceMock } from '../../shared/mocks/dot-message.service.mock';
import { DotAiServiceMock } from '../../shared/services/dot-ai/dot-ai-service.mock';
import { DotBlockEditorInitService } from '../../shared/services/dot-block-editor-init/dot-block-editor-init.service';

export default {
title: 'Library/Block Editor'
Expand Down Expand Up @@ -194,7 +195,8 @@ export const primary = () => ({
process.env.USE_MIDDLEWARE === 'true'
? DotMessageService
: DotMessageServiceMock
}
},
DotBlockEditorInitService
],
// We need these here because they are dynamically rendered
entryComponents: [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Subject, from } from 'rxjs';
import { assert, object, string, array, optional } from 'superstruct';
import { from, Subject } from 'rxjs';
import { array, assert, object, optional, string } from 'superstruct';

import {
Component,
EventEmitter,
inject,
Injector,
Input,
OnDestroy,
Expand All @@ -28,40 +29,41 @@ import { Youtube } from '@tiptap/extension-youtube';
import StarterKit, { StarterKitOptions } from '@tiptap/starter-kit';

import {
RemoteCustomExtensions,
EDITOR_MARKETING_KEYS,
IMPORT_RESULTS
IMPORT_RESULTS,
RemoteCustomExtensions
} from '@dotcms/dotcms-models';

import {
ActionsMenu,
AIContentActionsExtension,
AIContentPromptExtension,
AIImagePromptExtension,
AssetUploader,
BubbleAssetFormExtension,
BubbleFormExtension,
BubbleLinkFormExtension,
DotBubbleMenuExtension,
DEFAULT_LANG_ID,
DotBubbleMenuExtension,
DotComands,
DotConfigExtension,
DotFloatingButton,
DotTableCellExtension,
DotTableHeaderExtension,
DotTableExtension,
DotTableHeaderExtension,
DragHandler,
DotFloatingButton,
BubbleAssetFormExtension,
FreezeScroll,
FREEZE_SCROLL_KEY,
AssetUploader,
DotComands,
AIContentPromptExtension,
AIImagePromptExtension,
AIContentActionsExtension
FreezeScroll
} from '../../extensions';
import { DotPlaceholder } from '../../extensions/dot-placeholder/dot-placeholder-plugin';
import { ContentletBlock, ImageNode, VideoNode, AIContentNode, LoaderNode } from '../../nodes';
import { AIContentNode, ContentletBlock, ImageNode, LoaderNode, VideoNode } from '../../nodes';
import {
AI_PLUGIN_INSTALLED_TOKEN,
DotMarketingConfigService,
formatHTML,
removeInvalidNodes,
SetDocAttrStep,
DotMarketingConfigService,
RestoreDefaultDOMAttrs
RestoreDefaultDOMAttrs,
SetDocAttrStep
} from '../../shared';

@Component({
Expand Down Expand Up @@ -117,6 +119,8 @@ export class DotBlockEditorComponent implements OnInit, OnDestroy {
['loader', LoaderNode]
]);

private readonly isAIPluginInstalled: boolean = inject(AI_PLUGIN_INSTALLED_TOKEN);

private destroy$: Subject<boolean> = new Subject<boolean>();

get characterCount(): CharacterCountStorage {
Expand Down Expand Up @@ -362,7 +366,7 @@ export class DotBlockEditorComponent implements OnInit, OnDestroy {
* @memberof DotBlockEditorComponent
*/
private getEditorExtensions() {
return [
const extensions = [
DotConfigExtension({
lang: this.lang,
allowedContentTypes: this.allowedContentTypes,
Expand All @@ -380,14 +384,13 @@ export class DotBlockEditorComponent implements OnInit, OnDestroy {
}),
Subscript,
Superscript,
ActionsMenu(this.viewContainerRef, this.getParsedCustomBlocks()),
ActionsMenu(this.viewContainerRef, this.getParsedCustomBlocks(), {
shouldShowAIExtensions: this.isAIPluginInstalled
}),
DragHandler(this.viewContainerRef),
BubbleLinkFormExtension(this.viewContainerRef, this.lang),
DotBubbleMenuExtension(this.viewContainerRef),
BubbleFormExtension(this.viewContainerRef),
AIContentPromptExtension(this.viewContainerRef),
AIImagePromptExtension(this.viewContainerRef),
AIContentActionsExtension(this.viewContainerRef),
DotFloatingButton(this.injector, this.viewContainerRef),
DotTableCellExtension(this.viewContainerRef),
BubbleAssetFormExtension(this.viewContainerRef),
Expand All @@ -397,6 +400,16 @@ export class DotBlockEditorComponent implements OnInit, OnDestroy {
CharacterCount,
AssetUploader(this.injector, this.viewContainerRef)
];

if (this.isAIPluginInstalled) {
extensions.push(
AIContentPromptExtension(this.viewContainerRef),
AIImagePromptExtension(this.viewContainerRef),
AIContentActionsExtension(this.viewContainerRef)
);
}

return extensions;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,26 @@ import Suggestion, { SuggestionOptions, SuggestionProps } from '@tiptap/suggesti
import { RemoteCustomExtensions } from '@dotcms/dotcms-models';

import {
SuggestionPopperModifiers,
SuggestionsCommandProps,
SuggestionsComponent,
FloatingActionsProps,
FLOATING_ACTIONS_MENU_KEYBOARD,
clearFilter,
CONTENT_SUGGESTION_ID,
ItemsType,
DotMenuItem,
findParentNode,
FLOATING_ACTIONS_MENU_KEYBOARD,
FloatingActionsKeydownProps,
FloatingActionsPlugin,
findParentNode,
DotMenuItem,
FloatingActionsProps,
ItemsType,
suggestionOptions,
clearFilter
SuggestionPopperModifiers,
SuggestionsCommandProps,
SuggestionsComponent
} from '../../shared';
import { AI_CONTENT_PROMPT_EXTENSION_NAME } from '../ai-content-prompt/ai-content-prompt.extension';
import { AI_IMAGE_PROMPT_EXTENSION_NAME } from '../ai-image-prompt/ai-image-prompt.extension';
import { NodeTypes } from '../bubble-menu/models';

const AI_BLOCK_EXTENSIONS_IDS = [AI_CONTENT_PROMPT_EXTENSION_NAME, AI_IMAGE_PROMPT_EXTENSION_NAME];

declare module '@tiptap/core' {
interface Commands<ReturnType> {
actionsMenu: {
Expand Down Expand Up @@ -210,7 +214,8 @@ function getCustomActions(customBlocks): Array<DotMenuItem> {

export const ActionsMenu = (
viewContainerRef: ViewContainerRef,
customBlocks: RemoteCustomExtensions
customBlocks: RemoteCustomExtensions,
disabledExtensions: { shouldShowAIExtensions: boolean | unknown }
) => {
let myTippy;
let suggestionsComponent: ComponentRef<SuggestionsComponent>;
Expand Down Expand Up @@ -289,10 +294,27 @@ export const ActionsMenu = (
}
}

/**
* Retrieves the items for the given parameters.
*
* @param {object} options - The options for retrieving the items.
* @param {string[]} options.allowedBlocks - The array of allowed block IDs.
* @param {object} options.editor - The editor object.
* @param {object} options.range - The range object.
* @return {DotMenuItem[]} - The array of DotMenuItem objects.
*/
function getItems({ allowedBlocks = [], editor, range }): DotMenuItem[] {
let filteredSuggestionOptions: DotMenuItem[] = [...suggestionOptions];

if (!disabledExtensions?.shouldShowAIExtensions) {
filteredSuggestionOptions = suggestionOptions.filter(
(item) => !AI_BLOCK_EXTENSIONS_IDS.includes(item.id)
);
}

const items = allowedBlocks.length
? suggestionOptions.filter((item) => allowedBlocks.includes(item.id))
: suggestionOptions;
? filteredSuggestionOptions.filter((item) => allowedBlocks.includes(item.id))
: filteredSuggestionOptions;

const customItems = [...items, ...getCustomActions(customBlocks)];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ export const DOT_AI_TEXT_CONTENT_KEY = 'dotAITextContent';

export const AI_CONTENT_PROMPT_PLUGIN_KEY = new PluginKey(DOT_AI_TEXT_CONTENT_KEY);

export const AI_CONTENT_PROMPT_EXTENSION_NAME = 'aiContentPrompt';

export const AIContentPromptExtension = (viewContainerRef: ViewContainerRef) => {
return Extension.create<AIContentPromptOptions>({
name: 'aiContentPrompt',
name: AI_CONTENT_PROMPT_EXTENSION_NAME,

addOptions() {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ export const DOT_AI_IMAGE_CONTENT_KEY = 'dotAIImageContent';

export const AI_IMAGE_PROMPT_PLUGIN_KEY = new PluginKey('aiImagePrompt-form');

export const AI_IMAGE_PROMPT_EXTENSION_NAME = 'aiImagePrompt';

export const AIImagePromptExtension = (viewContainerRef: ViewContainerRef) => {
return Extension.create<AIImagePromptOptions>({
name: 'aiImagePrompt',
name: AI_IMAGE_PROMPT_EXTENSION_NAME,

addOptions() {
return {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Observable, throwError } from 'rxjs';

import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpResponse } from '@angular/common/http';
import { inject, Injectable } from '@angular/core';

import { catchError, map, pluck, switchMap } from 'rxjs/operators';

Expand All @@ -18,7 +18,7 @@ const headers = new HttpHeaders({
type ImageSize = '1024x1024' | '1024x1792' | '1792x1024';
@Injectable()
export class DotAiService {
constructor(private http: HttpClient) {}
private http: HttpClient = inject(HttpClient);
/**
* Generates content by sending a HTTP POST request to the AI plugin endpoint.
*
Expand Down Expand Up @@ -70,6 +70,15 @@ export class DotAiService {
);
}

/**
* Checks the installation status of a plugin.
*
* @return {Observable<HttpResponse<unknown>>} Observable that emits an HttpResponse object containing the plugin installation status.
*/
checkPluginInstallation(): Observable<HttpResponse<unknown>> {
return this.http.get(`${API_ENDPOINT}/image/test`, { observe: 'response' });
}

private createAndPublishContentlet(image: DotAIImageResponse): Observable<DotCMSContentlet[]> {
const { response, tempFileName } = image;
const contentlets: Partial<DotCMSContentlet>[] = [
Expand Down
Loading

0 comments on commit cb116f6

Please sign in to comment.