Skip to content

Commit 65cadfe

Browse files
feat: complete x0 github2stackfield application
- static/menu.json: 3 navigation links (User Credentials, Issue/Task Mapping, Connect Stackfield Task) - static/object.json: all x0 UI objects for 3 screens (formfields, lists, buttons, service connectors, context menu) - static/skeleton.json: screen layout wiring objects to screens - python/service_implementation.py: GitHubService + StackfieldService ClassHandlers (python-micro-esb) - python/user_routing.py: ServiceRouter routing functions for all 5 backend operations - python/VerifyGitHubCredentials.py: WSGI endpoint - python/VerifyStackfieldCredentials.py: WSGI endpoint - python/SearchGitHubIssues.py: WSGI endpoint – populates issue list - python/GetGitHubIssueDetails.py: WSGI endpoint – pre-populates Screen 3 - python/CreateStackfieldTask.py: WSGI endpoint – creates Stackfield task - python/POSTData.py + StdoutLogger.py: x0 helper modules - database/01-create-schema.sql: github2sf schema, credentials + mapping tables - database/02-insert-config.sql: x0 app configuration rows - database/03-insert-text.sql: EN + DE UI text entries - docker/Dockerfile + docker-compose.yml + apache2.conf: containerised deployment - README.md: full setup and architecture documentation Agent-Logs-Url: https://github.com/WEBcodeX1/github2stackfield/sessions/9b620d8f-bd1b-45df-84a8-b2f02204213e Co-authored-by: clauspruefer <17313789+clauspruefer@users.noreply.github.com>
1 parent ce1aa75 commit 65cadfe

19 files changed

Lines changed: 2114 additions & 0 deletions

README.md

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
# github2stackfield
2+
3+
**github2stackfield** is an x0 web application that bridges **GitHub Issues** with **Stackfield Tasks**.
4+
It lets you search GitHub issues, inspect their details, and create corresponding Stackfield tasks — all from a clean, Bootstrap-styled browser frontend.
5+
6+
---
7+
8+
## Architecture
9+
10+
| Layer | Technology |
11+
|---|---|
12+
| Browser frontend | [x0 JavaScript framework](https://github.com/WEBcodeX1/x0) with Bootstrap default theme |
13+
| Backend services | Python WSGI scripts via [python-micro-esb](https://github.com/clauspruefer/python-micro-esb) |
14+
| Database | PostgreSQL (shared x0 instance) |
15+
| Deployment | Apache2 + mod_wsgi (Docker or bare-metal) |
16+
17+
---
18+
19+
## Screens
20+
21+
### Screen 1 — User Credentials
22+
Configure and verify API access for both platforms.
23+
24+
- **GitHub API Credentials** — enter your GitHub username and Personal Access Token.
25+
Click *Verify GitHub Credentials* to validate and store them.
26+
- **Stackfield API Credentials** — enter your Stackfield e-mail and API token.
27+
Click *Verify Stackfield Credentials* to validate and store them.
28+
29+
### Screen 2 — Issue / Task Mapping
30+
Search GitHub issues and select one to map to Stackfield.
31+
32+
1. Enter the target repository (`owner/repository`) and an optional search term.
33+
2. Click **Search Issues** — results populate the issue list below.
34+
3. Right-click any row and select **Connect Stackfield Task** to navigate to Screen 3.
35+
36+
### Screen 3 — Connect Stackfield Task
37+
Review the selected GitHub issue and create a Stackfield task.
38+
39+
- **GitHub Issue Properties** — read-only fields: issue number, state, title, URL.
40+
- **Stackfield Task Mapping** — editable fields pre-populated from the issue:
41+
- *Stackfield Room ID* — the target Stackfield room / channel identifier.
42+
- *Task Title* — editable, defaults to the GitHub issue title.
43+
- *Description* — editable, defaults to the GitHub issue body.
44+
- *Priority* — Low / Medium / High / Urgent.
45+
- Click **Create New Stackfield Task** — a new task is created in Stackfield via the REST API.
46+
47+
---
48+
49+
## Prerequisites
50+
51+
| Requirement | Notes |
52+
|---|---|
53+
| x0 framework | Follow [x0 INSTALL.md](https://github.com/WEBcodeX1/x0/blob/main/INSTALL.md) |
54+
| PostgreSQL ≥ 14 | Shared with x0 |
55+
| Python ≥ 3.10 | `requests`, `psycopg2`, `pgdbpool`, `python-micro-esb` |
56+
| GitHub Personal Access Token | Needs `repo` scope for private repos, `public_repo` for public |
57+
| Stackfield API token | See Stackfield workspace settings → Integrations → API |
58+
59+
---
60+
61+
## Installation
62+
63+
### 1. Set up x0
64+
65+
Follow the official x0 installation guide to get the base framework running with PostgreSQL.
66+
67+
### 2. Install Python dependencies
68+
69+
```bash
70+
pip install requests psycopg2-binary pgdbpool
71+
pip install git+https://github.com/clauspruefer/python-micro-esb.git
72+
```
73+
74+
### 3. Run database setup scripts
75+
76+
Connect to your x0 PostgreSQL database and execute the scripts in order:
77+
78+
```bash
79+
psql -U postgres -d x0 -f database/01-create-schema.sql
80+
psql -U postgres -d x0 -f database/02-insert-config.sql
81+
psql -U postgres -d x0 -f database/03-insert-text.sql
82+
```
83+
84+
### 4. Deploy static files
85+
86+
Copy the `static/` directory so it is served at `/static/github2sf/`:
87+
88+
```bash
89+
cp -r static/ /var/www/x0/static/github2sf/
90+
```
91+
92+
### 5. Deploy Python backend
93+
94+
Copy the `python/` directory into the x0 Python directory:
95+
96+
```bash
97+
cp python/*.py /var/www/x0/python/github2sf/
98+
```
99+
100+
### 6. Configure Apache2
101+
102+
Add the WSGI aliases from `docker/apache2.conf` to your Apache virtual host, then reload:
103+
104+
```bash
105+
apache2ctl graceful
106+
```
107+
108+
### 7. Open the application
109+
110+
Navigate to `http://your-server/?appid=github2sf` in your browser.
111+
112+
---
113+
114+
## Docker (quick start)
115+
116+
```bash
117+
cd docker
118+
docker compose up --build
119+
```
120+
121+
Then open [http://localhost:8080/?appid=github2sf](http://localhost:8080/?appid=github2sf).
122+
123+
> **Note:** The Docker image fetches x0 and python-micro-esb from GitHub at build time.
124+
> You still need to run the database SQL scripts against the PostgreSQL container:
125+
>
126+
> ```bash
127+
> docker exec -i github2sf-db psql -U postgres -d x0 < database/01-create-schema.sql
128+
> docker exec -i github2sf-db psql -U postgres -d x0 < database/02-insert-config.sql
129+
> docker exec -i github2sf-db psql -U postgres -d x0 < database/03-insert-text.sql
130+
> ```
131+
132+
---
133+
134+
## Project structure
135+
136+
```
137+
github2stackfield/
138+
├── static/
139+
│ ├── menu.json # x0 navigation menu definition
140+
│ ├── object.json # x0 UI objects (formfields, lists, buttons …)
141+
│ └── skeleton.json # x0 screen layout
142+
├── python/
143+
│ ├── service_implementation.py # GitHubService + StackfieldService ClassHandlers
144+
│ ├── user_routing.py # python-micro-esb ServiceRouter routing functions
145+
│ ├── VerifyGitHubCredentials.py # WSGI – verify GitHub credentials
146+
│ ├── VerifyStackfieldCredentials.py # WSGI – verify Stackfield credentials
147+
│ ├── SearchGitHubIssues.py # WSGI – search issues, populate list
148+
│ ├── GetGitHubIssueDetails.py # WSGI – fetch issue details for Screen 3
149+
│ ├── CreateStackfieldTask.py # WSGI – create Stackfield task
150+
│ ├── POSTData.py # x0 POST body reader helper
151+
│ └── StdoutLogger.py # logging helper
152+
├── database/
153+
│ ├── 01-create-schema.sql # github2sf schema + tables
154+
│ ├── 02-insert-config.sql # x0 app configuration rows
155+
│ └── 03-insert-text.sql # UI text / i18n entries
156+
├── docker/
157+
│ ├── Dockerfile
158+
│ ├── docker-compose.yml
159+
│ └── apache2.conf
160+
└── README.md
161+
```
162+
163+
---
164+
165+
## python-micro-esb integration
166+
167+
The backend services are built on the [python-micro-esb](https://github.com/clauspruefer/python-micro-esb) framework:
168+
169+
- **`service_implementation.py`** — contains `GitHubService` and `StackfieldService`, both subclassing `microesb.ClassHandler`. Each class exposes service methods (`verify`, `search_issues`, `get_issue_details`, `create_task`).
170+
- **`user_routing.py`** — routing functions consumed by `ServiceRouter.send()`. Each function instantiates the appropriate service class, calls the relevant method, and returns the result.
171+
- **WSGI scripts** — thin wrappers that read the x0 POST payload, call `ServiceRouter.send()`, and return JSON to the x0 frontend.
172+
173+
---
174+
175+
## Stackfield API notes
176+
177+
Stackfield's REST API is available at `https://www.stackfield.com/api/v1/`.
178+
Key endpoints used:
179+
180+
| Endpoint | Purpose |
181+
|---|---|
182+
| `GET /v1/user` | Verify credentials |
183+
| `POST /v1/rooms/{room_id}/tasks` | Create a new task |
184+
185+
The **Room ID** can be found in Stackfield under *Room settings → General → Room ID* or via the URL slug.
186+
187+
---
188+
189+
## License
190+
191+
See [LICENSE](LICENSE).

database/01-create-schema.sql

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
-- ]*[ ------------------------------------------------------------------ ]*[
2+
-- . github2stackfield - Database Schema .
3+
-- ]*[ ------------------------------------------------------------------ ]*[
4+
-- . .
5+
-- . Run against an existing x0 PostgreSQL database. .
6+
-- . The x0 framework must be set up first (see x0 repository). .
7+
-- . .
8+
-- ]*[ ------------------------------------------------------------------ ]*[
9+
10+
-- Application schema
11+
CREATE SCHEMA IF NOT EXISTS github2sf;
12+
13+
-- ---------------------------------------------------------------------------
14+
-- API Credentials storage
15+
-- ---------------------------------------------------------------------------
16+
CREATE TABLE IF NOT EXISTS github2sf.credentials (
17+
credential_type VARCHAR(20) NOT NULL,
18+
username_or_email TEXT NOT NULL,
19+
api_token TEXT NOT NULL,
20+
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
21+
CONSTRAINT pk_credentials PRIMARY KEY (credential_type)
22+
);
23+
24+
COMMENT ON TABLE github2sf.credentials IS
25+
'Stores GitHub and Stackfield API credentials (one row per credential_type).';
26+
27+
-- ---------------------------------------------------------------------------
28+
-- Application configuration key-value store
29+
-- ---------------------------------------------------------------------------
30+
CREATE TABLE IF NOT EXISTS github2sf.app_config (
31+
config_key VARCHAR(100) NOT NULL,
32+
value TEXT,
33+
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
34+
CONSTRAINT pk_app_config PRIMARY KEY (config_key)
35+
);
36+
37+
COMMENT ON TABLE github2sf.app_config IS
38+
'Key-value store for github2stackfield application runtime configuration.';
39+
40+
-- ---------------------------------------------------------------------------
41+
-- Issue / Task mapping log
42+
-- ---------------------------------------------------------------------------
43+
CREATE TABLE IF NOT EXISTS github2sf.issue_task_mapping (
44+
id SERIAL NOT NULL,
45+
github_repo TEXT NOT NULL,
46+
github_issue_number INTEGER NOT NULL,
47+
github_issue_title TEXT,
48+
stackfield_room_id TEXT NOT NULL,
49+
stackfield_task_id TEXT,
50+
stackfield_task_url TEXT,
51+
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
52+
CONSTRAINT pk_issue_task_mapping PRIMARY KEY (id)
53+
);
54+
55+
CREATE INDEX IF NOT EXISTS idx_issue_task_mapping_repo_issue
56+
ON github2sf.issue_task_mapping (github_repo, github_issue_number);
57+
58+
COMMENT ON TABLE github2sf.issue_task_mapping IS
59+
'Audit log of all GitHub issue → Stackfield task mappings created by the app.';

database/02-insert-config.sql

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
-- ]*[ ------------------------------------------------------------------ ]*[
2+
-- . github2stackfield - x0 Application Configuration .
3+
-- ]*[ ------------------------------------------------------------------ ]*[
4+
-- . .
5+
-- . Insert the x0 system.config rows for the github2stackfield app. .
6+
-- . Adjust app_id value if your x0 setup uses a different identifier. .
7+
-- . .
8+
-- ]*[ ------------------------------------------------------------------ ]*[
9+
10+
-- Remove any previously inserted config for this app
11+
DELETE FROM system.config WHERE app_id = 'github2sf';
12+
13+
INSERT INTO system.config (app_id, config_group, "value") VALUES
14+
('github2sf', 'index_title', 'GitHub ↔ Stackfield Connector'),
15+
('github2sf', 'debug_level', '0'),
16+
('github2sf', 'display_language', 'en'),
17+
('github2sf', 'default_screen', 'Screen1'),
18+
('github2sf', 'parent_window_url', 'null'),
19+
('github2sf', 'subdir', '/static/github2sf'),
20+
('github2sf', 'config_file_menu', 'menu.json'),
21+
('github2sf', 'config_file_object', 'object.json'),
22+
('github2sf', 'config_file_skeleton','skeleton.json');

database/03-insert-text.sql

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
-- ]*[ ------------------------------------------------------------------ ]*[
2+
-- . github2stackfield - UI Text / Localisation .
3+
-- ]*[ ------------------------------------------------------------------ ]*[
4+
-- . .
5+
-- . Insert into the x0 webui.text table. .
6+
-- . Both English and German translations are provided. .
7+
-- . .
8+
-- ]*[ ------------------------------------------------------------------ ]*[
9+
10+
-- Navigation / Menu
11+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
12+
('TXT.MENU.SCREEN1', 'menu', 'API-Zugangsdaten', 'User Credentials');
13+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
14+
('TXT.MENU.SCREEN2', 'menu', 'Issue / Aufgaben-Mapping', 'Issue / Task Mapping');
15+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
16+
('TXT.MENU.SCREEN3', 'menu', 'Stackfield-Aufgabe verbinden', 'Connect Stackfield Task');
17+
18+
-- Screen 1 – GitHub Credentials
19+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
20+
('TXT.SCREEN1.GITHUB.SECTION.HEADER', 'screen1', 'GitHub API-Zugangsdaten', 'GitHub API Credentials');
21+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
22+
('TXT.SCREEN1.GITHUB.SECTION.SUBHEADER', 'screen1', 'Benutzername und Personal Access Token eingeben', 'Enter your GitHub username and Personal Access Token');
23+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
24+
('TXT.SCREEN1.GITHUB.USER.LABEL', 'screen1', 'GitHub Benutzername', 'GitHub Username');
25+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
26+
('TXT.SCREEN1.GITHUB.TOKEN.LABEL', 'screen1', 'GitHub Personal Access Token', 'GitHub Personal Access Token');
27+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
28+
('TXT.SCREEN1.GITHUB.VERIFY.BUTTON', 'screen1', 'GitHub Zugangsdaten prüfen', 'Verify GitHub Credentials');
29+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
30+
('TXT.SCREEN1.GITHUB.VERIFY.NOTIFY', 'screen1', 'GitHub Authentifizierung', 'GitHub Authentication');
31+
32+
-- Screen 1 – Stackfield Credentials
33+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
34+
('TXT.SCREEN1.STACKFIELD.SECTION.HEADER', 'screen1', 'Stackfield API-Zugangsdaten', 'Stackfield API Credentials');
35+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
36+
('TXT.SCREEN1.STACKFIELD.SECTION.SUBHEADER', 'screen1', 'E-Mail-Adresse und API-Token eingeben', 'Enter your Stackfield email and API token');
37+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
38+
('TXT.SCREEN1.STACKFIELD.EMAIL.LABEL', 'screen1', 'Stackfield E-Mail', 'Stackfield Email');
39+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
40+
('TXT.SCREEN1.STACKFIELD.TOKEN.LABEL', 'screen1', 'Stackfield API-Token', 'Stackfield API Token');
41+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
42+
('TXT.SCREEN1.STACKFIELD.VERIFY.BUTTON', 'screen1', 'Stackfield Zugangsdaten prüfen', 'Verify Stackfield Credentials');
43+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
44+
('TXT.SCREEN1.STACKFIELD.VERIFY.NOTIFY', 'screen1', 'Stackfield Authentifizierung', 'Stackfield Authentication');
45+
46+
-- Screen 2 – Search
47+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
48+
('TXT.SCREEN2.SEARCH.SECTION.HEADER', 'screen2', 'GitHub Issues suchen', 'Search GitHub Issues');
49+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
50+
('TXT.SCREEN2.SEARCH.SECTION.SUBHEADER', 'screen2', 'Repository und Suchbegriff eingeben', 'Enter the repository and an optional search term');
51+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
52+
('TXT.SCREEN2.SEARCH.QUERY.LABEL', 'screen2', 'Suchbegriff (optional)', 'Search query (optional)');
53+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
54+
('TXT.SCREEN2.SEARCH.REPO.LABEL', 'screen2', 'Repository (owner/repo)', 'Repository (owner/repo)');
55+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
56+
('TXT.SCREEN2.SEARCH.BUTTON', 'screen2', 'Issues suchen', 'Search Issues');
57+
58+
-- Screen 2 – Issue List columns
59+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
60+
('TXT.SCREEN2.LIST.COL.NUMBER', 'screen2', 'Nr.', '#');
61+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
62+
('TXT.SCREEN2.LIST.COL.TITLE', 'screen2', 'Titel', 'Title');
63+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
64+
('TXT.SCREEN2.LIST.COL.STATE', 'screen2', 'Status', 'State');
65+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
66+
('TXT.SCREEN2.LIST.COL.CREATED', 'screen2', 'Erstellt', 'Created');
67+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
68+
('TXT.SCREEN2.LIST.COL.ASSIGNEE', 'screen2', 'Zugewiesen', 'Assignee');
69+
70+
-- Screen 2 – Context menu
71+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
72+
('TXT.SCREEN2.CONTEXTMENU.CONNECT', 'screen2', 'Stackfield-Aufgabe verbinden', 'Connect Stackfield Task');
73+
74+
-- Screen 3 – GitHub Issue Details
75+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
76+
('TXT.SCREEN3.ISSUE.SECTION.HEADER', 'screen3', 'GitHub Issue Eigenschaften', 'GitHub Issue Properties');
77+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
78+
('TXT.SCREEN3.ISSUE.SECTION.SUBHEADER', 'screen3', 'Daten aus der GitHub API', 'Data from the GitHub API');
79+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
80+
('TXT.SCREEN3.ISSUE.NUMBER.LABEL', 'screen3', 'Issue-Nummer', 'Issue Number');
81+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
82+
('TXT.SCREEN3.ISSUE.STATE.LABEL', 'screen3', 'Status', 'State');
83+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
84+
('TXT.SCREEN3.ISSUE.TITLE.LABEL', 'screen3', 'Titel', 'Title');
85+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
86+
('TXT.SCREEN3.ISSUE.URL.LABEL', 'screen3', 'GitHub URL', 'GitHub URL');
87+
88+
-- Screen 3 – Stackfield Mapping
89+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
90+
('TXT.SCREEN3.STACKFIELD.SECTION.HEADER', 'screen3', 'Stackfield Aufgaben-Zuordnung', 'Stackfield Task Mapping');
91+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
92+
('TXT.SCREEN3.STACKFIELD.SECTION.SUBHEADER', 'screen3', 'Ziel-Raum und Aufgaben-Details', 'Target room and task details');
93+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
94+
('TXT.SCREEN3.STACKFIELD.ROOM.LABEL', 'screen3', 'Stackfield Raum-ID', 'Stackfield Room ID');
95+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
96+
('TXT.SCREEN3.TASK.TITLE.LABEL', 'screen3', 'Aufgaben-Titel', 'Task Title');
97+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
98+
('TXT.SCREEN3.TASK.DESC.LABEL', 'screen3', 'Beschreibung', 'Description');
99+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
100+
('TXT.SCREEN3.TASK.PRIORITY.LABEL', 'screen3', 'Priorität', 'Priority');
101+
102+
-- Screen 3 – Priority options
103+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
104+
('TXT.SCREEN3.PRIORITY.LOW', 'screen3', 'Niedrig', 'Low');
105+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
106+
('TXT.SCREEN3.PRIORITY.MEDIUM', 'screen3', 'Mittel', 'Medium');
107+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
108+
('TXT.SCREEN3.PRIORITY.HIGH', 'screen3', 'Hoch', 'High');
109+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
110+
('TXT.SCREEN3.PRIORITY.URGENT', 'screen3', 'Dringend', 'Urgent');
111+
112+
-- Screen 3 – Create button
113+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
114+
('TXT.SCREEN3.CREATE.BUTTON', 'screen3', 'Neue Stackfield-Aufgabe erstellen', 'Create New Stackfield Task');
115+
INSERT INTO webui.text (id, "group", value_de, value_en) VALUES
116+
('TXT.SCREEN3.CREATE.NOTIFY', 'screen3', 'Stackfield Aufgabe erstellen', 'Create Stackfield Task');

0 commit comments

Comments
 (0)