Initial commit
This commit is contained in:
603
references/api-calendar-bookmarks.md
Normal file
603
references/api-calendar-bookmarks.md
Normal file
@@ -0,0 +1,603 @@
|
||||
# Calendar, Bookmarks & Advanced Integration API Reference
|
||||
|
||||
Complete reference for Calendar integration, Bookmarks, Linked Analysis, and Timer APIs in SAP Analytics Cloud.
|
||||
|
||||
**Source**: [Analytics Designer API Reference 2025.14](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Calendar Integration API](#calendar-integration-api)
|
||||
2. [Bookmarks API](#bookmarks-api)
|
||||
3. [Linked Analysis API](#linked-analysis-api)
|
||||
4. [Timer API](#timer-api)
|
||||
5. [Notification Badges](#notification-badges)
|
||||
|
||||
---
|
||||
|
||||
## Calendar Integration API
|
||||
|
||||
SAC Calendar allows integration with planning workflows, tasks, and processes.
|
||||
|
||||
### Getting Calendar Events
|
||||
|
||||
#### getCalendarEventById(eventId)
|
||||
|
||||
Retrieves an existing calendar event by ID.
|
||||
|
||||
```javascript
|
||||
var event = Calendar.getCalendarEventById("EVENT_ID_STRING");
|
||||
```
|
||||
|
||||
**Note**: The ID is a unique string (like a story/model ID), not the display name.
|
||||
|
||||
#### getCurrentEvent()
|
||||
|
||||
Returns the event from which the application was started.
|
||||
|
||||
```javascript
|
||||
var currentEvent = Calendar.getCurrentEvent();
|
||||
if (currentEvent) {
|
||||
console.log("Started from event:", currentEvent.getName());
|
||||
}
|
||||
```
|
||||
|
||||
### Calendar Event Methods
|
||||
|
||||
Once you have an event reference, these methods are available:
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `getId()` | Returns event unique ID |
|
||||
| `getName()` | Returns event display name |
|
||||
| `getDescription()` | Returns event description |
|
||||
| `getStartDate()` | Returns start date |
|
||||
| `getDueDate()` | Returns due date |
|
||||
| `getStatus()` | Returns current status |
|
||||
| `getType()` | Returns event type |
|
||||
| `getProgress()` | Returns completion percentage |
|
||||
| `hasUserRole(role)` | Checks if user has specific role |
|
||||
| `activate()` | Activates the event |
|
||||
|
||||
### Calendar Composite Task
|
||||
|
||||
For composite tasks (multi-step workflows):
|
||||
|
||||
#### CalendarCompositeTask Methods
|
||||
|
||||
```javascript
|
||||
// Approval workflow
|
||||
task.approve(); // Approve task (requires Reviewer role)
|
||||
task.reject(); // Reject task
|
||||
task.submit(); // Submit for review
|
||||
task.decline(); // Decline task
|
||||
|
||||
// Check permissions
|
||||
task.canUserApprove();
|
||||
task.canUserReject();
|
||||
task.canUserSubmit();
|
||||
task.canUserDecline();
|
||||
|
||||
// Reviewer management
|
||||
task.addReviewer(userId);
|
||||
task.removeReviewer(userId);
|
||||
```
|
||||
|
||||
### Creating Calendar Events
|
||||
|
||||
#### Create Process
|
||||
|
||||
```javascript
|
||||
var processProperties = {
|
||||
name: "Q4 Planning",
|
||||
startDate: new Date(2024, 9, 1), // October 1, 2024
|
||||
endDate: new Date(2024, 11, 31), // December 31, 2024
|
||||
description: "Q4 Budget Planning Process",
|
||||
owners: ["user1@company.com"],
|
||||
assignees: ["user2@company.com", "user3@company.com"]
|
||||
};
|
||||
|
||||
var newProcess = Calendar.createProcess(processProperties);
|
||||
```
|
||||
|
||||
#### Create Task
|
||||
|
||||
```javascript
|
||||
var taskProperties = {
|
||||
name: "Review Budget",
|
||||
startDate: new Date(2024, 10, 1),
|
||||
dueDate: new Date(2024, 10, 15),
|
||||
description: "Review Q4 budget allocations",
|
||||
assignees: ["reviewer@company.com"],
|
||||
parentId: parentProcessId,
|
||||
reminders: [
|
||||
{ daysBeforeDue: 3 },
|
||||
{ daysBeforeDue: 1 }
|
||||
]
|
||||
};
|
||||
|
||||
var newTask = Calendar.createTask(taskProperties);
|
||||
```
|
||||
|
||||
### CalendarCompositeTaskCreateProperties
|
||||
|
||||
Full list of properties when creating tasks:
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| name | string | Task name (required) |
|
||||
| startDate | Date | Start date (required) |
|
||||
| endDate | Date | End date (required) |
|
||||
| dueDate | Date | Due date |
|
||||
| description | string | Task description |
|
||||
| owners | string[] | Owner user IDs |
|
||||
| assignees | string[] | Assignee user IDs |
|
||||
| reviewers | string[] | Reviewer user IDs |
|
||||
| parentId | string | Parent process ID |
|
||||
| dependencies | string[] | Dependent task IDs |
|
||||
| contextFilters | object | Context filter settings |
|
||||
| reminders | object[] | Reminder configurations |
|
||||
| workFiles | string[] | Attached work file IDs |
|
||||
|
||||
### Calendar Event Status
|
||||
|
||||
```javascript
|
||||
CalendarTaskStatus.Open // Not started
|
||||
CalendarTaskStatus.InProgress // In progress
|
||||
CalendarTaskStatus.Successful // Completed successfully
|
||||
CalendarTaskStatus.Failed // Failed
|
||||
CalendarTaskStatus.Canceled // Canceled
|
||||
```
|
||||
|
||||
### Reminder Configuration
|
||||
|
||||
```javascript
|
||||
// Add reminder to event
|
||||
event.addReminder({
|
||||
daysBeforeDue: 5,
|
||||
notificationType: "Email"
|
||||
});
|
||||
```
|
||||
|
||||
### Bulk Operations Example
|
||||
|
||||
```javascript
|
||||
// Bulk modify context filters on calendar events
|
||||
function updateEventFilters(eventIds, newFilter) {
|
||||
eventIds.forEach(function(eventId) {
|
||||
var event = Calendar.getCalendarEventById(eventId);
|
||||
if (event) {
|
||||
event.setContextFilter("Year", newFilter);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Bookmarks API
|
||||
|
||||
Bookmarks save application state for later restoration.
|
||||
|
||||
### What Bookmarks Save
|
||||
|
||||
- Dimension and measure selections in Tables/Charts
|
||||
- Filter states
|
||||
- Hierarchical levels
|
||||
- Dropdown/Radio button/Checkbox selections
|
||||
- Script Variables (primitive types: string, boolean, integer, number)
|
||||
- Widget visibility states
|
||||
- Sorting configurations
|
||||
|
||||
**Not Saved**: Non-primitive type script variables (complex objects)
|
||||
|
||||
### BookmarkSet Technical Object
|
||||
|
||||
Add a BookmarkSet technical object to enable bookmark functionality.
|
||||
|
||||
#### saveBookmark(options)
|
||||
|
||||
Saves current application state as a bookmark.
|
||||
|
||||
```javascript
|
||||
BookmarkSet_1.saveBookmark({
|
||||
name: "Q4 Analysis View",
|
||||
isGlobal: true // false for personal bookmark
|
||||
});
|
||||
```
|
||||
|
||||
#### getAll()
|
||||
|
||||
Returns all available bookmarks.
|
||||
|
||||
```javascript
|
||||
var bookmarks = BookmarkSet_1.getAll();
|
||||
bookmarks.forEach(function(bookmark) {
|
||||
console.log(bookmark.id + ": " + bookmark.name);
|
||||
});
|
||||
```
|
||||
|
||||
#### apply(bookmarkId)
|
||||
|
||||
Applies a saved bookmark.
|
||||
|
||||
```javascript
|
||||
// Apply by ID
|
||||
Bookmarks.apply("22305278-9717-4296-8809-298841349359");
|
||||
|
||||
// Or from bookmark info
|
||||
var selectedBookmark = Dropdown_Bookmarks.getSelectedKey();
|
||||
BookmarkSet_1.apply(selectedBookmark);
|
||||
```
|
||||
|
||||
#### getAppliedBookmark()
|
||||
|
||||
Returns currently applied bookmark info.
|
||||
|
||||
```javascript
|
||||
var currentBookmark = BookmarkSet_1.getAppliedBookmark();
|
||||
if (currentBookmark) {
|
||||
console.log("Current bookmark:", currentBookmark.name);
|
||||
}
|
||||
```
|
||||
|
||||
#### deleteBookmark(bookmarkInfo)
|
||||
|
||||
Deletes a bookmark.
|
||||
|
||||
```javascript
|
||||
var bookmarkToDelete = BookmarkSet_1.getAppliedBookmark();
|
||||
BookmarkSet_1.deleteBookmark(bookmarkToDelete);
|
||||
```
|
||||
|
||||
### URL Parameters for Bookmarks
|
||||
|
||||
Bookmarks can be loaded via URL:
|
||||
|
||||
```
|
||||
# Load specific bookmark
|
||||
?bookmarkId=XXXXXXXXXXX
|
||||
|
||||
# Load default bookmark
|
||||
?bookmarkId=DEFAULT
|
||||
```
|
||||
|
||||
### Bookmark Dropdown Pattern
|
||||
|
||||
```javascript
|
||||
// Populate dropdown with bookmarks
|
||||
function populateBookmarkDropdown() {
|
||||
var bookmarks = BookmarkSet_1.getAll();
|
||||
var items = bookmarks.map(function(b) {
|
||||
return { key: b.id, text: b.name };
|
||||
});
|
||||
Dropdown_Bookmarks.setItems(items);
|
||||
}
|
||||
|
||||
// Apply selected bookmark
|
||||
Dropdown_Bookmarks.onSelect = function() {
|
||||
var bookmarkId = Dropdown_Bookmarks.getSelectedKey();
|
||||
if (bookmarkId) {
|
||||
BookmarkSet_1.apply(bookmarkId);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Global vs Personal Bookmarks
|
||||
|
||||
| Type | Visibility | Use Case |
|
||||
|------|-----------|----------|
|
||||
| Personal | Only creator | Individual analysis views |
|
||||
| Global | All viewers | Shared scenarios, published views |
|
||||
|
||||
---
|
||||
|
||||
## Linked Analysis API
|
||||
|
||||
Linked Analysis enables cross-widget filtering based on selections.
|
||||
|
||||
### In Analytics Applications
|
||||
|
||||
Linked Analysis must be implemented via scripting in Analytic Applications (unlike Stories where it's automatic).
|
||||
|
||||
### setFilters(selections)
|
||||
|
||||
Applies selection filters to linked widgets.
|
||||
|
||||
```javascript
|
||||
// In Chart_1 onSelect event
|
||||
var selections = Chart_1.getSelections();
|
||||
if (selections.length > 0) {
|
||||
// Set filters on linked widgets
|
||||
LinkedAnalysis.setFilters(selections);
|
||||
}
|
||||
```
|
||||
|
||||
### removeFilters()
|
||||
|
||||
Removes linked analysis filters.
|
||||
|
||||
```javascript
|
||||
LinkedAnalysis.removeFilters();
|
||||
```
|
||||
|
||||
### Custom Linked Analysis Pattern
|
||||
|
||||
```javascript
|
||||
// Create reusable linked analysis function
|
||||
function applyLinkedAnalysis(sourceWidget, targetWidgets) {
|
||||
var selections = sourceWidget.getSelections();
|
||||
|
||||
if (selections.length === 0) {
|
||||
// Clear filters on all targets
|
||||
targetWidgets.forEach(function(widget) {
|
||||
widget.getDataSource().clearAllFilters();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply selection to all targets
|
||||
var selection = selections[0];
|
||||
targetWidgets.forEach(function(widget) {
|
||||
var ds = widget.getDataSource();
|
||||
Object.keys(selection).forEach(function(dimId) {
|
||||
ds.setDimensionFilter(dimId, selection[dimId]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Usage in Chart_1.onSelect
|
||||
applyLinkedAnalysis(Chart_1, [Table_1, Chart_2, Chart_3]);
|
||||
```
|
||||
|
||||
### With Custom Widgets
|
||||
|
||||
Starting from QRC Q3 2023, Custom Widgets support Linked Analysis:
|
||||
|
||||
```javascript
|
||||
// In custom widget code
|
||||
this.dataBindings
|
||||
.getDataBinding()
|
||||
.getLinkedAnalysis()
|
||||
.setFilters(selection);
|
||||
|
||||
// Remove filters
|
||||
this.dataBindings
|
||||
.getDataBinding()
|
||||
.getLinkedAnalysis()
|
||||
.removeFilters();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Timer API
|
||||
|
||||
Timer enables scheduled script execution and animations.
|
||||
|
||||
### Timer Technical Object
|
||||
|
||||
Add a Timer from the Outline panel under Technical Objects.
|
||||
|
||||
### Timer Methods
|
||||
|
||||
```javascript
|
||||
// Start timer (interval in milliseconds)
|
||||
Timer_1.start(1000); // Every 1 second
|
||||
|
||||
// Stop timer
|
||||
Timer_1.stop();
|
||||
|
||||
// Check if running
|
||||
var isRunning = Timer_1.isRunning();
|
||||
|
||||
// Get interval
|
||||
var interval = Timer_1.getInterval();
|
||||
|
||||
// Set interval
|
||||
Timer_1.setInterval(2000); // Change to 2 seconds
|
||||
```
|
||||
|
||||
### Timer Events
|
||||
|
||||
```javascript
|
||||
// onTimeout - fires at each interval
|
||||
Timer_1.onTimeout = function() {
|
||||
// Update data or animations
|
||||
refreshDashboard();
|
||||
};
|
||||
```
|
||||
|
||||
### Auto-Refresh Pattern
|
||||
|
||||
```javascript
|
||||
// Auto-refresh dashboard every 30 seconds
|
||||
Timer_Refresh.start(30000);
|
||||
|
||||
Timer_Refresh.onTimeout = function() {
|
||||
Application.showBusyIndicator();
|
||||
|
||||
Chart_1.getDataSource().refreshData();
|
||||
Table_1.getDataSource().refreshData();
|
||||
|
||||
Application.hideBusyIndicator();
|
||||
|
||||
// Update timestamp
|
||||
Text_LastRefresh.setText(
|
||||
"Last updated: " + new Date().toLocaleTimeString()
|
||||
);
|
||||
};
|
||||
|
||||
// Stop on user interaction
|
||||
Button_Pause.onClick = function() {
|
||||
if (Timer_Refresh.isRunning()) {
|
||||
Timer_Refresh.stop();
|
||||
Button_Pause.setText("Resume Auto-Refresh");
|
||||
} else {
|
||||
Timer_Refresh.start(30000);
|
||||
Button_Pause.setText("Pause Auto-Refresh");
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Countdown Timer Pattern
|
||||
|
||||
```javascript
|
||||
var countdown = 60; // 60 seconds
|
||||
|
||||
Timer_Countdown.start(1000); // 1 second intervals
|
||||
|
||||
Timer_Countdown.onTimeout = function() {
|
||||
countdown--;
|
||||
Text_Countdown.setText("Time remaining: " + countdown + "s");
|
||||
|
||||
if (countdown <= 0) {
|
||||
Timer_Countdown.stop();
|
||||
performAction();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Animation Pattern
|
||||
|
||||
```javascript
|
||||
var currentIndex = 0;
|
||||
var dataPoints = ["Jan", "Feb", "Mar", "Apr", "May", "Jun"];
|
||||
|
||||
Timer_Animation.start(2000); // 2 second intervals
|
||||
|
||||
Timer_Animation.onTimeout = function() {
|
||||
// Highlight next data point
|
||||
Chart_1.getDataSource().setDimensionFilter("Month", dataPoints[currentIndex]);
|
||||
|
||||
currentIndex++;
|
||||
if (currentIndex >= dataPoints.length) {
|
||||
currentIndex = 0; // Loop back
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notification Badges
|
||||
|
||||
### Application Messages
|
||||
|
||||
```javascript
|
||||
// Display notifications
|
||||
Application.showMessage(ApplicationMessageType.Info, "Information message");
|
||||
Application.showMessage(ApplicationMessageType.Success, "Success message");
|
||||
Application.showMessage(ApplicationMessageType.Warning, "Warning message");
|
||||
Application.showMessage(ApplicationMessageType.Error, "Error message");
|
||||
```
|
||||
|
||||
### Custom Notification Pattern
|
||||
|
||||
```javascript
|
||||
// Check for alerts and show notification
|
||||
function checkAlerts() {
|
||||
var data = Chart_1.getDataSource().getData({
|
||||
"@MeasureDimension": "[Account].[parentId].&[Revenue]"
|
||||
});
|
||||
|
||||
if (data.rawValue < 10000) {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Warning,
|
||||
"Revenue below threshold: " + data.formattedValue
|
||||
);
|
||||
|
||||
// Open alert popup
|
||||
Popup_Alert.open();
|
||||
}
|
||||
}
|
||||
|
||||
// Call on data change
|
||||
Chart_1.onResultChanged = function() {
|
||||
checkAlerts();
|
||||
};
|
||||
```
|
||||
|
||||
### Tooltip Patterns
|
||||
|
||||
Tooltips appear automatically on chart data points. For custom tooltips:
|
||||
|
||||
```javascript
|
||||
// Use popup as custom tooltip
|
||||
Chart_1.onSelect = function() {
|
||||
var selections = Chart_1.getSelections();
|
||||
if (selections.length > 0) {
|
||||
// Get detailed data
|
||||
var details = getDetailedInfo(selections[0]);
|
||||
|
||||
// Update tooltip content
|
||||
Text_TooltipContent.setText(details);
|
||||
|
||||
// Position and show popup
|
||||
Popup_Tooltip.open();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Example: Planning Workflow with Calendar
|
||||
|
||||
```javascript
|
||||
// Initialize from calendar event
|
||||
Application.onInitialization = function() {
|
||||
var event = Calendar.getCurrentEvent();
|
||||
|
||||
if (event) {
|
||||
// Set context from calendar event
|
||||
var year = event.getContextFilter("Year");
|
||||
var version = event.getContextFilter("Version");
|
||||
|
||||
if (year) {
|
||||
Table_1.getDataSource().setDimensionFilter("Year", year);
|
||||
}
|
||||
if (version) {
|
||||
Table_1.getDataSource().setDimensionFilter("Version", version);
|
||||
}
|
||||
|
||||
// Update title
|
||||
Text_Title.setText("Planning: " + event.getName());
|
||||
|
||||
// Show task info
|
||||
Text_DueDate.setText("Due: " + event.getDueDate().toLocaleDateString());
|
||||
}
|
||||
};
|
||||
|
||||
// Submit task
|
||||
Button_Submit.onClick = function() {
|
||||
var event = Calendar.getCurrentEvent();
|
||||
|
||||
if (event && event.canUserSubmit()) {
|
||||
// Save data first
|
||||
Table_1.getPlanning().submitData();
|
||||
|
||||
// Submit for review
|
||||
event.submit();
|
||||
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Success,
|
||||
"Task submitted for review"
|
||||
);
|
||||
} else {
|
||||
Application.showMessage(
|
||||
ApplicationMessageType.Warning,
|
||||
"Cannot submit task"
|
||||
);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [DataSource API](api-datasource.md)
|
||||
- [Widgets API](api-widgets.md)
|
||||
- [Planning API](api-planning.md)
|
||||
- [Application API](api-application.md)
|
||||
|
||||
**Official Reference**: [https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html](https://help.sap.com/doc/958d4c11261f42e992e8d01a4c0dde25/release/en-US/index.html)
|
||||
Reference in New Issue
Block a user