The iTop Integration for Nextcloud follows a layered architecture with clear separation of concerns:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Nextcloud Frontend (Vue.js) β
β ββββββββββββββββ¬βββββββββββββββ¬βββββββββββββββββββββββββββ β
β β Dashboard β Unified β Rich Preview β Smart β β
β β Widget β Search UI β Widgets β Picker β β
β ββββββββββββββββ΄βββββββββββββββ΄βββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β HTTP/JSON (OCS API)
ββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββ
β Nextcloud Backend (PHP) β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β Controllers (API Endpoints) ββ
β β ββββββββββββββββ¬βββββββββββββββββ¬βββββββββββββββββββ ββ
β β βItopAPI β Config β (Future β ββ
β β βController β Controller β Controllers) β ββ
β β ββββββββββββββββ΄βββββββββββββββββ΄βββββββββββββββββββ ββ
β βββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β βββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββ
β β Service Layer ββ
β β ββββββββββββββββ¬βββββββββββββββ¬βββββββββββββββββββββ ββ
β β βItopAPI β Profile β PreviewMapper β ββ
β β βService β Service β (Future) β ββ
β β ββββββββββββββββ΄βββββββββββββββ΄βββββββββββββββββββββ ββ
β βββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β βββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββ
β β Provider Layer ββ
β β ββββββββββββββββ¬βββββββββββββββ¬βββββββββββββββββββββ ββ
β β βUnified β Reference β SmartPicker β ββ
β β βSearch β Provider β Provider β ββ
β β βProvider β (Future) β (Future) β ββ
β β ββββββββββββββββ΄βββββββββββββββ΄βββββββββββββββββββββ ββ
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β REST API (Auth-Token header)
ββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββ
β iTop CMDB β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β REST/JSON API v1.3 ββ
β β - core/get (CI queries) ββ
β β - core/check_credentials (auth validation) ββ
β β - list_operations (capability discovery) ββ
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
lib/
βββ Controller/
β βββ ItopAPIController.php # Ticket & CI queries
β βββ ConfigController.php # Settings management
βββ Service/
β βββ ItopAPIService.php # Primary API client
β βββ ProfileService.php # User profile detection (Phase 2)
β βββ PreviewMapper.php # CI β DTO transformation (Phase 2)
β βββ CacheService.php # Caching layer (Phase 2)
βββ Search/
β βββ ItopSearchProvider.php # Current search implementation
β βββ ItopUnifiedSearchProvider.php # CI search (Phase 3)
βββ Reference/
β βββ ItopReferenceProvider.php # Rich previews (Phase 4)
βββ Picker/
β βββ ItopSmartPickerProvider.php # Smart suggestions (Phase 3)
βββ Dashboard/
β βββ ItopWidget.php # Dashboard widget (current)
βββ Settings/
β βββ Admin.php # Admin settings UI
β βββ Personal.php # Personal settings UI
βββ Notification/
β βββ Notifier.php # Notification handler
βββ AppInfo/
βββ Application.php # App bootstrap
βββ routes.php # Route definitions
src/
βββ components/
β βββ CIPreviewWidget.vue # Common CI preview (Phase 4)
β βββ CISearchResult.vue # Search result item (Phase 3)
β βββ DashboardWidget.vue # Current dashboard
βββ views/
β βββ AdminSettings.vue # Admin settings panel
β βββ PersonalSettings.vue # Personal settings panel
βββ main.js # Vue app entry point
sequenceDiagram
participant User
participant PersonalSettings
participant ConfigController
participant iTop
User->>PersonalSettings: Enter Personal Token
PersonalSettings->>ConfigController: POST /config/set {personal_token}
ConfigController->>iTop: POST core/get Person WHERE id=:current_contact_id
Note over ConfigController,iTop: Headers: Auth-Token: {personal_token}
iTop-->>ConfigController: {person_id: 3, first_name: "Boris", ...}
ConfigController->>ConfigController: Store person_id=3, user_id=1
ConfigController->>ConfigController: Discard personal_token
ConfigController-->>PersonalSettings: {success: true, user_info}
PersonalSettings-->>User: "Configuration successful!"
sequenceDiagram
participant User
participant UnifiedSearch
participant ItopAPIService
participant ProfileService
participant iTop
User->>UnifiedSearch: Search "laptop"
UnifiedSearch->>ItopAPIService: search(userId, "laptop")
ItopAPIService->>ItopAPIService: getPersonId(userId) β 3
ItopAPIService->>ProfileService: isPortalOnly(userId)
ProfileService-->>ItopAPIService: true
ItopAPIService->>ItopAPIService: Build query:<br/>SELECT PC WHERE contacts_list MATCHES Person WHERE id=3<br/>AND name LIKE '%laptop%'
ItopAPIService->>iTop: POST core/get {query}
Note over ItopAPIService,iTop: Headers: Auth-Token: {app_token}
iTop-->>ItopAPIService: {objects: {PC::1, PC::2}}
ItopAPIService-->>UnifiedSearch: [PC-001, PC-002]
UnifiedSearch-->>User: Display 2 results
sequenceDiagram
participant User
participant UnifiedSearch
participant ItopAPIService
participant ProfileService
participant iTop
User->>UnifiedSearch: Search "laptop"
UnifiedSearch->>ItopAPIService: search(userId, "laptop")
ItopAPIService->>ItopAPIService: getPersonId(userId) β 1
ItopAPIService->>ProfileService: isPortalOnly(userId)
ProfileService-->>ItopAPIService: false (has Service Desk Agent)
ItopAPIService->>ItopAPIService: Build query:<br/>SELECT PC WHERE name LIKE '%laptop%'
ItopAPIService->>iTop: POST core/get {query}
Note over ItopAPIService,iTop: Headers: Auth-Token: {app_token}
iTop-->>ItopAPIService: {objects: {PC::1, PC::2, PC::5, PC::7}}
ItopAPIService-->>UnifiedSearch: [PC-001, PC-002, PC-005, PC-007]
UnifiedSearch-->>User: Display 4 results
sequenceDiagram
participant User
participant TextApp
participant ReferenceProvider
participant PreviewMapper
participant ItopAPIService
participant iTop
User->>TextApp: Paste URL:<br/>http://itop/...?class=PC&id=5
TextApp->>ReferenceProvider: resolveReference(url)
ReferenceProvider->>ReferenceProvider: Parse URL β class=PC, id=5
ReferenceProvider->>ItopAPIService: getCIPreview(userId, "PC", 5)
ItopAPIService->>iTop: POST core/get PC key=5 output_fields={full set}
iTop-->>ItopAPIService: {PC::5 with all fields}
ItopAPIService->>PreviewMapper: mapCIToPreview(data, "PC")
PreviewMapper-->>ItopAPIService: {title, subtitle, badges, chips, extras}
ItopAPIService-->>ReferenceProvider: Preview DTO
ReferenceProvider-->>TextApp: Rich preview HTML
TextApp-->>User: Display preview widget
Responsibilities:
- Manage application token encryption/decryption
- Store and retrieve user person_id
- Execute all iTop REST API requests
- Handle authentication errors
- Log API interactions
Key Methods:
class ItopAPIService {
// Authentication
private function getApplicationToken(): ?string
private function getPersonId(string $userId): ?string
// API Operations
public function request(string $userId, array $params): array
public function search(string $userId, string $query, int $offset, int $limit): array
public function getCurrentUser(string $userId): array
// Ticket Operations
public function getUserCreatedTickets(string $userId, ?string $since, ?int $limit): array
public function getUserCreatedTicketsCount(string $userId): array
public function getTicketInfo(string $userId, int $ticketId, string $class): array
// CI Operations (Phase 2+)
public function getCIPreview(string $userId, string $class, int $id): array
public function searchCIs(string $userId, string $term, array $classes): array
}Dependencies:
IConfig- Configuration storageICrypto- Token encryptionIClientService- HTTP clientICacheFactory- Result cachingLoggerInterface- LoggingIL10N- Internationalization
Responsibilities:
- Detect user profiles from iTop
- Determine if user is portal-only
- Cache profile information with TTL
- Handle profile changes
Key Methods:
class ProfileService {
public function getUserProfiles(string $userId): array
public function isPortalOnly(string $userId): bool
public function refreshProfiles(string $userId): void
private function parseProfileList(array $userData): array
}Caching Strategy:
- TTL: 300 seconds (5 minutes)
- Cache key:
profile_{userId} - Invalidation: Manual refresh or TTL expiry
Responsibilities:
- Transform iTop CI data to preview DTOs
- Apply class-specific field mappings
- Generate status badges
- Format timestamps
Key Methods:
class PreviewMapper {
public function mapCIToPreview(array $ciData, string $class): array
private function getCommonFields(array $fields): array
private function getClassSpecificFields(array $fields, string $class): array
private function formatBadges(array $fields): array
private function formatChips(array $fields): array
}DTO Structure:
return [
'title' => 'LAPTOP-001',
'subtitle' => 'PC β’ Demo',
'badges' => [
['label' => 'Production', 'type' => 'success'],
['label' => 'High', 'type' => 'warning']
],
'chips' => [
['icon' => 'building', 'label' => 'Demo'],
['icon' => 'map-marker', 'label' => 'Vienna Office'],
['icon' => 'barcode', 'label' => 'ASSET-001']
],
'extras' => [
['label' => 'Brand', 'value' => 'Dell'],
['label' => 'Model', 'value' => 'Latitude 7420'],
['label' => 'CPU', 'value' => 'Intel i7-1185G7'],
['label' => 'RAM', 'value' => '16GB']
],
'url' => 'http://itop/pages/UI.php?operation=details&class=PC&id=1',
'icon' => 'PC.svg',
'last_update' => '2025-01-15 10:30:00'
];Responsibilities:
- Implement distributed caching
- Manage cache TTLs
- Handle cache invalidation
- Support ETag-based caching
Key Methods:
class CacheService {
public function get(string $key): mixed
public function set(string $key, mixed $value, int $ttl): void
public function delete(string $key): void
public function invalidateUserCache(string $userId): void
}Cache Strategy:
| Data Type | TTL | Key Pattern | Invalidation |
|---|---|---|---|
| CI Preview | 60s | ci_preview_{userId}_{class}_{id} |
Manual or TTL |
| Search Results | 30s | search_{userId}_{hash(query)} |
TTL only |
| Profile Data | 300s | profile_{userId} |
Manual or TTL |
| User Info | 600s | user_info_{userId} |
Manual or TTL |
Interface: IProvider from Nextcloud
Responsibilities:
- Register as search provider for "iTop CIs"
- Execute CI searches across all enabled classes
- Return formatted search results
- Respect rate limits
Key Methods:
class ItopUnifiedSearchProvider implements IProvider {
public function getId(): string // "itop_ci"
public function getName(): string // "iTop Configuration Items"
public function getOrder(string $route, array $routeParams): int
public function search(IUser $user, ISearchQuery $query): SearchResult
}Search Result Format:
return SearchResult::complete(
$this->getName(),
array_map(function($ci) {
return new SearchResultEntry(
$ci['url'], // thumbnail URL (CI icon)
$ci['name'], // title
$ci['org'] . ' β’ ' . $ci['status'], // subline
$ci['url'], // resource URL
'icon-itop', // icon CSS class
true // rounded
);
}, $results)
);Interface: IReferenceProvider from Nextcloud
Responsibilities:
- Detect iTop CI URLs in text
- Fetch CI data from API
- Render rich preview widgets
- Handle access permissions
Key Methods:
class ItopReferenceProvider implements IReferenceProvider {
public function matchReference(string $referenceText): bool
public function resolveReference(string $referenceText): ?IReference
public function getCachePrefix(string $referenceId): string
public function getCacheKey(string $referenceId): ?string
public function invalidateUserCache(string $userId): void
}URL Pattern Matching:
private const ITOP_URL_PATTERN = '/^https?:\/\/[^\/]+\/pages\/UI\.php\?.*operation=details.*class=([A-Za-z]+).*id=(\d+)/';Interface: ISmartPickerProvider from Nextcloud
Responsibilities:
- Provide CI suggestions in text editors
- Debounce search queries
- Return clickable CI links
- Format suggestions with icons
Key Methods:
class ItopSmartPickerProvider implements ISmartPickerProvider {
public function getId(): string // "itop_ci"
public function getName(): string // "iTop CIs"
public function getItemIconUrl(string $itemId): string
public function search(string $searchTerm, int $offset, int $limit): array
}Storage: oc_appconfig table
| Key | Type | Encrypted | Description |
|---|---|---|---|
admin_instance_url |
string | No | iTop base URL |
user_facing_name |
string | No | Display name (e.g., "iTop Dev") |
application_token |
string | Yes | Admin API token (ICrypto) |
enabled_ci_classes |
JSON | No | List of enabled CI classes (Phase 2) |
global_result_limit |
int | No | Max search results (default: 20) |
Access: Admin users only
Storage: oc_preferences table
| Key | Type | Description |
|---|---|---|
person_id |
string | User's iTop Person ID (numeric) |
user_id |
string | User's iTop User ID (numeric) |
is_portal_only |
bool | Cached profile check (Phase 2) |
profiles_last_check |
timestamp | Last profile refresh time |
notification_enabled |
bool | Enable ticket notifications |
last_open_check |
timestamp | Last notification check |
Access: Per-user (isolated)
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 1. Admin Setup β
β Admin generates Application Token in iTop β
β β β
β Admin enters token in Nextcloud Admin Settings β
β β β
β ICrypto::encrypt(token) β Store in oc_appconfig β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 2. User Setup β
β User generates Personal Token in iTop β
β β β
β User enters token in Nextcloud Personal Settings β
β β β
β POST /config/set {personal_token} β
β β β
β Validate token: core/get Person :current_contact_id β
β β β
β Extract person_id β Store in oc_preferences β
β β β
β Discard personal_token (never stored) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 3. API Request β
β User triggers search/preview β
β β β
β Retrieve person_id from oc_preferences β
β β β
β Retrieve & decrypt application_token β
β β β
β Build OQL query filtered by person_id (if portal) β
β β β
β POST to iTop with Auth-Token: {app_token} β
β β β
β Return results (within user permissions) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// Pseudo-code for all CI queries
function queryCIs(userId, query) {
personId = getPersonId(userId);
if (!personId) throw NotConfiguredError;
isPortalOnly = profileService.isPortalOnly(userId);
if (isPortalOnly) {
// Enforce contact-based filtering
query += " AND contacts_list MATCHES Person WHERE id = " + personId;
}
// else: Power users get full CMDB (within ACL)
appToken = decrypt(getApplicationToken());
return itopAPI.request(query, appToken);
}ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Client-Side Cache (Browser) β
β - HTTP Cache-Control headers β
β - ETag support for preview images β
β - TTL: 60s for previews β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Server-Side Cache (Redis/Memcache) β
β - CI preview data (60s TTL) β
β - Search results (30s TTL) β
β - Profile data (300s TTL) β
β - User info (600s TTL) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β iTop API (Source of Truth) β
β - Always queried if cache miss β
β - Always queried if cache expired β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- User-triggered: Clear cache when user updates personal settings
- Time-based: TTL expiry (most common)
- Admin-triggered: Clear all caches when admin updates application token
- Manual: Expose cache clear button in admin settings (Phase 2)
iTop API Error (e.g., 401 Unauthorized)
β
ItopAPIService catches ClientException
β
Returns ['error' => 'Authentication failed', 'error_code' => 401]
β
Controller receives error response
β
Logs error (without sensitive data)
β
Returns DataResponse with user-friendly message
β
Frontend displays error notification
β
User sees: "Could not connect to iTop - check your settings"
| Error Type | HTTP Code | User Message | Admin Action Required |
|---|---|---|---|
| Network Error | 503 | "iTop server unavailable" | Check iTop status |
| Auth Error | 401 | "Authentication failed" | Reconfigure tokens |
| Portal Block | 400 | "Portal user restriction" | Configure app token |
| Not Configured | 400 | "Please configure settings" | User action |
| Empty Results | 200 | "No results found" | None (expected) |
| Column | Type | Example |
|---|---|---|
| appid | varchar(255) | integration_itop |
| configkey | varchar(255) | application_token |
| configvalue | text | {encrypted_blob} |
| Column | Type | Example |
|---|---|---|
| userid | varchar(64) | boris |
| appid | varchar(255) | integration_itop |
| configkey | varchar(255) | person_id |
| configvalue | text | 3 |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Load Balancer β
ββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββΌβββββββββ¬βββββββββββββββββ
β β β
βββββΌβββββ ββββββΌβββββ ββββββΌβββββ
β NC #1 β β NC #2 β β NC #3 β
β +App β β +App β β +App β
βββββ¬βββββ ββββββ¬βββββ ββββββ¬βββββ
β β β
ββββββββββ¬ββββββββ΄βββββββββββββββββ
β
ββββββββββΌβββββββββββββββββββββββββββββ
β Shared Redis Cache β
βββββββββββββββββββββββββββββββββββββββ
β
ββββββββββΌβββββββββββββββββββββββββββββ
β iTop Server (192.168.139.92) β
β - REST API Enabled β
β - Application Token Configured β
βββββββββββββββββββββββββββββββββββββββ
Required PHP Extensions:
curlorguzzle(HTTP client)json(JSON parsing)openssl(ICrypto encryption)
Nextcloud Version:
- Minimum: 30.0
- Tested: 30.x
iTop Version:
- Minimum: 3.0
- Recommended: 3.1+
- API Version: 1.3
| Operation | Target | Notes |
|---|---|---|
| Admin settings load | <200ms | Cached config |
| Personal settings load | <300ms | Includes user info fetch |
| Unified search (cached) | <500ms | Server-side cache hit |
| Unified search (uncached) | <2s | iTop API + processing |
| Rich preview (cached) | <300ms | Render from cache |
| Rich preview (uncached) | <1s | Single CI fetch |
| Dashboard widget | <1s | Ticket count query |
Concurrent Users: 100+ (limited by iTop API capacity)
Request Rate: 5 req/sec/user (recommended rate limit)
Cache Hit Rate Target: >80% for previews, >60% for searches
- ProfileService with cached profile detection
- PreviewMapper for CI data transformation
- CacheService with distributed caching
- Enhanced error handling and retry logic
- Webhook support for real-time CI updates
- Batch API requests (if iTop 3.2+ supports)
- Offline mode with local cache
- Advanced search filters (by organization, status, etc.)
- Implementation: lib/Service/ItopAPIService.php
- Security: security-auth.md
- API Patterns: itop-api.md
- Testing: testing.md