From 2d60cb6df33b374979837af0622b70bcaa6d7b93 Mon Sep 17 00:00:00 2001 From: Humberto Morera <31667212+hmoreras@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:11:50 -0600 Subject: [PATCH 1/4] implemntation ( locales portlet ): #30395 Click on a locale in the new Locales portlet doesn't to anything (#30402) ### Proposed Changes * Add click event on row to show the edit popup. ### Screenshots https://github.com/user-attachments/assets/2ef16df8-7fa6-47e8-a04b-139d83567a58 --- .../dot-locales-list/dot-locales-list.component.html | 2 +- .../dot-locales-list.component.spec.ts | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/core-web/libs/portlets/dot-locales/portlet/src/lib/dot-locales-list/dot-locales-list.component.html b/core-web/libs/portlets/dot-locales/portlet/src/lib/dot-locales-list/dot-locales-list.component.html index 0414d374d3de..837bc51dbead 100644 --- a/core-web/libs/portlets/dot-locales/portlet/src/lib/dot-locales-list/dot-locales-list.component.html +++ b/core-web/libs/portlets/dot-locales/portlet/src/lib/dot-locales-list/dot-locales-list.component.html @@ -44,7 +44,7 @@ - + {{ locale.language }} ({{ locale.isoCode }}) @if (locale.defaultLanguage) { diff --git a/core-web/libs/portlets/dot-locales/portlet/src/lib/dot-locales-list/dot-locales-list.component.spec.ts b/core-web/libs/portlets/dot-locales/portlet/src/lib/dot-locales-list/dot-locales-list.component.spec.ts index 130a8b2e77b5..9363423c12c8 100644 --- a/core-web/libs/portlets/dot-locales/portlet/src/lib/dot-locales-list/dot-locales-list.component.spec.ts +++ b/core-web/libs/portlets/dot-locales/portlet/src/lib/dot-locales-list/dot-locales-list.component.spec.ts @@ -81,4 +81,16 @@ describe('DotLocalesListComponent', () => { expect(spectator.query('.p-tag-success')).toHaveText('Default'); }); + + it('should open AddEditDialog with locale id when row is clicked', () => { + spectator.detectChanges(); + + jest.spyOn(spectator.component.store, 'openAddEditDialog'); + + const row = spectator.query(byTestId('locale-row')); + + spectator.click(row); + + expect(spectator.component.store.openAddEditDialog).toHaveBeenCalled(); + }); }); From fd2618948abaab24f1b49ed87c5c24aa21ed5d29 Mon Sep 17 00:00:00 2001 From: Humberto Morera <31667212+hmoreras@users.noreply.github.com> Date: Sun, 20 Oct 2024 14:50:27 -0600 Subject: [PATCH 2/4] fix (velocity playground): #30393 Dev Tools - Velocity. Cursor has odd behavior when editing. (#30407) ### Proposed Changes * Remove custom editor properties and just follow the general rule in the ace.js, here: https://github.com/dotCMS/core/blob/main/dotCMS/src/main/webapp/html/js/ace-builds-1.2.3/src-noconflict/ace.js#L14755 * ### Video https://github.com/user-attachments/assets/bf5d7564-8ea9-479f-9f96-b35afcaf47ca --- .../src/main/webapp/WEB-INF/jsp/velocity_playground/render.jsp | 3 --- 1 file changed, 3 deletions(-) diff --git a/dotCMS/src/main/webapp/WEB-INF/jsp/velocity_playground/render.jsp b/dotCMS/src/main/webapp/WEB-INF/jsp/velocity_playground/render.jsp index ef51140b50b3..ea02b42c0e3c 100644 --- a/dotCMS/src/main/webapp/WEB-INF/jsp/velocity_playground/render.jsp +++ b/dotCMS/src/main/webapp/WEB-INF/jsp/velocity_playground/render.jsp @@ -63,9 +63,6 @@ editor = ace.edit('esEditor'); editor.setTheme("ace/theme/tomorrow_night"); editor.getSession().setMode("ace/mode/velocity"); - editor.setOptions({ - fontSize: "14px" - }); let myLocal = window.localStorage.getItem('velocityPlayground'); myLocal = (myLocal) ? JSON.parse(myLocal) : {}; From a8440a999a0ca2af78766e48f856b5133f7d3c83 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Mon, 21 Oct 2024 13:40:22 -0600 Subject: [PATCH 3/4] #30369 adding the custom user event (#30376) Adding the ability to fire user custom events on analytics --- .../collectors/BasicProfileCollector.java | 1 + .../ConcurrentCollectorPayloadBean.java | 8 ++ .../collectors/WebEventsCollectorService.java | 14 ++ .../WebEventsCollectorServiceFactory.java | 25 +++- .../UserCustomDefinedRequestMatcher.java | 16 +++ .../content/ContentAnalyticsResource.java | 62 ++++++++- .../Content_Analytics.postman_collection.json | 120 +++++++++++++++++- 7 files changed, 239 insertions(+), 7 deletions(-) create mode 100644 dotCMS/src/main/java/com/dotcms/analytics/track/matchers/UserCustomDefinedRequestMatcher.java diff --git a/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/BasicProfileCollector.java b/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/BasicProfileCollector.java index 05cc3a915bc6..7b8ba7b8bb2f 100644 --- a/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/BasicProfileCollector.java +++ b/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/BasicProfileCollector.java @@ -26,6 +26,7 @@ public boolean test(CollectorContextMap collectorContextMap) { public CollectorPayloadBean collect(final CollectorContextMap collectorContextMap, final CollectorPayloadBean collectorPayloadBean) { + // todo: add the user id final String requestId = (String)collectorContextMap.get("requestId"); final Long time = (Long)collectorContextMap.get("time"); final String clusterId = (String)collectorContextMap.get("cluster"); diff --git a/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/ConcurrentCollectorPayloadBean.java b/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/ConcurrentCollectorPayloadBean.java index bd25853f5ef4..3b3aa7ac240f 100644 --- a/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/ConcurrentCollectorPayloadBean.java +++ b/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/ConcurrentCollectorPayloadBean.java @@ -12,6 +12,14 @@ public class ConcurrentCollectorPayloadBean implements CollectorPayloadBean { private final ConcurrentHashMap map = new ConcurrentHashMap<>(); + public ConcurrentCollectorPayloadBean() { + // empty + } + + public ConcurrentCollectorPayloadBean(final Map customMap) { + map.putAll(customMap); + } + @Override public CollectorPayloadBean put(final String key, final Serializable value) { if (null != value) { diff --git a/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/WebEventsCollectorService.java b/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/WebEventsCollectorService.java index 388256b7bd0d..89ad5af64ba1 100644 --- a/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/WebEventsCollectorService.java +++ b/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/WebEventsCollectorService.java @@ -1,9 +1,12 @@ package com.dotcms.analytics.track.collectors; import com.dotcms.analytics.track.matchers.RequestMatcher; +import com.dotcms.analytics.track.matchers.UserCustomDefinedRequestMatcher; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.Serializable; +import java.util.Map; /** * This class is in charge of firing the collectors to populate the event payload. Also do the triggering the event to save the analytics data @@ -26,4 +29,15 @@ void fireCollectors (final HttpServletRequest request, final HttpServletResponse * @param collectorId */ void removeCollector(final String collectorId); + + /** + * Fire the collectors and emit the event + * @param request + * @param response + * @param requestMatcher + * @param userEventPayload + */ + void fireCollectorsAndEmitEvent(HttpServletRequest request, HttpServletResponse response, + final RequestMatcher requestMatcher, Map userEventPayload); + } diff --git a/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/WebEventsCollectorServiceFactory.java b/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/WebEventsCollectorServiceFactory.java index 3218b8610d56..ec9103110cfa 100644 --- a/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/WebEventsCollectorServiceFactory.java +++ b/dotCMS/src/main/java/com/dotcms/analytics/track/collectors/WebEventsCollectorServiceFactory.java @@ -9,9 +9,11 @@ import com.dotmarketing.filters.Constants; import com.dotmarketing.util.Logger; import com.dotmarketing.util.PageMode; +import com.dotmarketing.util.UtilMethods; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.Serializable; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -78,14 +80,24 @@ public void fireCollectors(final HttpServletRequest request, } } - private void fireCollectorsAndEmitEvent(final HttpServletRequest request, + @Override + /** + * Allows to fire the collections and emit the event from a base payload map already built by the user + * @param request + * @param response + * @param requestMatcher + * @param basePayloadMap + */ + public void fireCollectorsAndEmitEvent(final HttpServletRequest request, final HttpServletResponse response, - final RequestMatcher requestMatcher) { + final RequestMatcher requestMatcher, + final Map basePayloadMap) { final Character character = WebAPILocator.getCharacterWebAPI().getOrCreateCharacter(request, response); final Host site = WebAPILocator.getHostWebAPI().getCurrentHostNoThrow(request); - final CollectorPayloadBean base = new ConcurrentCollectorPayloadBean(); + final CollectorPayloadBean base = UtilMethods.isSet(basePayloadMap)? + new ConcurrentCollectorPayloadBean(basePayloadMap): new ConcurrentCollectorPayloadBean(); final CollectorContextMap syncCollectorContextMap = new RequestCharacterCollectorContextMap(request, character, requestMatcher); @@ -120,6 +132,13 @@ private void fireCollectorsAndEmitEvent(final HttpServletRequest request, } } + private void fireCollectorsAndEmitEvent(final HttpServletRequest request, + final HttpServletResponse response, + final RequestMatcher requestMatcher) { + + this.fireCollectorsAndEmitEvent(request, response, requestMatcher, Map.of()); + } + private static Map getCollectorContextMap(final HttpServletRequest request, final PageMode pageMode, final Host site) { final Map contextMap = new HashMap<>(Map.of("uri", request.getRequestURI(), diff --git a/dotCMS/src/main/java/com/dotcms/analytics/track/matchers/UserCustomDefinedRequestMatcher.java b/dotCMS/src/main/java/com/dotcms/analytics/track/matchers/UserCustomDefinedRequestMatcher.java new file mode 100644 index 000000000000..d539c02bd920 --- /dev/null +++ b/dotCMS/src/main/java/com/dotcms/analytics/track/matchers/UserCustomDefinedRequestMatcher.java @@ -0,0 +1,16 @@ +package com.dotcms.analytics.track.matchers; + +/** + * This is just flag class to identify the user custom defined event matcher + * @author jsanca + */ +public final class UserCustomDefinedRequestMatcher implements RequestMatcher { + + public static final String USER_CUSTOM_EVENT_MATCHER_ID = "user-custom-event"; + + + @Override + public String getId() { + return USER_CUSTOM_EVENT_MATCHER_ID; + } +} diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/analytics/content/ContentAnalyticsResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/analytics/content/ContentAnalyticsResource.java index 8a4aa4204fe9..ee02c0e6d8a0 100644 --- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/analytics/content/ContentAnalyticsResource.java +++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/analytics/content/ContentAnalyticsResource.java @@ -3,13 +3,17 @@ import com.dotcms.analytics.content.ContentAnalyticsAPI; import com.dotcms.analytics.content.ReportResponse; import com.dotcms.analytics.model.ResultSetItem; +import com.dotcms.analytics.track.collectors.WebEventsCollectorServiceFactory; +import com.dotcms.analytics.track.matchers.UserCustomDefinedRequestMatcher; import com.dotcms.cdi.CDIUtils; import com.dotcms.rest.InitDataObject; +import com.dotcms.rest.ResponseEntityStringView; import com.dotcms.rest.WebResource; import com.dotcms.rest.annotation.NoCache; import com.dotcms.util.DotPreconditions; import com.dotmarketing.business.APILocator; import com.dotmarketing.util.Logger; +import com.dotmarketing.util.UUIDUtil; import com.google.common.annotations.VisibleForTesting; import com.liferay.portal.model.User; import io.swagger.v3.oas.annotations.Operation; @@ -27,7 +31,10 @@ import javax.ws.rs.Produces; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; +import java.io.Serializable; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; /** @@ -44,6 +51,8 @@ description = "Endpoints that exposes information related to how dotCMS content is accessed and interacted with by users.") public class ContentAnalyticsResource { + private static final UserCustomDefinedRequestMatcher USER_CUSTOM_DEFINED_REQUEST_MATCHER = new UserCustomDefinedRequestMatcher(); + private final WebResource webResource; private final ContentAnalyticsAPI contentAnalyticsAPI; @@ -137,7 +146,7 @@ public ReportResponseEntityView query(@Context final HttpServletRequest request, * * @param request the HTTP request. * @param response the HTTP response. - * @param queryForm the query form. + * @param cubeJsQueryJson the query form. * @return the report response entity view. */ @Operation( @@ -189,4 +198,55 @@ public ReportResponseEntityView queryCubeJs(@Context final HttpServletRequest re return new ReportResponseEntityView(reportResponse.getResults().stream().map(ResultSetItem::getAll).collect(Collectors.toList())); } + /** + * Fire an user custom event. + * + * @param request the HTTP request. + * @param response the HTTP response. + * @param userEventPayload the query form. + * @return the report response entity view. + */ + @Operation( + operationId = "fireUserCustomEvent", + summary = "Fire an user custom event.", + description = "receives a custom event payload and fires the event to the collectors", + tags = {"Content Analytics"}, + responses = { + @ApiResponse(responseCode = "200", description = "If the event was created successfully", + content = @Content(mediaType = "application/json", + examples = { + @ExampleObject( + value = "TBD" + ) + } + ) + ), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "403", description = "Forbidden"), + @ApiResponse(responseCode = "500", description = "Internal Server Error") + } + ) + @POST + @Path("/event") + @JSONP + @NoCache + @Consumes(MediaType.APPLICATION_JSON) + @Produces({MediaType.APPLICATION_JSON, "application/javascript"}) + public ResponseEntityStringView fireUserCustomEvent(@Context final HttpServletRequest request, + @Context final HttpServletResponse response, + final Map userEventPayload) { + + new WebResource.InitBuilder(this.webResource) + .requestAndResponse(request, response) + .rejectWhenNoUser(true) + .init(); + + DotPreconditions.checkNotNull(userEventPayload, IllegalArgumentException.class, "The 'userEventPayload' JSON cannot be null"); + DotPreconditions.checkNotNull(userEventPayload.get("event_type"), IllegalArgumentException.class, "The 'event_type' field is required"); + Logger.debug(this, ()->"Creating an user custom event with the payload: " + userEventPayload); + request.setAttribute("requestId", UUIDUtil.uuid()); + WebEventsCollectorServiceFactory.getInstance().getWebEventsCollectorService().fireCollectorsAndEmitEvent(request, response, USER_CUSTOM_DEFINED_REQUEST_MATCHER, userEventPayload); + return new ResponseEntityStringView("User event created successfully"); + } + } diff --git a/dotcms-postman/src/main/resources/postman/Content_Analytics.postman_collection.json b/dotcms-postman/src/main/resources/postman/Content_Analytics.postman_collection.json index 8d505c8bf851..77197f1320e5 100644 --- a/dotcms-postman/src/main/resources/postman/Content_Analytics.postman_collection.json +++ b/dotcms-postman/src/main/resources/postman/Content_Analytics.postman_collection.json @@ -1,11 +1,10 @@ { "info": { - "_postman_id": "e4fd950b-b8f4-4213-99a1-eb468e732dd6", + "_postman_id": "e003f644-856a-4639-ac55-dfbcd946bd4e", "name": "Content Analytics", "description": "Performs simple data validation for the Content Analytics REST Endpoint. It's very important to notice that, for the time being, the CICD instance does not start up any of the additional third-party tools required to actually run the Content Analytics feature.\n\nThis means that these test do not deal with retrieveing or saving data at all. It verifies that important/required information is present.", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "5403727", - "_collection_link": "https://cloudy-robot-285072.postman.co/workspace/JCastro-Workspace~5bfa586e-54db-429b-b7d5-c4ff997e3a0d/collection/5403727-e4fd950b-b8f4-4213-99a1-eb468e732dd6?action=share&source=collection_link&creator=5403727" + "_exporter_id": "781456" }, "item": [ { @@ -93,6 +92,121 @@ "response": [] } ] + }, + { + "name": "Events", + "item": [ + { + "name": "No Query Form on Event", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"HTTP Status code must be 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{jwt}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/analytics/content/event", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "analytics", + "content", + "event" + ] + } + }, + "response": [] + }, + { + "name": "EventType is must", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"HTTP Status code must be 400\", function () {", + " pm.response.to.have.status(400);", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{jwt}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"test\":\"test\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{serverURL}}/api/v1/analytics/content/event", + "host": [ + "{{serverURL}}" + ], + "path": [ + "api", + "v1", + "analytics", + "content", + "event" + ] + } + }, + "response": [] + } + ] } ], "auth": { From 0bae3dccac0d44d4dafcc775df27e63a9391c7bc Mon Sep 17 00:00:00 2001 From: Nicolas Molina Monroy Date: Mon, 21 Oct 2024 18:32:44 -0400 Subject: [PATCH 4/4] chore(edit-content): Use new Angular Syntax in DotNavigationComponent (#30403) ### Parent Issue https://github.com/dotCMS/core/issues/30401 ### Proposed Changes This pull request includes some changes to the `dot-navigation` components in the `dotcms-ui` application. The primary focus is on refactoring the code to use Angular's new reactive primitives and simplifying the template syntax. ### Template Refactoring: * [`core-web/apps/dotcms-ui/src/app/view/components/dot-navigation/components/dot-nav-icon/dot-nav-icon.component.html`](diffhunk://#diff-93a54f28d1e0bee7ef4548b64f4803d76086c903bcc0da2dafec62761c72c9fdL1-R5): Replaced `*ngIf` and `ng-template` with Angular's new `@if` and `@else` syntax. * [`core-web/apps/dotcms-ui/src/app/view/components/dot-navigation/components/dot-sub-nav/dot-sub-nav.component.html`](diffhunk://#diff-1f36b07f7cdfbecc666246d6847285e225e96a1ee4802cd8c11c6bb149e484a0L2-R3): Updated `*ngFor` to use the new `@for` syntax with tracking. [[1]](diffhunk://#diff-1f36b07f7cdfbecc666246d6847285e225e96a1ee4802cd8c11c6bb149e484a0L2-R3) [[2]](diffhunk://#diff-1f36b07f7cdfbecc666246d6847285e225e96a1ee4802cd8c11c6bb149e484a0R13) * [`core-web/apps/dotcms-ui/src/app/view/components/dot-navigation/dot-navigation.component.html`](diffhunk://#diff-536660425506325914cc437d2a9733660d2ea944540b0c354e4599435ad85e06L1-R12): Replaced `*ngFor` and async pipe with `@for` and reactive signals. ### Component Refactoring: * [`core-web/apps/dotcms-ui/src/app/view/components/dot-navigation/dot-navigation.component.ts`](diffhunk://#diff-6e9db9ff07d7bd661a702691aa91170567a42d0a9786902f91cdd899b7da1b51L3-R4): Replaced constructor dependency injection with the `inject` function and used `toSignal` to convert observables to signals. [[1]](diffhunk://#diff-6e9db9ff07d7bd661a702691aa91170567a42d0a9786902f91cdd899b7da1b51L3-R4) [[2]](diffhunk://#diff-6e9db9ff07d7bd661a702691aa91170567a42d0a9786902f91cdd899b7da1b51L16-R32) * [`core-web/apps/dotcms-ui/src/app/view/components/dot-navigation/dot-navigation.component.ts`](diffhunk://#diff-6e9db9ff07d7bd661a702691aa91170567a42d0a9786902f91cdd899b7da1b51L43-R47): Updated method calls to use private fields for services. [[1]](diffhunk://#diff-6e9db9ff07d7bd661a702691aa91170567a42d0a9786902f91cdd899b7da1b51L43-R47) [[2]](diffhunk://#diff-6e9db9ff07d7bd661a702691aa91170567a42d0a9786902f91cdd899b7da1b51L56-R62) [[3]](diffhunk://#diff-6e9db9ff07d7bd661a702691aa91170567a42d0a9786902f91cdd899b7da1b51L70-R74) ### Checklist - [x] Tests - [x] Translations - [x] Security Implications Contemplated (add notes if applicable) ### Screenshots --- .../dot-nav-icon/dot-nav-icon.component.html | 13 +- .../dot-sub-nav/dot-sub-nav.component.html | 22 +- .../dot-navigation.component.html | 22 +- .../dot-navigation.component.spec.ts | 374 +++++++++--------- .../dot-navigation.component.ts | 67 +++- 5 files changed, 265 insertions(+), 233 deletions(-) diff --git a/core-web/apps/dotcms-ui/src/app/view/components/dot-navigation/components/dot-nav-icon/dot-nav-icon.component.html b/core-web/apps/dotcms-ui/src/app/view/components/dot-navigation/components/dot-nav-icon/dot-nav-icon.component.html index f15f79d2674a..0a941ea10ebb 100644 --- a/core-web/apps/dotcms-ui/src/app/view/components/dot-navigation/components/dot-nav-icon/dot-nav-icon.component.html +++ b/core-web/apps/dotcms-ui/src/app/view/components/dot-navigation/components/dot-nav-icon/dot-nav-icon.component.html @@ -1,8 +1,5 @@ - - - - +@if (isFaIcon(icon)) { + +} @else { +