From a4f9fa2ef87bdbee4a74ffc0b039f5d8f37213d0 Mon Sep 17 00:00:00 2001 From: Andriy Oblivantsev Date: Fri, 13 Feb 2026 13:44:41 +0100 Subject: [PATCH] Expand docs: tasks, subtasks, Gitea sync use case, Gantt/PM workflows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add architecture diagrams showing developer-to-PM bridge - Document task lifecycle: create, update, track progress, overdue detection - Add Gitea → OnlyOffice sync example with URL-based matching - Describe Gantt chart, milestone planning, and executive reporting use cases - Add sync architecture diagram (cron/webhook/CLI triggers) - Document all task fields relevant for project planning (dates, priority, milestone) --- README.md | 528 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 483 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index 0acc816..c333f4a 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,101 @@ # go-onlyoffice -Go client library for the [OnlyOffice](https://www.onlyoffice.com/) Project Management API. +Go client library for the [OnlyOffice](https://www.onlyoffice.com/) Project Management API — manage projects, tasks, subtasks, milestones, and users programmatically. -## Features +Pairs with [go-gitea-helpers](https://github.com/eSlider/go-gitea-helpers) to bridge developer issue trackers with CRM-grade project management for Gantt charts, resource planning, and executive reporting. -- Token-based authentication with automatic renewal -- Project CRUD operations (create, read, update, delete) -- Task management (create, update, list, filter) -- Milestone management -- User listing -- Query parameter serialization via struct tags +## Architecture + +```mermaid +graph TB + subgraph "Developer Tools" + GIT["Gitea / GitHub
Issues, PRs, Milestones"] + end + + subgraph "go-onlyoffice" + CL["Client"] + AUTH["Auth
Token-based"] + PRJ["Projects"] + TSK["Tasks & Subtasks"] + MS["Milestones"] + USR["Users"] + end + + subgraph "OnlyOffice CRM" + GANTT["Gantt Charts"] + PLAN["Project Planning"] + RPT["Reports & Dashboards"] + PM["PM Workflow"] + end + + GIT -->|"sync issues"| CL + CL --> AUTH + AUTH --> PRJ + AUTH --> TSK + AUTH --> MS + AUTH --> USR + + PRJ --> GANTT + TSK --> GANTT + MS --> PLAN + TSK --> RPT + PRJ --> PM +``` + +## The Problem: Developers vs. Project Managers + +```mermaid +graph LR + subgraph "Engineering World" + DEV["Developers"] + GITEA["Gitea / GitHub
Issues & PRs"] + CODE["Code Reviews"] + end + + subgraph "Management World" + PM["Project Managers"] + OO["OnlyOffice CRM
Gantt · Planning · Reports"] + EXEC["Executives
Status Reports"] + end + + DEV -->|"create issues"| GITEA + GITEA -.->|"❌ invisible"| PM + PM -->|"manual copy"| OO + OO --> EXEC + + style GITEA fill:#f96,stroke:#333 + style OO fill:#69f,stroke:#333 +``` + +**Without sync:** Project managers manually copy issue titles, deadlines, and status from Gitea into OnlyOffice. Developers don't update the CRM. Gantt charts rot. Reports lie. + +**With sync:** Issues flow automatically from Gitea to OnlyOffice with start dates, deadlines, and status. PMs get live Gantt charts. Developers keep working in Git. + +```mermaid +graph LR + subgraph "Engineering World" + DEV["Developers"] + GITEA["Gitea / GitHub"] + end + + subgraph "Sync Bridge" + SYNC["go-onlyoffice
+ go-gitea-helpers"] + end + + subgraph "Management World" + OO["OnlyOffice CRM"] + GANTT["Gantt Charts ✓"] + RPT["Reports ✓"] + end + + DEV -->|"create/close issues"| GITEA + GITEA -->|"auto-sync"| SYNC + SYNC -->|"create/update tasks"| OO + OO --> GANTT + OO --> RPT + + style SYNC fill:#4a4,stroke:#333,color:#fff +``` ## Installation @@ -17,7 +103,143 @@ Go client library for the [OnlyOffice](https://www.onlyoffice.com/) Project Mana go get github.com/eslider/go-onlyoffice ``` -## Usage +For Gitea sync (optional): + +```bash +go get github.com/eslider/go-gitea-helpers +``` + +## Quick Start + +### Connect and List Projects + +```go +client := onlyoffice.NewClient(onlyoffice.GetEnvironmentCredentials()) + +projects, _ := client.GetProjects() +for _, p := range projects { + fmt.Printf("[%d] %s — %d tasks\n", *p.ID, *p.Title, safeInt(p.TaskCountTotal)) +} +``` + +### Create a Project with Tasks and Deadlines + +```go +// Create a project +project, _ := client.CreateProject(onlyoffice.NewProjectRequest{ + Title: "Q1 2026 Release", + Description: "Backend API v2 + mobile app redesign", +}) + +// Create tasks with start/end dates (for Gantt chart) +client.CreateProjectTask(onlyoffice.NewProjectTaskRequest{ + ProjectId: *project.ID, + Title: "Design API schema", + Description: "OpenAPI 3.1 spec for all endpoints", + StartDate: onlyoffice.Time(time.Now()), + Deadline: onlyoffice.Time(time.Now().AddDate(0, 0, 14)), + Priority: 1, // High +}) + +client.CreateProjectTask(onlyoffice.NewProjectTaskRequest{ + ProjectId: *project.ID, + Title: "Implement auth service", + Description: "JWT + OAuth2 + refresh tokens", + StartDate: onlyoffice.Time(time.Now().AddDate(0, 0, 14)), + Deadline: onlyoffice.Time(time.Now().AddDate(0, 1, 0)), +}) +``` + +### List and Filter Tasks + +```go +// Get all tasks for a project +tasks, _ := client.GetTasks(onlyoffice.NewProjectGetTasksRequest(*project.ID)) + +for _, t := range tasks { + status := "open" + if t.Status != nil && *t.Status == onlyoffice.ProjectTaskStatusClosed { + status = "closed" + } + fmt.Printf(" [%s] %s", status, *t.Title) + if t.Deadline != nil { + fmt.Printf(" (due: %s)", t.Deadline.Format("2006-01-02")) + } + fmt.Println() +} +``` + +### Update Task Status and Dates + +```go +// Close a task and set actual end date +client.UpdateProjectTask(onlyoffice.ProjectTaskUpdateRequest{ + ID: taskID, + Title: "Design API schema", + Status: onlyoffice.ProjectTaskStatusClosed, + Deadline: &onlyoffice.Time(time.Now()), +}) +``` + +### Get Milestones and Task Progress + +```go +milestones, _ := client.GetProjectMilestones(project) +for _, ms := range milestones { + active := int64(0) + closed := int64(0) + if ms.ActiveTaskCount != nil { active = *ms.ActiveTaskCount } + if ms.ClosedTaskCount != nil { closed = *ms.ClosedTaskCount } + total := active + closed + + fmt.Printf("Milestone: %s — %d/%d tasks done", *ms.Title, closed, total) + if ms.Deadline != nil { + fmt.Printf(" (deadline: %s)", ms.Deadline.Format("2006-01-02")) + } + fmt.Println() +} +``` + +--- + +## Use Case: Gitea → OnlyOffice Sync + +The primary use case is **bridging developer workflows with project management**. Developers create issues in Gitea; a sync job automatically mirrors them as OnlyOffice tasks with proper start/end dates, enabling PMs to work with Gantt charts without developers leaving their Git workflow. + +### Sync Flow + +```mermaid +sequenceDiagram + participant Dev as Developer + participant Gitea + participant Sync as Sync Job + participant OO as OnlyOffice + + Dev->>Gitea: Create issue "Add OAuth2" + Note over Gitea: issue.Created = Feb 13
issue.Deadline = Mar 1 + + Sync->>Gitea: GET /repos/{org}/*/issues + Gitea-->>Sync: issues list (paginated) + + Sync->>OO: GET /api/2.0/project/filter.json + OO-->>Sync: projects list + + Sync->>Sync: Match Gitea labels → OO projects + + alt Issue not yet in OnlyOffice + Sync->>OO: POST /api/2.0/project/{id}/task.json + Note over OO: Task created:
Start: Feb 13, End: Mar 1
Description includes Gitea URL + else Issue already synced + Sync->>OO: PUT /api/2.0/project/task/{id}.json + Note over OO: Title, status, dates updated + end + + Dev->>Gitea: Close issue "Add OAuth2" + Sync->>OO: PUT status → Closed + Note over OO: Gantt chart updates automatically +``` + +### Sync Example ```go package main @@ -25,34 +247,263 @@ package main import ( "fmt" "log" + "os" + "strings" + gitea "github.com/eslider/go-gitea-helpers" onlyoffice "github.com/eslider/go-onlyoffice" ) func main() { - client := onlyoffice.NewClient(onlyoffice.Credentials{ - Url: "https://your-onlyoffice.example.com", - User: "admin@example.com", - Password: "your-password", - }) + // Connect to both services + oo := onlyoffice.NewClient(onlyoffice.GetEnvironmentCredentials()) + gc, _ := gitea.NewClient(gitea.GetEnvironmentConfig()) + owner := os.Getenv("GITEA_OWNER") - // List projects - projects, err := client.GetProjects() - if err != nil { - log.Fatal(err) + // Load all Gitea issues and OnlyOffice projects + repos, _ := gc.GetAllReposIssues(owner) + projects, _ := oo.GetProjects() + + for repoName, repo := range repos { + // Find matching OnlyOffice project by name + project := projects.Get(repoName) + if project == nil { + fmt.Printf("SKIP %s (no matching OO project)\n", repoName) + continue + } + + // Load existing tasks + tasks, _ := oo.GetTasks(onlyoffice.NewProjectGetTasksRequest(*project.ID)) + + for _, issue := range repo.Issues { + // Check if issue is already synced (URL in description) + existing := findSyncedTask(tasks, issue.HTMLURL) + + if existing != nil { + // Update existing task + status := onlyoffice.ProjectTaskStatusOpen + if issue.State == "closed" { + status = onlyoffice.ProjectTaskStatusClosed + } + + oo.UpdateProjectTask(onlyoffice.ProjectTaskUpdateRequest{ + ID: *existing.ID, + Title: issue.Title, + Status: status, + StartDate: timePtr(onlyoffice.Time(issue.Created)), + Deadline: deadlineFromIssue(issue), + }) + fmt.Printf(" UPDATED: %s\n", issue.Title) + } else { + // Create new task + status := onlyoffice.ProjectTaskStatusOpen + if issue.State == "closed" { + status = onlyoffice.ProjectTaskStatusClosed + } + + oo.CreateProjectTask(onlyoffice.NewProjectTaskRequest{ + ProjectId: *project.ID, + Title: issue.Title, + Description: issue.Body + "\n\nURL:" + issue.HTMLURL, + StartDate: onlyoffice.Time(issue.Created), + Deadline: *deadlineFromIssue(issue), + Status: status, + }) + fmt.Printf(" CREATED: %s\n", issue.Title) + } + } } - for _, p := range projects { - fmt.Printf("Project: %s (ID: %d)\n", *p.Title, *p.ID) +} + +// findSyncedTask checks task descriptions for the Gitea issue URL. +func findSyncedTask(tasks []*onlyoffice.Task, issueURL string) *onlyoffice.Task { + for _, t := range tasks { + if t.Description != nil && strings.Contains(*t.Description, issueURL) { + return t + } + } + return nil +} +``` + +### What Project Managers Get + +Once synced, OnlyOffice provides without any developer intervention: + +| Feature | How It Works | +|---|---| +| **Gantt Charts** | Tasks have `StartDate` and `Deadline` from Gitea issue created/due dates | +| **Status Tracking** | Open/closed status mirrors Gitea issue state in real time | +| **Milestone Planning** | Gitea milestones map to OnlyOffice milestones with progress % | +| **Resource Allocation** | Task assignees sync so PMs see who's working on what | +| **Sprint Reports** | Filter by date range to generate sprint/release reports | +| **Cross-Repo View** | All repos' issues appear as tasks in a unified project board | +| **Executive Dashboards** | Project progress, overdue tasks, team workload at a glance | + +### Recommended Sync Architecture + +```mermaid +graph TB + subgraph "Trigger Options" + CRON["Cron Job
every 15 min"] + HOOK["Gitea Webhook
on issue events"] + CLI["Manual CLI
on-demand"] + end + + subgraph "Sync Engine" + S["Sync Job"] + MAP["Label → Project
Mapping"] + MATCH["URL-based Task
Matching"] + DATE["Date Translation
Created → Start
Deadline → End
Closed → Actual End"] + end + + subgraph "Output" + OO["OnlyOffice Tasks"] + GANTT["Gantt Timeline"] + REP["PM Reports"] + end + + CRON --> S + HOOK --> S + CLI --> S + + S --> MAP + S --> MATCH + S --> DATE + + MAP --> OO + MATCH --> OO + DATE --> OO + OO --> GANTT + OO --> REP +``` + +--- + +## Use Case: Task Lifecycle Management + +### Creating Tasks with Full Metadata + +```go +task, _ := client.CreateProjectTask(onlyoffice.NewProjectTaskRequest{ + ProjectId: projectID, + Title: "Implement payment gateway", + Description: "Integrate Stripe API for subscription billing", + StartDate: onlyoffice.Time(time.Date(2026, 3, 1, 0, 0, 0, 0, time.UTC)), + Deadline: onlyoffice.Time(time.Date(2026, 3, 15, 0, 0, 0, 0, time.UTC)), + Priority: 1, // High + MilestoneId: milestoneID, + Notify: true, +}) +``` + +### Tracking Task Progress + +```go +tasks, _ := client.GetTasks(onlyoffice.NewProjectGetTasksRequest(projectID)) + +open, closed := 0, 0 +var overdue []*onlyoffice.Task + +for _, t := range tasks { + if t.Status != nil && *t.Status == onlyoffice.ProjectTaskStatusClosed { + closed++ + } else { + open++ + if t.Deadline != nil && t.Deadline.Before(time.Now()) { + overdue = append(overdue, t) + } + } +} + +fmt.Printf("Progress: %d/%d done (%.0f%%)\n", closed, open+closed, + float64(closed)/float64(open+closed)*100) + +if len(overdue) > 0 { + fmt.Printf("⚠ %d overdue tasks:\n", len(overdue)) + for _, t := range overdue { + fmt.Printf(" - %s (due: %s)\n", *t.Title, t.Deadline.Format("2006-01-02")) } } ``` -Or load credentials from environment variables: +### Subtask Management + +Tasks support subtasks for breaking work into smaller pieces: ```go -client := onlyoffice.NewClient(onlyoffice.GetEnvironmentCredentials()) +type Task struct { + ID *int `json:"id"` + Title *string `json:"title"` + StartDate *time.Time `json:"startDate"` + Deadline *time.Time `json:"deadline"` + Description *string `json:"description"` + Priority *int `json:"priority"` // High=1, Normal=0, Low=-1 + Status *ProjectTaskStatus `json:"status"` // Open=1, Closed=2 + Subtasks []any `json:"subtasks"` + MilestoneID *int64 `json:"milestoneId"` + Responsibles []*User `json:"responsibles"` // Assigned team members + // ... timestamps, permissions +} ``` +--- + +## API Reference + +### Client + +| Function | Description | +|---|---| +| `NewClient(credentials)` | Create a new API client | +| `GetEnvironmentCredentials()` | Load from `ONLYOFFICE_*` env vars | +| `Auth(credentials)` | Authenticate and get token | +| `Query(request, result)` | Execute raw API request | + +### Projects + +| Method | Description | +|---|---| +| `GetProjects()` | List all projects | +| `CreateProject(req)` | Create a new project | +| `UpdateProject(req)` | Update project details | +| `DeleteProject(id)` | Delete a project | +| `GetProjectMilestones(project)` | Get milestones with task counts | + +### Tasks + +| Method | Description | +|---|---| +| `GetTasks(req)` | List tasks with filtering | +| `CreateProjectTask(req)` | Create task with dates, priority, milestone | +| `UpdateProjectTask(req)` | Update title, status, dates, priority | + +### Task Fields for Gantt + +| Field | Type | Purpose | +|---|---|---| +| `StartDate` | `*time.Time` | Gantt bar start | +| `Deadline` | `*time.Time` | Gantt bar end | +| `Status` | `ProjectTaskStatus` | Open (1) / Closed (2) | +| `Priority` | `*int` | High (1) / Normal (0) / Low (-1) | +| `MilestoneID` | `*int64` | Groups tasks under milestones | +| `Responsibles` | `[]*User` | Assigned team members | +| `Subtasks` | `[]any` | Sub-items within a task | + +### Users + +| Method | Description | +|---|---| +| `GetUsers()` | List all users with profiles | + +### Helper Types + +| Type | Description | +|---|---| +| `Projects` | `[]*Project` with `.Get(title)` lookup | +| `Time` | `time.Time` wrapper with OnlyOffice JSON format | +| `Task.GetGiteaIssueLink()` | Extract Gitea URL from task description | + ## Environment Variables | Variable | Description | @@ -61,32 +512,19 @@ client := onlyoffice.NewClient(onlyoffice.GetEnvironmentCredentials()) | `ONLYOFFICE_USER` | Login email or username | | `ONLYOFFICE_PASS` | Password | -## API Reference +## Examples -### Client +| Example | Description | +|---|---| +| [basic](examples/basic/) | List projects and users | -- `NewClient(credentials)` - Create a new client -- `GetEnvironmentCredentials()` - Load credentials from env vars -- `Auth(credentials)` - Authenticate and get token -- `Query(request, result)` - Execute an API request +## Related Libraries -### Projects - -- `GetProjects()` - List all projects -- `CreateProject(req)` - Create a new project -- `UpdateProject(req)` - Update a project -- `DeleteProject(id)` - Delete a project -- `GetProjectMilestones(project)` - Get milestones for a project - -### Tasks - -- `GetTasks(req)` - List tasks with filtering -- `CreateProjectTask(req)` - Create a new task -- `UpdateProjectTask(req)` - Update a task - -### Users - -- `GetUsers()` - List all users +| Library | Description | +|---|---| +| [go-gitea-helpers](https://github.com/eSlider/go-gitea-helpers) | Gitea pagination helpers for issue/repo fetching | +| [go-matrix-bot](https://github.com/eSlider/go-matrix-bot) | Matrix bot with OnlyOffice task creation from chat | +| [go-trade](https://github.com/eSlider/go-trade) | Unified trade data model across exchanges | ## License