Skip to content

Conversation

kunaljaykam
Copy link
Member

@kunaljaykam kunaljaykam commented Aug 8, 2025

TODO:
icon mapping
attachment url to calendar

Summary by CodeRabbit

  • New Features
    • Global search now includes calendar events across your sites.
    • Results display a calendar-specific icon and the “Calendar” tool name for easy recognition.
    • Search results surface key event details (title, description, location, type) and link directly to the event in its site.
    • Access controls are respected—only events you’re permitted to view appear in results.
    • Improved indexing ensures calendar content is discoverable alongside other tools.

@ern
Copy link
Contributor

ern commented Aug 11, 2025

@coderabbitai full review

Copy link

coderabbitai bot commented Aug 11, 2025

✅ Actions performed

Full review triggered.

Copy link

coderabbitai bot commented Aug 11, 2025

Walkthrough

Adds calendar search integration. Introduces a CalendarContentProducer for indexing calendar events, wires it via Spring, and adds required search dependencies. Updates search UI mappings and bundles to display calendar tool name and icon. No removals; new class, bean, properties, and minimal frontend adjustments.

Changes

Cohort / File(s) Change summary
Build dependencies
calendar/calendar-impl/impl/pom.xml
Added Maven dependencies: org.sakaiproject.search:search-api and search-util.
Backend search integration
calendar/calendar-impl/impl/src/java/org/sakaiproject/calendar/impl/CalendarContentProducer.java, calendar/calendar-impl/impl/src/webapp/WEB-INF/components.xml
New content producer class implementing EntityContentProducer; registers add/remove event types; wires SearchService, SearchIndexBuilder, EntityManager, CalendarService, SiteService, and ServerConfigurationService via Spring with init/destroy lifecycle.
Search UI updates
webcomponents/bundle/src/main/bundle/search.properties, webcomponents/tool/src/main/frontend/packages/sakai-search/src/SakaiSearch.js
Added toolname_calendar bundle key; extended iconMapping and toolNameMapping to support calendar icon and localized name in search results.

Sequence Diagram(s)

sequenceDiagram
  participant EventBus as Calendar Events
  participant SearchService
  participant CCP as CalendarContentProducer
  participant CalSvc as CalendarService
  participant SiteSvc as SiteService
  participant Indexer as SearchIndexBuilder

  EventBus->>SearchService: calendar.new / revise / delete
  SearchService->>CCP: notify with reference
  CCP->>CalSvc: get event by reference
  CalSvc-->>CCP: CalendarEvent
  CCP->>SiteSvc: verify site access (siteId)
  SiteSvc-->>CCP: access result
  alt add/update
    CCP-->>Indexer: provide content, title, url
    Indexer->>Indexer: index document
  else delete
    CCP-->>Indexer: remove document
  end
Loading
sequenceDiagram
  actor User
  participant UI as SakaiSearch (frontend)
  participant I18n as search.properties

  User->>UI: enter query
  UI->>UI: map tool "calendar" -> icon si-sakai-schedule
  UI->>I18n: load toolname_calendar
  I18n-->>UI: "Calendar"
  UI-->>User: render results with icon and tool name
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I nibbled through the fields of time,
Indexed hops in tidy rhyme.
A calendar carrot, crisp and bright,
Now searchable by day or night.
With icons twinkling, names that sing,
I thump my paws—search does its thing! 🥕✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (3)
webcomponents/tool/src/main/frontend/packages/sakai-search/src/SakaiSearch.js (1)

22-33: Icon mapping for calendar looks correct

Key matches producer getTool() = "calendar" and icon class looks right (si-sakai-schedule).

Consider a safe fallback to avoid "undefined" class if an unknown r.tool appears:

  • this.iconMapping[r.tool] || "si si-sakai-help"
calendar/calendar-impl/impl/src/java/org/sakaiproject/calendar/impl/CalendarContentProducer.java (2)

340-347: Null-guard event in matches(Event) to avoid NPEs

Minor safety improvement.

-    public boolean matches(Event event) {
-        return matches(event.getResource());
-    }
+    public boolean matches(Event event) {
+        return event != null && matches(event.getResource());
+    }

339-342: Null-guard reference in matches(String)

Minor safety improvement when called defensively.

-    public boolean matches(String reference) {
-        return reference.startsWith(CalendarService.REFERENCE_ROOT);
-    }
+    public boolean matches(String reference) {
+        return reference != null && reference.startsWith(CalendarService.REFERENCE_ROOT);
+    }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 206d57b and 78a94b9.

📒 Files selected for processing (5)
  • calendar/calendar-impl/impl/pom.xml (1 hunks)
  • calendar/calendar-impl/impl/src/java/org/sakaiproject/calendar/impl/CalendarContentProducer.java (1 hunks)
  • calendar/calendar-impl/impl/src/webapp/WEB-INF/components.xml (1 hunks)
  • webcomponents/bundle/src/main/bundle/search.properties (1 hunks)
  • webcomponents/tool/src/main/frontend/packages/sakai-search/src/SakaiSearch.js (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (3)
webcomponents/tool/src/main/frontend/packages/sakai-search/src/SakaiSearch.js (1)

37-49: Tool name mapping: ensure i18n key availability at runtime

Mapping to this._i18n.toolname_calendar is correct. Please confirm the key exists in all locale bundles to avoid undefined labels in non-English UIs.

Use the script in the search.properties comment to verify locale coverage.

calendar/calendar-impl/impl/pom.xml (1)

184-191: DependencyManagement Verified for search-api and search-util

The parent BOM (master/pom.xml lines 2063–2073) provides:

  • <dependencyManagement> entry for search-api with version ${sakai.version} and scope provided
  • <dependencyManagement> entry for search-util with version ${sakai.version} (default compile scope)

calendar/calendar-impl/impl/pom.xml inherits these, so no explicit <version> or <scope> is required.
Please ensure your code imports org.sakaiproject.search.util.SearchUtils from the search-util artifact.

calendar/calendar-impl/impl/src/java/org/sakaiproject/calendar/impl/CalendarContentProducer.java (1)

239-243: Verify getEvents(null, null) semantics and performance

Confirm this returns all events and is safe. Some Calendar API variants expect a TimeRange and/or type filters; nulls may not yield the intended result or may be costly.

Would you prefer we scope the range (e.g., past year + future year) and inject TimeService to build a TimeRange? I can draft that if desired.

import org.sakaiproject.search.api.EntityContentProducer;
import org.sakaiproject.search.api.SearchIndexBuilder;
import org.sakaiproject.search.api.SearchService;
import org.sakaiproject.search.api.SearchUtils;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Wrong package for SearchUtils import (compile-time issue)

SearchUtils resides in search-util, typically under org.sakaiproject.search.util.SearchUtils, not org.sakaiproject.search.api.

Apply:

-import org.sakaiproject.search.api.SearchUtils;
+import org.sakaiproject.search.util.SearchUtils;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import org.sakaiproject.search.api.SearchUtils;
import org.sakaiproject.search.util.SearchUtils;
🤖 Prompt for AI Agents
In
calendar/calendar-impl/impl/src/java/org/sakaiproject/calendar/impl/CalendarContentProducer.java
around line 43, the file currently imports SearchUtils from the wrong package;
replace the incorrect import org.sakaiproject.search.api.SearchUtils with the
correct package org.sakaiproject.search.util.SearchUtils (remove the old import
and add the new one) so the class compiles against the search-util module.

Comment on lines +85 to +97
protected void init() throws Exception {
log.info("init()");

if ("true".equals(serverConfigurationService.getString("search.enable", "false"))) {
for (Iterator<String> i = addEvents.iterator(); i.hasNext();) {
getSearchService().registerFunction((String) i.next());
}
for (Iterator<String> i = removeEvents.iterator(); i.hasNext();) {
getSearchService().registerFunction((String) i.next());
}
getSearchIndexBuilder().registerEntityContentProducer(this);
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use boolean config and guard service nulls in init()

Avoid string comparisons and NPEs if services aren’t injected (defensive).

-    if ("true".equals(serverConfigurationService.getString("search.enable", "false"))) {
+    if (serverConfigurationService != null
+        && serverConfigurationService.getBoolean("search.enable", false)
+        && searchService != null
+        && searchIndexBuilder != null) {
         for (Iterator<String> i = addEvents.iterator(); i.hasNext();) {
             getSearchService().registerFunction((String) i.next());
         }
         for (Iterator<String> i = removeEvents.iterator(); i.hasNext();) {
             getSearchService().registerFunction((String) i.next());
         }
         getSearchIndexBuilder().registerEntityContentProducer(this);
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
protected void init() throws Exception {
log.info("init()");
if ("true".equals(serverConfigurationService.getString("search.enable", "false"))) {
for (Iterator<String> i = addEvents.iterator(); i.hasNext();) {
getSearchService().registerFunction((String) i.next());
}
for (Iterator<String> i = removeEvents.iterator(); i.hasNext();) {
getSearchService().registerFunction((String) i.next());
}
getSearchIndexBuilder().registerEntityContentProducer(this);
}
}
protected void init() throws Exception {
log.info("init()");
if (serverConfigurationService != null
&& serverConfigurationService.getBoolean("search.enable", false)
&& searchService != null
&& searchIndexBuilder != null) {
for (Iterator<String> i = addEvents.iterator(); i.hasNext();) {
getSearchService().registerFunction((String) i.next());
}
for (Iterator<String> i = removeEvents.iterator(); i.hasNext();) {
getSearchService().registerFunction((String) i.next());
}
getSearchIndexBuilder().registerEntityContentProducer(this);
}
}

Comment on lines +207 to +209
log.debug("Calendar Event Content for reference: {} is: {}", ref.getReference(), sb.toString());
return sb.toString();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid logging full event content (PII leakage risk)

Debug log echoes event titles, descriptions, and locations into logs. Remove or redact to prevent PII leakage.

-        log.debug("Calendar Event Content for reference: {} is: {}", ref.getReference(), sb.toString());
+        log.debug("Indexed calendar event content for reference: {}", ref.getReference());
🤖 Prompt for AI Agents
In
calendar/calendar-impl/impl/src/java/org/sakaiproject/calendar/impl/CalendarContentProducer.java
around lines 207 to 209, the debug log currently emits the full event content
(title, description, location) which risks PII leakage; change the log to avoid
printing the full sb.toString() by either removing the content from the message
or redacting sensitive fields — e.g., log only the event reference/id and a
non-sensitive indicator such as content length or a hash, or replace
title/description/location with masked values; ensure any remaining debug output
contains no user-identifiable text.

Comment on lines +309 to +320
return "/portal/directtool/" + toolConfig.getId() +
"?eventReference=" + reference +
"&panel=Main&sakai_action=doDescription&sakai.state.reset=true";
}
} catch (Exception e) {
log.debug("Error getting tool configuration for site: {}", siteId, e);
}

// Fallback to regular site URL
return "/portal/site/" + siteId + "/tool/sakai.schedule?eventReference=" + reference +
"&panel=Main&sakai_action=doDescription&sakai.state.reset=true";
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

URL-encode query parameters to avoid broken links and injection issues

reference can contain reserved characters. Encode it for both directtool and site URLs.

-                return "/portal/directtool/" + toolConfig.getId() + 
-                       "?eventReference=" + reference + 
+                String encodedRef = java.net.URLEncoder.encode(reference, java.nio.charset.StandardCharsets.UTF_8.name());
+                return "/portal/directtool/" + toolConfig.getId() +
+                       "?eventReference=" + encodedRef +
                        "&panel=Main&sakai_action=doDescription&sakai.state.reset=true";
...
-        return "/portal/site/" + siteId + "/tool/sakai.schedule?eventReference=" + reference + 
+        String encodedRef = java.net.URLEncoder.encode(reference, java.nio.charset.StandardCharsets.UTF_8.name());
+        return "/portal/site/" + siteId + "/tool/sakai.schedule?eventReference=" + encodedRef +
                "&panel=Main&sakai_action=doDescription&sakai.state.reset=true";

Add imports if you prefer to pre-import:

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
🤖 Prompt for AI Agents
In
calendar/calendar-impl/impl/src/java/org/sakaiproject/calendar/impl/CalendarContentProducer.java
around lines 309 to 320, the reference query parameter is inserted raw into two
returned URLs which can break links or allow injection; URL-encode reference
before concatenation (e.g. use URLEncoder.encode(reference,
StandardCharsets.UTF_8.name())) for both the "/portal/directtool/..." and
"/portal/site/..." return paths, add imports for java.net.URLEncoder and
java.nio.charset.StandardCharsets, and handle the checked exception or let it
propagate appropriately (wrap encoding in a try/catch and log/throw if encoding
fails).

Comment on lines +92 to +126
<bean id="calendarSearchAdapter"
class="org.sakaiproject.calendar.impl.CalendarContentProducer"
init-method="init" destroy-method="destroy">
<property name="addEvents">
<list>
<value>calendar.new</value>
<value>calendar.revise</value>
<value>calendar.revise.event.title</value>
<value>calendar.revise.event.time</value>
<value>calendar.revise.event.type</value>
<value>calendar.revise.event.access</value>
<value>calendar.revise.event.frequency</value>
</list>
</property>
<property name="removeEvents">
<list>
<value>calendar.delete</value>
<value>calendar.delete.event</value>
</list>
</property>
<property name="entityManager">
<ref bean="org.sakaiproject.entity.api.EntityManager"/>
</property>
<property name="calendarService">
<ref bean="org.sakaiproject.calendar.api.CalendarService"/>
</property>
<property name="searchService">
<ref bean="org.sakaiproject.search.api.SearchService" />
</property>
<property name="searchIndexBuilder">
<ref bean="org.sakaiproject.search.api.SearchIndexBuilder" />
</property>
<property name="siteService" ref="org.sakaiproject.site.api.SiteService"/>
<property name="serverConfigurationService" ref="org.sakaiproject.component.api.ServerConfigurationService"/>
</bean>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Event trigger coverage: consider adding description/location revise events

To keep the index in sync, please verify whether description and location changes emit distinct events (e.g., calendar.revise.event.description, calendar.revise.event.location). If they exist, add them to addEvents.

Use this to search for available calendar event names:

Optionally, add an explicit depends-on to ensure search services are initialized before registering:

<bean id="calendarSearchAdapter"
      class="org.sakaiproject.calendar.impl.CalendarContentProducer"
      init-method="init" destroy-method="destroy"
      depends-on="org.sakaiproject.search.api.SearchService, org.sakaiproject.search.api.SearchIndexBuilder">
  ...
</bean>

🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Searching for calendar.* event keys..."
rg -n 'calendar\.(new|revise|delete)[\w\.]*' calendar | sort -u

Length of output: 18194


Review calendar event coverage and update adapter
No dedicated events exist for description or location changes (the API emits only the generic calendar.revise for those). However, two additional event keys—calendar.revise.event.exclusions and calendar.revise.event.excluded—are defined in CalendarService but not wired in the adapter. To ensure index consistency, add them to the addEvents list.

• File: calendar/calendar-impl/impl/src/webapp/WEB-INF/components.xml (lines ~92–126)
Update the <list> under <property name="addEvents">:

     <value>calendar.revise.event.frequency</value>
+    <value>calendar.revise.event.exclusions</value>
+    <value>calendar.revise.event.excluded</value>

Optionally, you may still include a depends-on on the search beans for init ordering:

<bean id="calendarSearchAdapter"
      class="org.sakaiproject.calendar.impl.CalendarContentProducer"
      init-method="init" destroy-method="destroy"
      depends-on="org.sakaiproject.search.api.SearchService,org.sakaiproject.search.api.SearchIndexBuilder">
  …
</bean>
🤖 Prompt for AI Agents
In calendar/calendar-impl/impl/src/webapp/WEB-INF/components.xml around lines 92
to 126, the CalendarContentProducer bean's addEvents list is missing two event
keys (calendar.revise.event.exclusions and calendar.revise.event.excluded)
defined in CalendarService; add these two <value> entries into the <property
name="addEvents"> list so the adapter listens for those changes and keeps the
search index consistent, and optionally add a
depends-on="org.sakaiproject.search.api.SearchService,org.sakaiproject.search.api.SearchIndexBuilder"
attribute to the <bean id="calendarSearchAdapter" ...> to ensure init ordering
with search beans.

@@ -19,3 +19,4 @@ toolname_forum=Forum
toolname_lesson=Lesson
toolname_resources=Resources
toolname_wiki=Wiki
toolname_calendar=Calendar
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Add locale coverage for toolname_calendar

This addition looks good. Please ensure the same key exists across the other search.properties locale bundles; otherwise non-English UIs will show a missing label.

Run to verify presence across all search*.properties bundles:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Scanning for toolname_calendar across search.*.properties files..."
fd -a -t f 'search.*\.properties' | while read -r f; do
  if rg -n '^\s*toolname_calendar=' "$f" > /dev/null; then
    echo "OK: $f"
  else
    echo "MISSING: $f"
  fi
done

Length of output: 3560


Add toolname_calendar entry to all localized search bundles

Scan results show that only the default search.properties contains the new key. To avoid missing UI labels in non-English locales, please add toolname_calendar (with the appropriate translated value) to each of the following files:

• webcomponents/bundle/src/main/bundle/search_ar.properties
• webcomponents/bundle/src/main/bundle/search_bg.properties
• webcomponents/bundle/src/main/bundle/search_ca.properties
• webcomponents/bundle/src/main/bundle/search_de_DE.properties
• webcomponents/bundle/src/main/bundle/search_es.properties
• webcomponents/bundle/src/main/bundle/search_eu.properties
• webcomponents/bundle/src/main/bundle/search_fr_FR.properties
• webcomponents/bundle/src/main/bundle/search_ro_RO.properties
• webcomponents/bundle/src/main/bundle/search_sr.properties
• webcomponents/bundle/src/main/bundle/search_tr_TR.properties

Once added, ensure each translation is reviewed for accuracy.

🤖 Prompt for AI Agents
In webcomponents/bundle/src/main/bundle/search.properties around line 22 and in
all localized bundles listed (search_ar.properties, search_bg.properties,
search_ca.properties, search_de_DE.properties, search_es.properties,
search_eu.properties, search_fr_FR.properties, search_ro_RO.properties,
search_sr.properties, search_tr_TR.properties), the new key toolname_calendar is
missing; add a toolname_calendar entry to each localized file with the
appropriate translated value for "Calendar" (matching locale conventions), place
it alongside other toolname_* keys, and have each translation reviewed for
accuracy by a native speaker or translator before committing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants