zwischenstand

This commit is contained in:
Robin Grenzdörfer 2024-02-13 16:36:09 +00:00
parent e5fba13002
commit 5763db4e22
187 changed files with 16453 additions and 1359 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -10,6 +10,8 @@ help: ## show this help
@echo MAKE TARGETS @echo MAKE TARGETS
@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' @grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
docker-up: ## bring docker compose stack up in background docker-up: ## bring docker compose stack up in background
$(DOCKER_COMPOSE) --profile tibi up -d $(DOCKER_COMPOSE) --profile tibi up -d

12
api/assets/dist.yml Normal file
View File

@ -0,0 +1,12 @@
# Ordnerpfade, die über den tibi-server direkt erreichbar seien sollen,
# können über den "path" relativ zur "config.yml" definiert werden.
# Durch die "name"-Definition werden diese Pfade eindeutig unterschieden.
# Für folgende Beispielangaben bildet sich folgende URL:
#
# TIBI-SERVER-URL/api/v1/_/NAMESPACE/_/assets/_dist_/
#
# Jeder Zugriff wird intern umgeleitet auf ../frontend/_dist_/
# (relativ zur "config.yml").
# Es ist ausschließlich ein unbeschränkter Lesezugriff (GET-Methode) möglich.
name: _dist_
path: ../frontend/dist

View File

@ -41,22 +41,30 @@ fields:
- name: collectionName - name: collectionName
type: string type: string
meta: meta:
label: Collection Name label:
de: Collection Name
en: Collection Name
- name: entryId - name: entryId
type: string type: string
meta: meta:
label: Entry ID label:
de: Entry ID
en: Entry ID
- name: versionNr - name: versionNr
type: number type: number
meta: meta:
label: Version Nr label:
de: Version Nr
en: Version Nr
- name: manipulatedBy - name: manipulatedBy
type: string type: string
meta: meta:
label: Manipulated By label:
de: Manipulated By
en: Manipulated By
- name: eventDescription - name: eventDescription
type: string type: string
@ -65,38 +73,59 @@ fields:
widget: select widget: select
choices: choices:
- id: create - id: create
name: Create name:
de: Erstellt
en: Create
- id: update - id: update
name: Update name:
de: Update
en: Update
- id: delete - id: delete
name: Delete name:
de: Gelöscht
en: Delete
- id: recreate - id: recreate
name: Recreate name:
de: Wiederhergestellt
en: Recreate
- id: activate - id: activate
name: Activate name:
de: Aktiviert
en: Activate
- name: updateLogs - name: updateLogs
type: object[] type: object[]
meta: meta:
label: Veränderungen label:
de: Veränderungen
en: Update Logs
subFields: subFields:
- name: field - name: field
type: string type: string
meta: meta:
label: Feldname label:
de: Feldname
en: Fieldname
- name: previous - name: previous
type: string type: string
meta: meta:
label: Vorheriger Wert label:
de: Vorheriger Wert
en: Previous Value
- name: current - name: current
type: string type: string
meta: meta:
label: Aktueller Wert label:
de: Aktueller Wert
en: Current Value
- name: entry - name: entry
type: object type: object
meta: meta:
label: Entry label:
de: Eintrag
en: Entry

View File

@ -37,7 +37,6 @@ meta:
tablist: tablist:
activeTab: general activeTab: general
tabs: tabs:
- name: general - name: general
label: Allgemein label: Allgemein
@ -124,8 +123,12 @@ fields:
- type: string - type: string
name: path name: path
meta: meta:
label: Pfad label:
helperText: "Ein Pfad sollte mit einem / starten und ohne eins enden." de: Pfad
en: Path
helperText:
de: "Ein Pfad sollte mit einem / starten und ohne eins enden."
en: "A path should start with a / and end without one."
containerProps: containerProps:
layout: layout:
size: size:
@ -133,73 +136,20 @@ fields:
small: "col-12" small: "col-12"
large: "col-6" large: "col-6"
- type: boolean - !include fields/active.yml
name: active - !include fields/contentType.yml
meta:
label: Aktiv
helperText: Ist dies Aktiviert, so wird der Inhalt verfügbar.
defaultValue: true
containerProps:
layout:
size:
default: "col-6"
small: "col-12"
large: "col-6"
- type: string - !include fields/pageTitle.yml
name: type - !include fields/rows.yml
meta:
label: Inhaltstyp
widget: select
defaultValue: page
containerProps:
layout:
size:
default: "col-6"
small: "col-12"
large: "col-6"
choices:
- name: Seite
id: page
- name: pageTitle
type: string
meta:
label: Titel der Seite
helperText: "Dieser Titel wird in der Seite als h1 angezeigt."
dependsOn:
eval: $.type == "page"
containerProps:
layout:
size:
default: "col-6"
small: "col-12"
large: "col-6"
- name: rows
type: object[]
meta:
label: Zeilen
widget: grid
metaElements: []
subFields: !include fieldLists/row.yml
- name: meta - name: meta
type: object type: object
meta: meta:
label: Meta Agaben label:
de: Meta Agaben
en: Meta Data
dependsOn: dependsOn:
eval: $.type == "page" eval: $.type == "page"
subFields: !include fieldLists/meta.yml subFields: !include fieldLists/meta.yml
- name: sort - !include fields/manualSort.yml
type: number
meta:
label:
de: Manuelle Sortierung
en: Manual Sorting
inputProps:
{ readonly: true, placeholder: { de: "Wert wird automatisch gesetzt", en: "Value is set automatically" } }
helperText:
de: Dieses Feld wird für die manuelle Sortierung benötigt. Sobald ein Eintrag per Drag&Drop verschoben wurde, wird die neue Position innerhalb der Liste eingetragen.
en: This field is required for manual sorting. As soon as an entry is moved using Drag&Drop, the new position is entered in the list.

View File

@ -1,16 +1,49 @@
# attribute auf column ebene
- name: contentType - name: contentType
type: string type: string
meta: meta:
label: "" label:
de: Inhaltstyp
en: Content Type
widget: select widget: select
choices: choices:
- name: Bild - name:
id: image de: Bilder
en: Images
id: images
- name: Modul Import - name:
de: Modul Import
en: Module Import
id: moduleImport id: moduleImport
- name:
de: Formular
en: form
id: form
- name: Text - name:
de: Text
en: Text
id: text id: text
- name:
de: Google Maps
en: Google Maps
id: googleMaps
- !include ../fields/images.yml
- name: moduleImport
type: string
meta:
label:
de: Modul Import
en: Module Import
dependsOn:
eval: $parent.contentType == 'moduleImport' || $parent.contentType == 'form'
widget: foreignKey
foreign:
collection: module
id: id
subNavigation: 0
render:
defaultCollectionViews: true
- !include ../fields/text.yml

View File

@ -13,12 +13,17 @@ subFields:
- name: groupTitle - name: groupTitle
type: string type: string
meta: meta:
label: Checkbox Gruppe Titel label:
de: Gruppe Titel
en: Group title
- name: checkboxes - name: checkboxes
type: object[] type: object[]
meta: meta:
label: Checkbox Gruppe label:
de: Checkbox Gruppe
en: Checkbox Group
direction: row direction: row
widget: containerLessObjectArray widget: containerLessObjectArray
subFields: subFields:

View File

@ -11,6 +11,7 @@ meta:
eval: $parent?.inputWidgets?.includes('defaultCalendar') eval: $parent?.inputWidgets?.includes('defaultCalendar')
subFields: subFields:
- name: standardInputProperties - name: standardInputProperties
type: object
meta: meta:
widget: containerLessObject widget: containerLessObject
subFields: !include standardInputProperties.yml subFields: !include standardInputProperties.yml

View File

@ -10,29 +10,25 @@ subFields:
- name: props - name: props
type: object type: object
meta: meta:
label: Datumauswahl Eigenschaften label:
de: Datumauswahl Eigenschaften
en: Date selection properties
subFields: subFields:
- name: allowedDateRanges - name: allowedDateRanges
type: object[] type: object[]
meta: meta:
label: Erlaubte Datumsbereiche label:
de: Erlaubte Datumsbereiche
en: Allowed date ranges
widget: containerLessObjectArray widget: containerLessObjectArray
subFields: subFields:
- name: from - !include ../../fields/from.yml
type: date
meta:
label: Von
widget: date
containerProps:
layout:
size:
default: "col-6"
small: "col-12"
large: "col-6"
- name: to - name: to
type: date type: date
meta: meta:
label: Bis label:
de: Bis
en: To
widget: date widget: date
containerProps: containerProps:
layout: layout:
@ -41,34 +37,10 @@ subFields:
small: "col-12" small: "col-12"
large: "col-6" large: "col-6"
- name: excludeDays - !include ../../fields/excludedDays.yml
type: string[]
meta:
label: Auszuschließende Wochentage
widget: selectArray
choices:
- id: monday
name: Montag
- id: tuesday
name: Dienstag
- id: wednesday
name: Mittwoch
- id: thursday
name: Donnerstag
- id: friday
name: Freitag
- id: saturday
name: Samstag
- id: sunday
name: Sonntag
- name: standardInputProperties - name: standardInputProperties
type: object
meta: meta:
widget: containerLessObject widget: containerLessObject
subFields: !include standardInputProperties.yml subFields: !include standardInputProperties.yml

View File

@ -27,17 +27,7 @@
small: "col-12" small: "col-12"
large: "col-6" large: "col-6"
- name: emailCC - !include ../../fields/emailCC.yml
type: string[]
meta:
label:
de: Email CC
en: Email CC
widget: string
useDefaultArray: true
helperText:
de: "Bsp: zyx@gmail.com"
en: "E.g.: zyx@gmail.com"
- name: emailIntroduction - name: emailIntroduction
type: string type: string
@ -49,10 +39,22 @@
de: "Bsp: Hallo xyz, sie haben eine neue Kaufanfrage erhalten!" de: "Bsp: Hallo xyz, sie haben eine neue Kaufanfrage erhalten!"
en: "E.g.: Hello xyz, you have received a new purchase request!" en: "E.g.: Hello xyz, you have received a new purchase request!"
- name: sendFormBtnText
type: string
meta:
label:
de: Formular Absenden Button Text
en: Form Submit Button Text
helperText:
de: "Bsp: Absenden"
en: "E.g.: Submit"
- name: rows - name: rows
type: object[] type: object[]
meta: meta:
label: Zeile label:
de: Zeile
en: Row
widget: grid widget: grid
addElementLabel: Zeile Hinzufügen addElementLabel: Zeile Hinzufügen
@ -60,7 +62,9 @@
- name: title - name: title
type: string type: string
meta: meta:
label: Zeilenname label:
de: Zeilenname
en: Row name
helperText: helperText:
de: "Sollte der Titel keinen Wert enthalten, wird kein Zeilenname angezeigt!" de: "Sollte der Titel keinen Wert enthalten, wird kein Zeilenname angezeigt!"
en: "If the title does not contain a value, no row name will be displayed!" en: "If the title does not contain a value, no row name will be displayed!"
@ -68,7 +72,9 @@
- name: emailTitle - name: emailTitle
type: string type: string
meta: meta:
label: Email Abschnitt Titel label:
de: Email Abschnitt Titel
en: Email section title
helperText: helperText:
de: "Sollte der Titel keinen Wert enthalten, wird kein Abschnitt Titel angezeigt!" de: "Sollte der Titel keinen Wert enthalten, wird kein Abschnitt Titel angezeigt!"
en: "If the title does not contain a value, no section title will be displayed!" en: "If the title does not contain a value, no section title will be displayed!"
@ -77,16 +83,38 @@
type: object[] type: object[]
meta: meta:
label: Spalte label:
addElementLabel: Spalte hinzufügen de: Spalte
en: Column
addElementLabel:
de: Spalte hinzufügen
en: Add column
widget: grid widget: grid
direction: horizontal direction: horizontal
subFields: subFields:
- name: title - name: title
type: string type: string
meta: meta:
label: Überschrift label:
helperText: Optional de: Überschrift
en: Headline
helperText:
de: Optional
en: Optional
- name: emailTitle
type: string
meta:
label:
de: Email Abschnitt Titel
en: Email section title
- name: annotation
type: string
meta:
label:
de: Zusatzinformation
en: Additional information
- name: inputWidgets - name: inputWidgets
type: string[] type: string[]
@ -96,33 +124,63 @@
en: Displayed input fields en: Displayed input fields
widget: selectArray widget: selectArray
choices: choices:
- name: Nummernblock - name:
de: Nummernblock
en: Number block
id: labelNumber id: labelNumber
- name: Zeitenauswahlfeld - name:
de: Zeitenauswahlfeld
en: Time selection field
id: times id: times
- name: Auswahlfeld - name:
de: Auswahlfeld
en: Selection field
id: select id: select
- name: Datumsauswahl - Standard Kalender - name:
de: Datumsauswahl - Standard Kalender
en: Date selection - Standard calendar
id: defaultCalendar id: defaultCalendar
- name: Datumauswahl - Custom Kalender - name:
de: Datumauswahl - Custom Kalender
en: Date selection - Custom calendar
id: customCalendar id: customCalendar
- name: Nummerfeld - name:
de: Nummerfeld
en: Number field
id: number id: number
- name: Checkbox Gruppe - name:
de: Checkbox Gruppe
en: Checkbox group
id: checkboxGroup id: checkboxGroup
- name: Mehrfachauswahl - name:
de: Mehrfachauswahl
en: Multiple selection
id: multiSelect id: multiSelect
- name: Textfeld - name:
de: Textfeld
en: Text field
id: text id: text
- name:
de: Zeitenauswahl
en: Time selection
id: timeSelect
- name:
de: Standardauswahl
en: Standard selection
id: standardSelect
- !include standardSelect.yml
- !include labelNumberInput.yml - !include labelNumberInput.yml
- !include timesInput.yml - !include timesInput.yml
- !include dateInput.yml - !include dateInput.yml
@ -131,3 +189,4 @@
- !include datePicker.yml - !include datePicker.yml
- !include multiSelectInput.yml - !include multiSelectInput.yml
- !include textInputs.yml - !include textInputs.yml
- !include timeSelect.yml

View File

@ -1,7 +1,9 @@
name: labelNumberInput name: labelNumberInput
type: object[] type: object[]
meta: meta:
label: Nummer block label:
de: Nummer block
en: Number block
dependsOn: dependsOn:
eval: $parent?.inputWidgets?.includes('labelNumber') eval: $parent?.inputWidgets?.includes('labelNumber')
helperText: helperText:
@ -12,7 +14,9 @@ subFields:
- name: group - name: group
type: number type: number
meta: meta:
label: Gruppe label:
de: Gruppe
en: Group
helpterText: helpterText:
de: Pflichtfeld seperierung. Aus einem Nummernblock muss mindestens eine Gruppe input haben. de: Pflichtfeld seperierung. Aus einem Nummernblock muss mindestens eine Gruppe input haben.
en: Mandatory field separation. A number block must have at least one group input. en: Mandatory field separation. A number block must have at least one group input.
@ -34,7 +38,9 @@ subFields:
- name: emailName - name: emailName
type: string type: string
meta: meta:
label: Email Name label:
de: Email Name
en: Email name
containerProps: containerProps:
layout: layout:
size: size:
@ -62,7 +68,9 @@ subFields:
- name: emailName - name: emailName
type: string type: string
meta: meta:
label: Email Name label:
de: Email Name
en: Email name
containerProps: containerProps:
layout: layout:
size: size:

View File

@ -8,6 +8,7 @@ meta:
eval: $parent?.inputWidgets?.includes('multiSelect') eval: $parent?.inputWidgets?.includes('multiSelect')
subFields: subFields:
- name: standardInputProperties - name: standardInputProperties
type: object
meta: meta:
widget: containerLessObject widget: containerLessObject
subFields: !include standardInputProperties.yml subFields: !include standardInputProperties.yml
@ -15,24 +16,32 @@ subFields:
- name: options - name: options
type: object[] type: object[]
meta: meta:
label: Mehrfachauswahl Optionen label:
de: Mehrfachauswahl Optionen
en: Multi Select Options
direction: row direction: row
widget: containerLessObjectArray widget: containerLessObjectArray
subFields: subFields:
- name: name - name: name
type: string type: string
meta: meta:
label: Name label:
de: Name
en: Name
- name: props - name: props
type: object type: object
meta: meta:
label: Mehrfachauswahl Eigenschaften label:
de: Mehrfachauswahl Eigenschaften
en: Multi Select Properties
subFields: subFields:
- name: additionalAddableValues - name: additionalAddableValues
type: boolean type: boolean
meta: meta:
label: Zusätzliche hinzufügbare Werte label:
de: Zusätzliche hinzufügbare Werte
en: Additional addable values
containerProps: containerProps:
layout: layout:
size: size:

View File

@ -8,6 +8,7 @@ meta:
eval: $parent?.inputWidgets?.includes('number') eval: $parent?.inputWidgets?.includes('number')
subFields: subFields:
- name: standardInputProperties - name: standardInputProperties
type: object
meta: meta:
widget: containerLessObject widget: containerLessObject
subFields: !include standardInputProperties.yml subFields: !include standardInputProperties.yml

View File

@ -1,7 +1,9 @@
- name: emailTitle - name: emailTitle
type: string type: string
meta: meta:
label: Email Titel label:
de: Email Titel
en: Email title
containerProps: containerProps:
layout: layout:
size: size:
@ -12,7 +14,9 @@
- name: placeholder - name: placeholder
type: string type: string
meta: meta:
label: Platzhalter label:
de: Platzhalter
en: Placeholder
containerProps: containerProps:
layout: layout:
size: size:
@ -23,7 +27,9 @@
- name: notRequired - name: notRequired
type: boolean type: boolean
meta: meta:
label: nicht Notwendig label:
de: nicht Notwendig
en: not required
containerProps: containerProps:
layout: layout:
size: size:
@ -34,7 +40,9 @@
- name: fieldOrder - name: fieldOrder
type: number type: number
meta: meta:
label: Reihenfolge label:
de: Reihenfolge
en: Order
helperText: helperText:
de: Die kleinste angegebene Zahl wird am weitesten oben in der Formularspalte stehen de: Die kleinste angegebene Zahl wird am weitesten oben in der Formularspalte stehen
en: The smallest specified number will be at the top of the form column. en: The smallest specified number will be at the top of the form column.
@ -44,8 +52,20 @@
default: "col-6" default: "col-6"
small: "col-12" small: "col-12"
large: "col-6" large: "col-6"
- name: textTitle - name: textTitle
type: string type: string
meta: meta:
label: Text Titel label:
helperText: Alternative zu textPlaceholder de: Text Titel
en: Text title
helperText:
de: Alternative zu textPlaceholder, steht dann über dem Inputfeld
en: Alternative to textPlaceholder, then stands above the input field
- name: groupTitle
type: string
meta:
label:
de: Gruppe Titel
en: Group title

View File

@ -0,0 +1,58 @@
name: standardSelect
type: object
meta:
label: Standardauswahl
dependsOn:
eval: $parent?.inputWidgets?.includes('standardSelect')
subFields:
- name: standardInputProperties
type: object
meta:
widget: containerLessObject
subFields: !include standardInputProperties.yml
- name: selectEntries
type: object[]
meta:
label:
de: Auswahleingabe Möglichkeiten
en: Selection input options
widget: containerLessObjectArray
addElementLabel: Auswahleingabe hinzufügen
direction: horizontal
subFields:
- name: shownValue
type: string
meta:
label:
de: Angezeigter Wert
en: Displayed value
containerProps:
layout:
size:
default: "col-6"
small: "col-12"
large: "col-6"
- name: value
type: string
meta:
label:
de: per Email gesendeter Wert
en: Value sent by email
containerProps:
layout:
size:
default: "col-6"
small: "col-12"
large: "col-6"
- name: defaultValue
type: boolean
meta:
label:
de: Standardwert
en: Default value
helperText:
de: "Wird dieser Wert ausgewählt, wird er als Standardwert gesetzt"
en: "If this value is selected, it will be set as the default value"

View File

@ -7,6 +7,7 @@ meta:
eval: $parent?.inputWidgets?.includes('text') eval: $parent?.inputWidgets?.includes('text')
subFields: subFields:
- name: standardInputProperties - name: standardInputProperties
type: object
meta: meta:
widget: containerLessObject widget: containerLessObject
subFields: !include standardInputProperties.yml subFields: !include standardInputProperties.yml
@ -14,7 +15,9 @@ subFields:
- name: textArea - name: textArea
type: boolean type: boolean
meta: meta:
label: Großes Textfeld label:
de: Großes Textfeld
en: Large text field
containerProps: containerProps:
layout: layout:
size: size:
@ -25,7 +28,9 @@ subFields:
- name: emailValidation - name: emailValidation
type: boolean type: boolean
meta: meta:
label: E-Mail-Validierung label:
de: E-Mail-Validierung
en: E-mail validation
containerProps: containerProps:
layout: layout:
size: size:
@ -36,7 +41,9 @@ subFields:
- name: telValidation - name: telValidation
type: boolean type: boolean
meta: meta:
label: Telefon-Validierung label:
de: Telefon-Validierung
en: Phone validation
containerProps: containerProps:
layout: layout:
size: size:

View File

@ -0,0 +1,45 @@
name: timeSelect
type: object
meta:
label: Zeitenwahl
dependsOn:
eval: $parent?.inputWidgets?.includes('timeSelect')
subFields:
- name: standardInputProperties
type: object
meta:
widget: containerLessObject
subFields: !include standardInputProperties.yml
- name: selectEntries
type: object[]
meta:
label: Auswahleingabe
widget: containerLessObjectArray
helperText: "Die Angaben werden in folgendes Übersetzt: Anfangspunkt - Endpunkt"
direction: horizontal
subFields:
- name: leftSide
type: string
meta:
label: Anfangspunkt
helperText: Bspw. 14:30
containerProps:
layout:
size:
default: "col-6"
small: "col-12"
large: "col-6"
- name: rightSide
type: string
meta:
label: Endpunkt
helperText: Bspw. 15:30
containerProps:
layout:
size:
default: "col-6"
small: "col-12"
large: "col-6"

View File

@ -14,8 +14,12 @@ subFields:
- name: times - name: times
type: object[] type: object[]
meta: meta:
label: Zeitenangabe label:
helperText: "Die Angaben werden in folgendes Übersetzt: Anfangspunkt - Endpunkt" de: Zeitenauswahl Möglichkeiten
en: Time selection options
helperText:
de: "Die Angaben werden in folgendes Übersetzt: Anfangspunkt - Endpunkt"
en: "The information is translated into the following: starting point - end point"
direction: horizontal direction: horizontal
widget: containerLessObjectArray widget: containerLessObjectArray
@ -23,8 +27,12 @@ subFields:
- name: from - name: from
type: string type: string
meta: meta:
label: Anfangspunkt label:
helperText: Bspw. 14:30 de: Anfangspunkt
en: Starting point
helperText:
de: Bspw. 14:30
en: E.g. 14:30
containerProps: containerProps:
layout: layout:
size: size:
@ -36,7 +44,9 @@ subFields:
type: string type: string
meta: meta:
label: Endpunkt label: Endpunkt
helperText: Bspw. 15:30 helperText:
de: Bspw. 15:30
en: E.g. 15:30
containerProps: containerProps:
layout: layout:
size: size:
@ -44,6 +54,7 @@ subFields:
small: "col-12" small: "col-12"
large: "col-6" large: "col-6"
- name: standardInputProperties - name: standardInputProperties
type: object
meta: meta:
widget: containerLessObject widget: containerLessObject
subFields: !include standardInputProperties.yml subFields: !include standardInputProperties.yml

View File

@ -1,7 +1,9 @@
- name: title - name: title
type: string type: string
meta: meta:
label: Titel label:
de: Titel
en: Title
containerProps: containerProps:
layout: layout:
size: size:
@ -12,7 +14,11 @@
- name: description - name: description
type: string type: string
meta: meta:
label: Beschreibung label:
de: Beschreibung
en: Description
inputProps:
multiline: true
containerProps: containerProps:
layout: layout:
size: size:
@ -22,4 +28,8 @@
- name: keywords - name: keywords
type: string type: string
meta: meta:
label: Schlüsselwörter inputProps:
multiline: true
label:
de: Schlüsselwörter
en: Keywords

View File

@ -1,9 +1,30 @@
# attribute auf row ebene - name: title
type: string
meta:
label:
de: Titel
en: Title
- name: backgroundImage
type: string
meta:
label:
de: Hintergrundbild
en: Background Image
widget: foreignKey
foreign:
collection: medialib
id: id
subNavigation: 0
render:
defaultCollectionViews: true
- name: columns - name: columns
type: object[] type: object[]
meta: meta:
label: Spalten label:
de: Spalten
en: Columns
direction: row direction: row
widget: grid widget: grid
subFields: !include ../fieldLists/column.yml subFields: !include ./column.yml

View File

@ -0,0 +1,16 @@
type: boolean
name: active
meta:
label:
de: Aktiv
en: Active
helperText:
de: Ist dies Aktiviert, so wird der Inhalt verfügbar.
en: If this is activated, the content will be available.
defaultValue: true
containerProps:
layout:
size:
default: "col-6"
small: "col-12"
large: "col-6"

View File

@ -0,0 +1,3 @@
backup:
active: true # sollen backups beim verändern erstellt werden
collectionName: backups # in welcher collection es gespeichert werden soll

View File

@ -0,0 +1,19 @@
type: string
name: type
meta:
label:
de: Inhaltstyp
en: Content Type
widget: select
defaultValue: page
containerProps:
layout:
size:
default: "col-6"
small: "col-12"
large: "col-6"
choices:
- name: # name der im admin angezeigt wird
de: Seite
en: Page
id: page # wert der in der datenbank gespeichert wird

View File

@ -0,0 +1,11 @@
name: emailCC
type: string[]
meta:
label:
de: Email CC
en: Email CC
widget: string
useDefaultArray: true
helperText:
de: "Bsp: zyx@gmail.com"
en: "E.g.: zyx@gmail.com"

View File

@ -0,0 +1,40 @@
name: excludeDays
type: string[]
meta:
label: Auszuschließende Wochentage
widget: checkboxArray
choices:
- id: monday
name:
de: Montag
en: Monday
- id: tuesday
name:
de: Dienstag
en: Tuesday
- id: wednesday
name:
de: Mittwoch
en: Wednesday
- id: thursday
name:
de: Donnerstag
en: Thursday
- id: friday
name:
de: Freitag
en: Friday
- id: saturday
name:
de: Samstag
en: Saturday
- id: sunday
name:
de: Sonntag
en: Sunday

View File

@ -0,0 +1,6 @@
name: file
type: file
meta:
label:
de: Datei
en: File

View File

@ -0,0 +1,7 @@
type: object
name: formular
meta:
label:
de: Formular
en: Form
widget: jsonField

View File

@ -0,0 +1,13 @@
name: from
type: date
meta:
label:
de: Von
en: From
widget: date
containerProps:
layout:
size:
default: "col-6"
small: "col-12"
large: "col-6"

View File

@ -0,0 +1,37 @@
# Der Key des Objektes definiert den Namen des Filters.
# Jeder Filter ist eine Liste von Bildmanipulationen, die
# nacheinander angewandt werden.
# Die manipulierten Bilder werden gecachet. Ein nachträgliches
# Anpassen der Filter erfordert also das Löschen der gecachten
# Dateien welche sich jeweils neben den original Bilddateien
# im "uploadPath" der Kollektion befinden.
xs:
- fit: true
height: 90
width: 90
resampling: lanczos
quality: 60
s:
- fit: true
height: 300
width: 300
resampling: lanczos
quality: 60
m:
- fit: true
height: 600
width: 600
resampling: lanczos
quality: 60
l:
- fit: true
height: 1200
width: 1200
resampling: lanczos
quality: 60
xl:
- fit: true
height: 2000
width: 2000
resampling: lanczos
quality: 60

View File

@ -0,0 +1,25 @@
name: images
type: string[]
meta:
label:
de: Bild
en: Image
dependsOn:
eval: $parent.contentType == 'images'
widget: foreignKey # Verwendetes Widget.
foreign:
collection: medialib # Name der Sammlung, in der die ausgewählten Daten gespeichert sind.
id: id # Feldname, das als eindeutige Kennung für die ausgewählten Daten verwendet wird.
subNavigation: 0 # Bestimmt, welche Navigation für die Auswahl der ausgewählten Daten angezeigt wird.
#projection: xyz
#sort: "title"
render:
defaultCollectionViews: true
# alternativ könnte auch folgendes verwendet werden:
#raw: true
#eval: |
# (function() {
# var out = "";
# out += "<div style=\"color: #999;\">" + $foreignEntry.title + "</div>";
# return out;
# })()

View File

@ -0,0 +1,11 @@
name: sort
type: number
meta:
label:
de: Manuelle Sortierung
en: Manual Sorting
inputProps:
{ readonly: true, placeholder: { de: "Wert wird automatisch gesetzt", en: "Value is set automatically" } }
helperText:
de: Dieses Feld wird für die manuelle Sortierung benötigt. Sobald ein Eintrag per Drag&Drop verschoben wurde, wird die neue Position innerhalb der Liste eingetragen.
en: This field is required for manual sorting. As soon as an entry is moved using Drag&Drop, the new position is entered in the list.

View File

@ -0,0 +1,27 @@
type: cardList
mediaQuery: "(min-width: 1200px)"
selectionPriority: 1
fileDropArea:
label:
{
de: "Ziehen Sie Dateien per Drag and Drop hierher oder klicken Sie, um Dateien auszuwählen.",
en: "Drag and drop some files here, or click to upload.",
}
helperText: { de: "Maximale Uploadgröße: 1,54 MB", en: "Maximum upload size: 1.54MB" }
targetField: file
pageAsDropArea: false
fields:
- source: file
name: Datei
- source: category
name:
de: Kategorie
en: Category
filter: true
- source: updateTime
type: datetime
filter: true
label:
de: letztes Update
en: last update

View File

@ -0,0 +1,25 @@
type: simpleList
mediaQuery: "(min-width: 0px)"
defaultSelect: false
selectionPriority: 3 #gibt an, wenn mediaQuery passt, mit welcher priorität es default mäßig ausgewählt sein soll, je niedriger, desto wichtiger
fileDropArea:
label:
{
de: "Ziehen Sie Dateien per Drag and Drop hierher oder klicken Sie, um Dateien auszuwählen.",
en: "Drag and drop some files here, or click to upload.",
}
helperText: { de: "Maximale Uploadgröße: 1,54 MB", en: "Maximum upload size: 1.54MB" }
targetField: file # gibt an, in welches feld die datei hochgeladen werden soll
pageAsDropArea: false # wenn true, wird filedrop area garnicht erst angezeigt, da die ganze seite drag n drop funktionalität hat
primaryText:
source: file
filter: true
secondaryText:
source: alt
filter: true
tertiaryText:
source: category
filter: true

View File

@ -0,0 +1,30 @@
type: table
mediaQuery: "(min-width: 800px)"
defaultSelect: false
selectionPriority: 2
fileDropArea:
label:
{
de: "Ziehen Sie Dateien per Drag and Drop hierher oder klicken Sie, um Dateien auszuwählen.",
en: "Drag and drop some files here, or click to upload.",
}
helperText: { de: "Maximale Uploadgröße: 1,54 MB", en: "Maximum upload size: 1.54MB" }
targetField: file
pageAsDropArea: false
columns:
- source: file
name:
de: Datei
en: File
filter: true
- source: category
name:
de: Kategorie
en: Category
filter: true
- source: updateTime
type: datetime
label:
de: letztes Update
en: last update
filter: true

View File

@ -0,0 +1,10 @@
multiupload:
fields:
- source: category # gibt an, welche Felder in der Modalansicht bearbeitet werden können, wenn die Eigenschaft nicht festgelegt ist, werden automatisch alle Felder ausgewählt
prefilledFields: # gibt an, welche Felder in der Modalansicht vorbefüllt werden können, wenn die Eigenschaft nicht festgelegt ist, werden automatisch alle Felder ausgewählt
- source: title
defaultValue: # extrahiert den namen des files und fügt ihn in das feld "title" ein
eval: |
(function(){
return "Title" + $file.name
})()

View File

@ -0,0 +1,23 @@
name: pageTitle
type: string
meta:
label:
de: Titel der Seite
en: Page Title
helperText:
de: "Dieser Titel wird in der Seite als h1 angezeigt."
en: "This title is displayed in the page as h1."
dependsOn:
eval: $.type == "page"
inputProps:
#multiline: true
#placeholder: Seitentitel
#...
#openapi:
# example: Demo Titel
containerProps:
layout:
size:
default: "col-6"
small: "col-12"
large: "col-6"

View File

@ -0,0 +1,6 @@
quickEdit: # Erlaubt das Bearbeiten von Einträgen in der Listenansicht, ohne dass eine neue Seite geöffnet wird
enabled: true
fields: # felder, die in dieser Ansicht bearbeitet werden können
- title
- alt
- file

View File

@ -0,0 +1,9 @@
name: rows
type: object[]
meta:
label:
de: Zeilen
en: Rows
widget: grid
metaElements: []
subFields: !include ../fieldLists/row.yml

View File

@ -0,0 +1,9 @@
name: text
type: string
meta:
widget: richtext
label:
de: Text
en: text
dependsOn:
eval: $parent.contentType == 'text'

View File

@ -0,0 +1,6 @@
name: fulltextindex # Ein eindeutiger Name für den Index. Es ist optional, wird jedoch empfohlen, um den Index später leicht identifizieren zu können.
key: # Bestimmt, auf welche Felder der Index angewendet werden soll. Dies kann ein einfacher String sein, wenn der Index nur ein Feld umfasst, oder ein Array von Strings, wenn der Index mehrere Felder umfasst.
- $text:$** # definiert einen Volltextindex über alle Felder. Der spezielle Operator $text wird verwendet, um einen Volltextindex zu erstellen, und der Operator $\*\* bezeichnet alle Felder in der Sammlung.
background: true
unique: false # Wenn auf true gesetzt, erzwingt dies, dass der Index eindeutige Werte enthält. Wenn Sie versuchen, einen Eintrag mit einem bereits indizierten Wert hinzuzufügen, wird ein Fehler ausgelöst.
defaultLanguage: german # Wird verwendet, um die Sprache für Textindizes festzulegen. Dies ist wichtig für die Volltextsuche, da verschiedene Sprachen unterschiedliche Tokenisierungs- und Stemmungsregeln haben.

View File

@ -35,8 +35,4 @@ hooks:
file: hooks/forms/post_return.js file: hooks/forms/post_return.js
fields: fields:
- type: object - !include fields/form.yml
name: formular
meta:
label: Formular
widget: jsonField

View File

@ -46,11 +46,16 @@ hooks:
type: javascript type: javascript
file: hooks/lighthouse/post_create.js file: hooks/lighthouse/post_create.js
indexes:
- !include fields/textIndex.yml
fields: fields:
- name: analyzedPaths - name: analyzedPaths
type: string[] type: string[]
meta: meta:
label: Analyzed Paths label:
de: Analysierten Pfade
en: Analyzed Paths
- name: performance - name: performance
type: number type: number
meta: meta:
@ -58,15 +63,21 @@ fields:
- name: accessibility - name: accessibility
type: number type: number
meta: meta:
label: Accessibility label:
en: Accessibility
de: Zugänglichkeit
- name: bestPractices - name: bestPractices
type: number type: number
meta: meta:
label: Best Practices label:
en: Best Practices
de: Beste Praktiken
- name: seo - name: seo
type: number type: number
meta: meta:
label: SEO label:
en: SEO
de: SEO
- name: lighthouseMetrics - name: lighthouseMetrics
type: object type: object
meta: meta:
@ -75,45 +86,67 @@ fields:
- name: FCPS - name: FCPS
type: number type: number
meta: meta:
label: First Contentful Paint Score label:
en: First Contentful Paint Score
de: Erster Inhaltlicher Anstrich Score
- name: FCPV - name: FCPV
type: number type: number
meta: meta:
label: First Contentful Paint Value label:
en: First Contentful Paint Value
de: Erster Inhaltlicher Anstrich Wert
- name: FMPV - name: FMPV
type: number type: number
meta: meta:
label: First Meaningful Paint Value label:
de: Erster Bedeutungsvoller Anstrich Wert
en: First Meaningful Paint Value
- name: FMPS - name: FMPS
type: number type: number
meta: meta:
label: First Meaningful Paint Score label:
en: First Meaningful Paint Score
de: Erster Bedeutungsvoller Anstrich Score
- name: SIS - name: SIS
type: number type: number
meta: meta:
label: Speed Index Score label:
en: Speed Index Score
de: Geschwindigkeitsindex Score
- name: SIV - name: SIV
type: number type: number
meta: meta:
label: Speed Index Value label:
en: Speed Index Value
de: Geschwindigkeitsindex Wert
- name: TTIS - name: TTIS
type: number type: number
meta: meta:
label: Time to Interactive Score label:
en: Time to Interactive Score
de: Zeit bis zur Interaktion Score
- name: TTIV - name: TTIV
type: number type: number
meta: meta:
label: Time to Interactive Value label:
en: Time to Interactive Value
de: Zeit bis zur Interaktion Wert
- name: FPIDS - name: FPIDS
type: number type: number
meta: meta:
label: First Potential Input Delay Score label:
de: Erste potenzielle Eingabe Verzögerung Score
en: First Potential Input Delay Score
- name: FPIDV - name: FPIDV
type: number type: number
meta: meta:
label: First Potential Input Delay Value label:
en: First Potential Input Delay Value
de: Erste potenzielle Eingabe Verzögerung Wert

View File

@ -26,4 +26,6 @@ fields:
- type: string - type: string
name: lighthouseSubpath name: lighthouseSubpath
meta: meta:
label: PagespeedPaths label:
de: Zu analysierenden Pfad
en: Path to analyze

View File

@ -1,25 +1,29 @@
# Der Name der Kollektion ist beliebig, aber wird in unserem # Der Name der Kollektion wird in der Rest-API-URL verwendet, z.B.
# Beispiel vom ContentBuilder als "medialib" referenziert. # /_/tibi_starter/medialib
name: medialib name: medialib
# Enthält die Kollektion Felder vom Typ "file", so werden die
# hochgeladenen Dateien unter dem Ordner abgelegt, der mit
# "uploadPath" bestimmt wird.
uploadPath: ../media/medialib uploadPath: ../media/medialib
meta: meta:
allowExportAll: true allowExportAll: true # Erlaubt das Exportieren aller Einträge in dieser Sammlung - Nur für Admins
label: label: # label der Collection in der Sidebar
de: Medienbibliothek de: Medienbibliothek
en: Media Library en: Media Library
muiIcon: multimedia muiIcon: multimedia # icon in navigation
defaultSort: defaultSort:
field: sort field: sort
order: MANUALLY order: MANUALLY #alternativ auch ASC und DESC möglich
backup: backup:
active: true active: true # sollen backups beim verändern erstellt werden
collectionName: backups collectionName: backups # in welcher collection es gespeichert werden soll
quickEdit: quickEdit: # Erlaubt das Bearbeiten von Einträgen in der Listenansicht, ohne dass eine neue Seite geöffnet wird
enabled: true enabled: true
fields: fields: # felder, die in dieser Ansicht bearbeitet werden können
- title - title
- alt - alt
- file - file
@ -31,48 +35,20 @@ meta:
defaultImageFilter: xs defaultImageFilter: xs
multiupload: multiupload:
fields: [] fields:
- source: category # gibt an, welche Felder in der Modalansicht bearbeitet werden können, wenn die Eigenschaft nicht festgelegt ist, werden automatisch alle Felder ausgewählt
prefilledFields: # gibt an, welche Felder in der Modalansicht vorbefüllt werden können, wenn die Eigenschaft nicht festgelegt ist, werden automatisch alle Felder ausgewählt
- source: title
defaultValue: # extrahiert den namen des files und fügt ihn in das feld "title" ein
eval: |
(function(){
return "Title" + $file.name
})()
views: views:
- type: table - !include fields/medialibSimpleList.yml
mediaQuery: "(min-width: 0px)" - !include fields/medialibTable.yml
defaultSelect: false - !include fields/medialibCardList.yml
selectionPriority: 2
fileDropArea:
label:
{
de: "Ziehen Sie Dateien per Drag and Drop hierher oder klicken Sie, um Dateien auszuwählen.",
en: "Drag and drop some files here, or click to upload.",
}
helperText: { de: "Maximale Uploadgröße: 1,54 MB", en: "Maximum upload size: 1.54MB" }
targetField: file
pageAsDropArea: false
columns:
- source: file
name: Datei
- source: updateTime
type: datetime
label: letztes Update
- type: cardList
mediaQuery: "(min-width: 1200px)"
selectionPriority: 1
fileDropArea:
label:
{
de: "Ziehen Sie Dateien per Drag and Drop hierher oder klicken Sie, um Dateien auszuwählen.",
en: "Drag and drop some files here, or click to upload.",
}
helperText: { de: "Maximale Uploadgröße: 1,54 MB", en: "Maximum upload size: 1.54MB" }
targetField: file
pageAsDropArea: false
fields:
- source: file
name: Datei
- source: updateTime
type: datetime
label: letztes Update
subNavigation: subNavigation:
- name: modalForeign # Name des Eingabefelds oder der Ansicht. - name: modalForeign # Name des Eingabefelds oder der Ansicht.
@ -93,71 +69,168 @@ meta:
} }
//!js //!js
# Allgeine Zugriffsregeln auf Kollektions-Ebene werden mit dem
# "permissions" Objekt festgelegt.
permissions: permissions:
# Unter "public" werden die Zugriffsrechte für die Öffentlichkeit
# definiert.
public: public:
# "methods" führt die HTTP-Methoden auf, die erlaubt sind
methods: methods:
# "get: true" bedeutet hier, dass jeder die Daten lesen darf
get: true get: true
# "post", also Einträge erstellen, "put" = Bearbeiten und
# "delete" = löschen darf die Öffentlichkeit nicht.
post: false post: false
put: false put: false
delete: false delete: false
# Ist "validProjections" definiert, sind auch nur genau die
# aufgelisteten Projektionen erlaubt, welche zwingend mit dem
# GET-Parameter "projection=..." ausgewählt werden müssen.
validProjections:
- list
- details
# Der Key "user" steht für ALLE Benutzer die dem Projekt
# zugeordnet sind.
# D.h. eine feinere Abstufung auf Benutzerebene ist mit dem
# Key "user" allein nicht möglich.
# Für eine feinere Abstufung können nachgelagerte Hooks
# dienen oder die Verwendung von zugeordneten benutzerdefinierten
# "permissions" (siehe meta Objekt).
user: user:
methods: methods:
get: true get: true
post: true post: true
put: true put: true
delete: true delete: true
# Fehlt "validProjections", sind automatisch alle Projektionen
# erlaubt, wobei hier auch der GET-Parameter "projection="
# weggelassen werden darf und somit alle Felder in der Ausgabe
# zu finden sind.
# Folgende Brechtigung wird angewandt, wenn der Zugriff über
# den GET-Parameter "token=" oder die Header-Anweisung "token: "
# angefragt wird.
# "token" ist dabei die Markierung, dass es sich um einen Token
# handelt und "${TOKEN}" ist der benutzerdefinierte Token selbst.
# Dieser wird hier über eine Umgebungsvariable "TOKEN" injiziert,
# die in "config.yml.env" definiert werden kann mit "TOKEN=...".
token:${TOKEN}:
methods:
get: true
post: true
put: true
delete: true
# Alle Berechtigungs-Namen, die nicht "public", "user" oder "token:..."
# heißen, sind benutzerdefinierte Berechtigungen, die Benutzern
# zugeordnet werden können.
# Eine mögliche Auflistung um Vorschläge im tibi-admin anzubieten,
# werden im Top-Level meta-Objekt der "config.yml" unter "permissions"
# definiert.
pages:
methods:
get: true
post: true
put: true
delete: true
# Projektionen der Daten werden via GET-Parameter "projection=..."
# referenziert.
# "projections" is ein Objekt, dass die Namen der Projektionen
# als Key führt.
projections: projections:
# dashboard = name der Projektion
dashboard: dashboard:
# "select" definiert als Keys die Felder, die beim Abruf
# dieser Projektion in den Ausgabe-Daten enthalten sind.
# Felder werden über die Punkt-Notation referenziert.
select: select:
# keine auflistung von feldern, da alle felder ausgewählt werden
details:
# Alternativ kann "select" auch Auschlussregeln definieren.
# Eine Mischung von Inkludieren und Auschluss ist NICHT
# möglich.
select:
file: 0
list:
select:
file: 1
alt: 1
hooks: hooks:
# Hooks für die Methode "get"
#get:
# "read"-Schritt wird ausgeführt, bevor die Daten von der Datenbank
# gelesen werden.
#read:
#"type" ist derzeit immer "javascript"
# type: javascript
#"file" zeigt auf die Datei mit dem Javascript-Code relativ zum
#Ordner der "config.yml" Datei.
# file: hooks/democol/get_read.js
#"return"-Schritt wird ausgeführt, bevor die gelesenen Daten über
#HTTP übertragen werden.
#return:
# type: javascript
# file: hooks/democol/get_return.js
# Hooks für die Methode "post"
post: post:
# "bind" wird ausgeführt, bevor die übertragenen Daten in eine
# Objekt-Struktur umgewandelt werden.
# Der tibi-server erwarten nach diesem Schritt gültige JSON-Daten,
# d.h. sollte es möglich gemacht werden, dass andere Daten übertragen
# werden, sind diese in diesem Hook abzufangen und zu verarbeiten.
#bind:
# type: javascript
# file: hooks/democol/post_bind.js
# "validate" wird ausgeführt, bevor die Daten validiert werden.
#validate:
# type: javascript
# file: hooks/democol/post_validate.js
# "create" wird ausgeführt, bevor das Objekt/Dokument in der Datenbank
# angelegt wird.
#create:
# type: javascript
# file: hooks/democol/post_create.js
# "return" wird ausgeführt, bevor die Serverantwort über HTTP
# übertragen wird.
return: return:
type: javascript type: javascript
file: hooks/clear_cache.js file: hooks/clear_cache.js
put: put:
#bind:
# type: javascript
# file: hooks/democol/put_bind.js
#validate:
# type: javascript
# file: hooks/democol/put_validate.js
# "bind" und "validate" habe die gleiche Bedeutung wie Hooks der
# Methode "post".
# "update" wird ausgeführt bevor das Objekt in der Datenbank
# aktualisiert wird.
#update:
# type: javascript
# file: hooks/democol/put_update.js
# "return" wird auch hier vor der Serverantwort ausgeführt.
return: return:
type: javascript type: javascript
file: hooks/clear_cache.js file: hooks/clear_cache.js
imageFilter: imageFilter: !include fields/imageFilter.yml
xs:
- fit: true # "fields" stellen die Eigentliche Struktur der Kollektion dar.
height: 90 # "fields" ist als Array angelegt um eine Standard-Sortierung
width: 90 # im tibi-admin vorzugeben.
resampling: lanczos
quality: 60
s:
- fit: true
height: 300
width: 300
resampling: lanczos
quality: 60
m:
- fit: true
height: 600
width: 600
resampling: lanczos
quality: 60
l:
- fit: true
height: 1200
width: 1200
resampling: lanczos
quality: 60
xl:
- fit: true
height: 2000
width: 2000
resampling: lanczos
quality: 60
fields: fields:
- name: file # Das Einbinden von Feldern über extra Dateien bietet sich nur
type: file # an, wenn das jeweilige Feld mehrfach von dieser oder anderen
meta: # Kollektionen verwendet wird.
label: # Auf die möglichen Definitionen wird im Kapitel "fields"
de: Datei # eingegangen.
en: File - !include fields/file.yml
- name: alt - name: alt
type: string type: string
@ -179,14 +252,11 @@ fields:
de: Der Titel wird angezeigt, wenn die Datei geladen wird. de: Der Titel wird angezeigt, wenn die Datei geladen wird.
en: The title is displayed when the file is loaded. en: The title is displayed when the file is loaded.
- name: sort - name: category
type: number type: string
meta: meta:
label: label:
de: Manuelle Sortierung de: Kategorie
en: Manual Sorting en: Category
inputProps:
{ readonly: true, placeholder: { de: "Wert wird automatisch gesetzt", en: "Value is set automatically" } } - !include fields/manualSort.yml
helperText:
de: Dieses Feld wird für die manuelle Sortierung benötigt. Sobald ein Eintrag per Drag&Drop verschoben wurde, wird die neue Position innerhalb der Liste eingetragen.
en: This field is required for manual sorting. As soon as an entry is moved using Drag&Drop, the new position is entered in the list.

View File

@ -87,8 +87,12 @@ fields:
- name: type - name: type
type: string type: string
meta: meta:
label: Modultyp label:
helperText: "Wählen Sie den Typ des Moduls aus." de: Modultyp
en: Module Type
helperText:
de: "Wählen Sie den Typ des Moduls aus."
en: "Choose the type of the module."
widget: select widget: select
choices: choices:
- name: { de: "Formular", en: "Form" } - name: { de: "Formular", en: "Form" }

View File

@ -4,10 +4,6 @@ uploadPath: ../media/navigation
meta: meta:
label: "Navigation" label: "Navigation"
muiIcon: navigation muiIcon: navigation
allowExportAll: true
backup:
active: true
collectionName: backups
views: views:
- type: simpleList - type: simpleList
mediaQuery: "(max-width:599px)" mediaQuery: "(max-width:599px)"
@ -17,7 +13,6 @@ meta:
mediaQuery: "(min-width:600px)" mediaQuery: "(min-width:600px)"
columns: columns:
- source: tree - source: tree
name: Navigationsbaum
permissions: permissions:
public: public:
@ -32,15 +27,60 @@ permissions:
post: false post: false
put: true put: true
delete: false delete: false
hooks:
post: x-endpoint: &endpoint
return: name: endpoint
type: javascript type: boolean
file: hooks/clear_cache.js meta:
put: defaultValue:
return: eval: 1 == 1
type: javascript label: Endpunkt
file: hooks/clear_cache.js
x-elemente: &elemente
name: elements
type: object[]
meta:
label:
de: Elemente
en: elements
folding:
previewUnfolded: name
previewFolded: name
x-name: &name
name: name
type: string
meta:
label:
de: Name
en: name
helperText:
de: Dieser Name wird zur Anzeige in der Navigation verwendet.
en: This name is used for display in the navigation.
x-page: &seite
name: page
type: string
meta:
label:
de: Seite
en: page
widget: select
dependsOn:
eval: |
(function(){
if($parent.endpoint == undefined) return true;
return $parent.endpoint
})()
choices:
endpoint: content
params:
sort: path
projection: navigation
mapping:
id: id
name: path
fields: fields:
- name: tree - name: tree
@ -48,7 +88,9 @@ fields:
meta: meta:
label: Baum label: Baum
widget: select widget: select
helperText: Die Servicenavigation sollte Seiten wie bspw. die Datneschutzerklärung oder das Impressum umfassen. helperText:
de: Die Servicenavigation sollte Seiten wie bspw. die Datneschutzerklärung oder das Impressum umfassen.
en: The service navigation should include pages such as the data protection declaration or the imprint.
choices: choices:
- id: 0 - id: 0
name: name:
@ -59,38 +101,37 @@ fields:
de: Servicenavigation de: Servicenavigation
en: service navigation en: service navigation
- name: pages - <<: *elemente
type: object[]
meta:
label:
de: Seiten
en: pages
folding:
previewUnfolded: name
previewFolded: name
widget: containerLessObjectArray
subFields: subFields:
- name: name - *endpoint
type: string - *name
- *seite
- name: image
type: file
meta: meta:
label: label: Bild
de: Name dependsOn:
en: name eval: |
helperText: Dieser Name wird zur Anzeige in der Navigation verwendet. (function(){
if($parent.endpoint == undefined) return false;
return !$parent.endpoint
})()
- name: page - name: elements
type: string type: object[]
meta: meta:
label: label:
de: Seite de: Elemente
en: page en: elements
widget: select dependsOn:
choices: eval: |
endpoint: page (function(){
params: if($parent.endpoint == undefined) return false;
sort: path return !$parent.endpoint
projection: navigation })()
mapping: folding:
id: id previewUnfolded: name
name: path previewFolded: name
subFields:
- *name
- *seite

View File

@ -8,6 +8,12 @@ meta:
dashboard: helper/dashboard.yml dashboard: helper/dashboard.yml
# Liste möglicher Berechtigungen, die Benutzern zugeordnet werden können
permissions:
- name: pages
label:
de: Seiten
en: Pages
collections: collections:
- !include collections/backups.yml - !include collections/backups.yml
- !include collections/content.yml - !include collections/content.yml
@ -20,6 +26,7 @@ collections:
- !include collections/ssr.yml - !include collections/ssr.yml
jobs: jobs:
- cron: "0 0 * * 1" - !include jobs/lighthouse.yml
type: javascript
file: jobs/lighthouse.js assets:
- !include assets/dist.yml

View File

@ -4,6 +4,13 @@
backup.updateLogs = updateLogs backup.updateLogs = updateLogs
return { data: backup } return { data: backup }
/**
*
* @param {Object} entry
* @param {string} collectionName
* @param {number} versionNr
* @returns
*/
function compareAndUpdateEntry(entry, collectionName, versionNr) { function compareAndUpdateEntry(entry, collectionName, versionNr) {
let updateLogs let updateLogs
if (versionNr == 0) { if (versionNr == 0) {
@ -25,6 +32,11 @@
return updateLogs return updateLogs
} }
/**
*
* @param {any[]} array
* @returns {any[]}
*/
function filterValidObjects(array) { function filterValidObjects(array) {
return array.filter((object) => { return array.filter((object) => {
for (let key in object) { for (let key in object) {
@ -35,6 +47,14 @@
return true return true
}) })
} }
/**
*
* @param {Object} oldObj
* @param {Object} newObj
* @param {string} path
* @returns {any[]}
*/
function getUpdateLogs(oldObj = {}, newObj = {}, path = "") { function getUpdateLogs(oldObj = {}, newObj = {}, path = "") {
let updateLogs = [] let updateLogs = []
const ignoredKeys = ["id", "insertTime", "updateTime"] const ignoredKeys = ["id", "insertTime", "updateTime"]

View File

@ -8,7 +8,6 @@ var { setUpQuery, calculateAverageDynamically, run } = require("../hooks/lib/uti
} }
let dbObjs = [] let dbObjs = []
urls.forEach((url) => { urls.forEach((url) => {
console.log("URL:", url)
dbObjs.push(run(url)) dbObjs.push(run(url))
}) })
let dbObject = calculateAverageDynamically(dbObjs) let dbObject = calculateAverageDynamically(dbObjs)

14
api/jobs/lighthouse.yml Normal file
View File

@ -0,0 +1,14 @@
# Jedem Job muss ein "cron" Eintrag zugeordnet werden, der die
# Ausführungszeitpunkte definiert.
# Das cron-Schema ist dem üblichen Linux cron-Schema nachempfunden.
cron: "0 0 * * 1"
# "type" des Jobs ist derzeit immer "javascript" mit der "file"-Referenz
# relativ zur "config.yml".
type: javascript
file: jobs/lighthouse.js
# Es können beliebige "meta"-Daten hinterlegt werden, die im Javascript
# des Jobs über "context.job.meta" abgerufen werden können.
meta:
name: Lighthouse job

View File

@ -16,6 +16,15 @@
<div id="appContainer"><!--HTML--></div> <div id="appContainer"><!--HTML--></div>
<script type="module" src="/dist/index.mjs?t=__TIMESTAMP__"></script> <script type="module" src="/dist/index.mjs?t=__TIMESTAMP__"></script>
<script nomodule src="/dist/index.es5.js?t=__TIMESTAMP__"></script> <script nomodule src="/dist/index.es5.js?t=__TIMESTAMP__"></script>
<script
src="//cc.webmakers.de/cc.js"
defer
id="ccScript"
data-cc-tags="googleMaps"
data-cc-privacy-policy-url="/datenschutz"
data-cc-secondary-color="#c4253e"
data-cc-necessary-cookies="likecmsSession"
></script>
</body> </body>
<!--SSR.ERROR--> <!--SSR.ERROR-->

View File

@ -5,11 +5,14 @@ services:
docpress: docpress:
profiles: profiles:
- docpress - docpress
image: node:18 - tibi-dev
image: node:20
volumes: volumes:
- ./:/data - ./:/data
- ./tmp:/tmp - ./tmp:/tmp
- ./tmp/nonexistent:/nonexistent - ./tmp/nonexistent:/nonexistent
- ./tmp/.npm:/.npm
- ./tmp/.yarn:/.yarn
working_dir: /data/docs working_dir: /data/docs
command: sh -c "yarn install && yarn docpress:serve" command: sh -c "yarn install && yarn docpress:serve"
expose: expose:
@ -23,12 +26,13 @@ services:
profiles: profiles:
- tibi - tibi
- tibi-dev - tibi-dev
image: node:18 image: node:20
volumes: volumes:
- ./:/data - ./:/data
- ./tmp:/tmp - ./tmp:/tmp
- ./tmp/nonexistent:/nonexistent - ./tmp/nonexistent:/nonexistent
- ./tmp/.npm:/.npm - ./tmp/.npm:/.npm
- ./tmp/.yarn:/.yarn
working_dir: /data working_dir: /data
command: sh -c "yarn install && API_BASE=http://tibiserver:8080/api/v1/_/${TIBI_NAMESPACE} yarn start${START_SCRIPT}" command: sh -c "yarn install && API_BASE=http://tibiserver:8080/api/v1/_/${TIBI_NAMESPACE} yarn start${START_SCRIPT}"
expose: expose:
@ -112,7 +116,7 @@ services:
tibiadmin-dev: tibiadmin-dev:
profiles: profiles:
- tibi-dev - tibi-dev
image: node:18 image: node:20
volumes: volumes:
- ./../../cms/tibi-admin:/data - ./../../cms/tibi-admin:/data
working_dir: /data working_dir: /data

21
docs/README.md Normal file
View File

@ -0,0 +1,21 @@
# TibiCMS Dokumentation
![TibiCMS Aufbau](./md/tibi-aufbau.svg)
## Einleitung
_TibiCMS_ ist der Sammelbegriff für die Komponenten _tibi-server_ und _tibi-admin_, welche einen Rest-API Server und eine Administrationsoberfläche zur Verfügung stellen. Auf Basis dieser beiden Komponenten und der _MongoDB_ als Abhängigkeit lassen sich WebCMS Anwendungen abbilden.
Das CMS ist multi-mandanten-fähig, d.h. es kann mehrer Projekte mit unterschiedlichen Zugriffsbeschränkungen verwalten.
### tibi-server
Der Server selbst kommt ohne grafische Oberfläche oder WebUI. Er ist ausschließlich nach außen über eine Rest-API erreichbar.
Als einzige Abhängigkeit ist eine _MongoDB_ zu erwähnen. Da der Server in Go geschrieben ist, sind keine externen Bibliotheken notwendig. Er lässt sich daher prima via Docker-Container verteilen.
### tibi-admin
Die Administrationsoberfläche ist wie jeder andere Service, der die Rest-API des Servers nutzt, nur ein Frontend. _tibi-admin_ läuft vollständig im Browser und benötigt nur einen Webserver, der statischen Content ausliefert.
Die Version des _tibi-admin_ sollte synchron zur _tibi-server_ Version gehalten werden, damit alle Datentypen bedient werden können.

9
docs/docpress.json Normal file
View File

@ -0,0 +1,9 @@
{
"docs": "md",
"markdown": {
"plugins": {
"code-include": {}
}
},
"css": ["md/docpress.css", "md/github-dark-dimmed.css"]
}

View File

@ -0,0 +1,106 @@
const path = require('path');
const fs = require('fs');
const INCLUDE_RE = /!{3}\s*include(.+?)!{3}/i;
const BRACES_RE = /\((.+?)\)/i;
const include_plugin = (md, options) => {
const defaultOptions = {
root: '.',
getRootDir: (pluginOptions/*, state, startLine, endLine*/) => pluginOptions.root,
includeRe: INCLUDE_RE,
throwError: false,
bracesAreOptional: false,
notFoundMessage: 'File \'{{FILE}}\' not found.',
circularMessage: 'Circular reference between \'{{FILE}}\' and \'{{PARENT}}\'.'
};
if (typeof options === 'string') {
options = {
...defaultOptions,
root: options
};
} else {
options = {
...defaultOptions,
...options
};
}
const _replaceIncludeByContent = (src, rootdir, parentFilePath, filesProcessed) => {
filesProcessed = filesProcessed ? filesProcessed.slice() : []; // making a copy
let cap, filePath, mdSrc, errorMessage;
// store parent file path to check circular references
if (parentFilePath) {
filesProcessed.push(parentFilePath);
}
while ((cap = options.includeRe.exec(src))) {
let includePath = cap[1].trim();
const sansBracesMatch = BRACES_RE.exec(includePath);
if (!sansBracesMatch && !options.bracesAreOptional) {
errorMessage = `INCLUDE statement '${src.trim()}' MUST have '()' braces around the include path ('${includePath}')`;
} else if (sansBracesMatch) {
includePath = sansBracesMatch[1].trim();
} else if (!/^\s/.test(cap[1])) {
// path SHOULD have been preceeded by at least ONE whitespace character!
/* eslint max-len: "off" */
errorMessage = `INCLUDE statement '${src.trim()}': when not using braces around the path ('${includePath}'), it MUST be preceeded by at least one whitespace character to separate the include keyword and the include path.`;
}
if (!errorMessage) {
filePath = path.resolve(rootdir, includePath);
// check if child file exists or if there is a circular reference
if (!fs.existsSync(filePath)) {
// child file does not exist
errorMessage = options.notFoundMessage.replace('{{FILE}}', filePath);
} else if (filesProcessed.indexOf(filePath) !== -1) {
// reference would be circular
errorMessage = options.circularMessage.replace('{{FILE}}', filePath).replace('{{PARENT}}', parentFilePath);
}
}
// check if there were any errors
if (errorMessage) {
if (options.throwError) {
throw new Error(errorMessage);
}
mdSrc = `\n\n# INCLUDE ERROR: ${errorMessage}\n\n`;
} else {
// get content of child file
mdSrc = fs.readFileSync(filePath, 'utf8');
// check if child file also has includes
mdSrc = _replaceIncludeByContent(mdSrc, path.dirname(filePath), filePath, filesProcessed);
// remove one trailing newline, if it exists: that way, the included content does NOT
// automatically terminate the paragraph it is in due to the writer of the included
// part having terminated the content with a newline.
// However, when that snippet writer terminated with TWO (or more) newlines, these, minus one,
// will be merged with the newline after the #include statement, resulting in a 2-NL paragraph
// termination.
const len = mdSrc.length;
if (mdSrc[len - 1] === '\n') {
mdSrc = mdSrc.substring(0, len - 1);
}
}
const labelStyle = `padding: 0 4px; font-size: 12px; font-weight: bold; color: #ffffff; background: #444444; opacity: .6;`
const fileLabel = `<div style="position:relative; height: 0px;"><div class="code-file-label" style="position:absolute; top: -10px;${labelStyle}">${includePath}</div></div>\n\n`
const fileExt = includePath.replace(/^.+\./, "")
// replace include by file content
src = src.slice(0, cap.index) + fileLabel + "```" + fileExt + "\n" + mdSrc + "\n```" + src.slice(cap.index + cap[0].length, src.length);
}
return src;
};
const _includeFileParts = (state, startLine, endLine/*, silent*/) => {
state.src = _replaceIncludeByContent(state.src, options.getRootDir(options, state, startLine, endLine));
};
md.core.ruler.before('normalize', 'include', _includeFileParts);
};
module.exports = include_plugin;

View File

@ -0,0 +1,24 @@
{
"name": "markdown-it-code-include",
"version": "0.0.0",
"description": "A markdown-it plugin to include code blocks.",
"main": "./index.js",
"scripts": {
},
"keywords": [
"markdown",
"markdown-it",
"markdown-it-plugin",
"code-blocks",
"fence"
],
"license": "MIT",
"dependencies": {
"node-html-parser": "^1.3.1"
},
"devDependencies": {
"markdown-it": "^12.0.0",
"markdown-it-testgen": "^0.1.6",
"path": "^0.12.7"
}
}

52
docs/md/README.md Normal file
View File

@ -0,0 +1,52 @@
- [TibiCMS](../README.md)
- [Begriffe](begriffe.md)
- [Probleme und Lösungen](probleme.md)
- Servergrundlagen
- [Konfiguration](servergrundlagen/konfiguration.md)
- [Entitäten](servergrundlagen/entitaeten.md)
- [SSR & htaccess](servergrundlagen/ssr&htaccess.md)
- RestAPI Endpunkte
- [/login](restapi/login.md)
- [/user](restapi/user.md)
- [/project](restapi/project.md)
- [/\_/NS/COLLECTION](restapi/collection.md)
- [/_/NS/_/assets/ASSETSNAME](restapi/assets.md)
- Projekt Konfiguration
- [Ordnerstruktur](projektkonfig/ordnerstruktur.md)
- [config.yml](projektkonfig/config.yml.md)
- [collections](projektkonfig/collections.md)
- [fields](projektkonfig/collections/fields.md)
- [Datentypen](projektkonfig/collections/fields/datentypen.md)
- [Admin Widgets](projektkonfig/collections/fields/widgets.md)
- [indexes](projektkonfig/collections/indexes.md)
- [hooks](projektkonfig/collections/hooks.md)
- [imageFilter](projektkonfig/collections/imageFilter.md)
- [meta](projektkonfig/collections/meta.md)
- [jobs](projektkonfig/jobs.md)
- [assets](projektkonfig/assets.md)
- [dashboard](projektkonfig/dashboard.md)
- Admin Javascript Kontext
- [Allgemeines](admin-javascript-kontext/allgemeines.md)
- [collection.meta..eval](admin-javascript-kontext/collection.meta..eval.md)
- [field.meta..eval](admin-javascript-kontext/field.meta..eval.md)
- Server Javascript Kontext
- [Allgmeines](server-javascript-kontext/allgemeines.md)
- Packages
- [user](server-javascript-kontext/packages/user.md)
- [response](server-javascript-kontext/packages/response.md)
- [cookie](server-javascript-kontext/packages/cookie.md)
- [db](server-javascript-kontext/packages/db.md)
- [http](server-javascript-kontext/packages/http.md)
- [smtp](server-javascript-kontext/packages/smtp.md)
- [fs](server-javascript-kontext/packages/fs.md)
- [tpl](server-javascript-kontext/packages/tpl.md)
- [jwt](server-javascript-kontext/packages/jwt.md)
- [pdf](server-javascript-kontext/packages/pdf.md)
- [image](server-javascript-kontext/packages/image.md)
- [bcrypt](server-javascript-kontext/packages/bcrypt.md)
- [xml](server-javascript-kontext/packages/xml.md)
- [charset](server-javascript-kontext/packages/charset.md)
- [pdf](server-javascript-kontext/packages/pdf.md)
- [debug](server-javascript-kontext/packages/debug.md)

View File

@ -0,0 +1,47 @@
# Javascript-Kontext im tibi-admin
Diverse `meta`-Angaben ermöglichen neben der eigentliche Angabe eines festen Wertes wie z.B:
```yaml
defaultValue: "Hallo Welt"
```
auch die Angabe eines Javascript-Ausdrucks, der zur Laufzeit ausgewertet wird. Dieser Ausdruck wird in einem Javascript-Kontext clientseitig ausgeführt und ist mit diversen Variablen vorbelegt.
Die Angabe des Javascript-Codes erfolgt dabei meist mit dem `eval`-Attribut dessen Wert der String des Codes ist:
```yaml
defaultValue:
eval: "new Date().toISOString().substr(0, 10)"
```
In den Fällen in denen ein Oneliner nicht ausreiched ist, bieten sich "selbst ausführende Funktionen" an, wie z.B.:
```js
(function() {
return new Date().toISOString().substr(0, 10)
})()
```
Um diese im YAML unterzubringen nutzt man YAML-Multiline-Modifizierer:
```yaml
defaultValue:
eval: |
(function() {
return new Date().toISOString().substr(0, 10)
})()
```
## Kontext-Variablen
Der Javascript-Kontext ist mit folgenden Variablen standardmäßig angereichert:
| Variable | Datentyp | Bedeutung |
| --- | --- | --- | --- | --- |
| `$namespace` | string | Der Namespacebezeichner des aktuellen Projekts |
| `$apiBase` | string | Basis-URL des API-Endpunkts |
| `$projectBase` | string | Basis-URL des Projekts-API-Endpunkts (`$apiBase`_/`$namespace`/) |
| `$auth` TODO | object | Das aktuelle Auth-Objetc des eingeloggten Benutzers |
| `$project` | object | Das aktuelle Projekt-Objekt, siehe [API /project](./../restapi/project.md) |
Die `meta`-Daten der Collections und Fields bekommen in den Javascript-Kontext der `eval`-Eigenschaften noch jeweils zusätzliche Variablen.

View File

@ -0,0 +1,9 @@
# collection.meta..eval Javascript-Kontext
Die `eval`-Properties der Eigenschaften (wo möglich) bekommen unterhalb des `collection.meta`-Objektes zusätzlich zu den bereits bekannten Variablen (siehe [Allgemeines zum Kontext](./allgemeines.md)) folgende Variable zur Verfügung:
| Variable | Datentyp | Bedeutung |
| --- | --- | --- | --- | --- |
| `$object` | object | Das aktuelle Kollektion-Objekt, siehe [API /collection](./../restapi/collection.md) |
| `$navigation` | object | Das aktuelle Navigation-Objekt, also den entsprechenden aktiven Eintrag aus `meta.subNavigation` |

View File

@ -0,0 +1,104 @@
# field.meta..eval Javascript-Kontext
Zuätzlich zu den allgemeinen und Kollektions-spezifischen Variablen, die im Javascript-Kontext der `eval`-Eigenschaften unterhalb des zur Verfügung stehen, gibt es noch folgende Variablen unterhalb des `field.meta`-Objektes für die Evaluierung:
| Variable | Datentyp | Bedeutung |
| --- | --- | --- | --- | --- |
| `$field` TODO | object | Das aktuelle Feld-Objekt |
| `$method` | `"post"`/`"put"` | `"put"` bedeuted, dass der Datensatz gerade in Bearbeitung ist, `"post"` = Datensatz soll angelegt werden |
| `$this` | any | Der aktuelle Wert des Feldes |
| `$` | object | Das gesamte Objekt des Dokuments |
| `$parent` | object oder array | Der Wert des Elternknotens zum aktuellen Feld |
| `$stack` | array | Der Stack bis zum Ursprung des gesamten Objekts |
## Der Stack
Um die Abhängigkeiten zu bestimmten Werten ausdrücken zu können (z.B. in `meta.dependsOn.eval`), sind die Variablen `$this`, `$`, `$parent` und `$stack` verfügbar.
Folgendes Beispiel eines Datensatzes verdeutlicht die Belegung, während die Maske zum Editieren im *tibi-admin* geöffnet ist:
```json
{
"title": "Mein Datensatz",
"meta": {
"keywords": [
{
"key": "pla",
"description": "Ah Plah"
},
{
"key": "blup",
"description": "Buh Blup"
}
]
}
}
```
wobei wir den `"key": "pla"` betrachten, wären die Inhalte der Variablen folgende:
`$this`:
plah
`$parent` und `$stack[0]`:
```json
{
"key": "pla",
"description": "Ah Plah"
}
```
`$stack[1]`:
```json
[
{
"key": "pla",
"description": "Ah Plah"
},
{
"key": "blup",
"description": "Buh Blup"
}
]
```
`$stack[2]`:
```json
{
"keywords": [
{
"key": "pla",
"description": "Ah Plah"
},
{
"key": "blup",
"description": "Buh Blup"
}
]
}
```
`$stack[3]`, `entry` und `$`:
```json
{
"title": "Mein Datensatz",
"meta": {
"keywords": [
{
"key": "pla",
"description": "Ah Plah"
},
{
"key": "blup",
"description": "Buh Blup"
}
]
}
}
```

53
docs/md/begriffe.md Normal file
View File

@ -0,0 +1,53 @@
# Begriffe
## TibiCMS
Oberbegrff der den gesamten Stack, bestehend aus _tibi-server_ mit *MongoDB\*\* und *tibi-admin\* beschreibt.
## tibi-server
Rest-API Server des _TibiCMS_ Stack
## tibi-admin
Admin-UI/Backend zur Verwaltung der Inhalte im _tibi-server_
## API
Schnittstelle (hier Rest-API) des _tibi-server_ (im Projektkontext ebenso für Projektspezifische Schnittstelle vrwendet)
## project / Projekt
Projekt innerhalb des _TibiCMS_ welches üblicherweise die Datengrundlage für eine Website im _TibiCMS_ ist
## collection / Kollektion
Datensammlung innerhalb eines Projekte (z.B. Newsartikel), in relationalen Datenbanken oft eine Tabelle
## field / Feld
Ein Datenfeld innerhalb einer Kollektion mit einem bestimmten Datentyp (z.B. string, number, ...)
## validator / Validator
Code oder Anweisung zur Überprüfung der Gültigkeit von Feld-Daten
## filter / Filter
Bildfilter zum Verkleinern oder Bearbeiten von Bildern beim Abruf von der API
## projection / Projektion
Abbildung der Daten auf ein Subset der Originaldaten
## hook
Vorerst nur in Javascript geschriebene Algorithmen, die die sich in die API einklinken um Daten oder Abläufe zu manipulieren
## user / Benutzer
Ein Benutzer mit Login innerhalb des _TibiCMS_
## permission / Berechtigung
Berechtigung innerhalb eines Projektes, welche einem Benutzer zugeordnet werden kann

34
docs/md/docpress.css Normal file
View File

@ -0,0 +1,34 @@
.title, h1, h2, h3, h4, h5, .link.title.link-index {
color: #531414!important;
}
.link.title {
color: black!important;
}
.toc-menu .link.-active, .toc-menu .hlink.-active {
box-shadow: inset -2px 0 #7c2828!important;
}
ul.heading-list .hlink, ul.heading-list .hlink:visited {
color: #414141!important;
}
.menu-toggle:hover, .footer-nav a:hover, .footer-nav a:hover:after, .footer-nav a:hover:before {
color: #7c2828!important;
}
.code-file-label {
background: #dcd9d9!important;
color: #3a0909!important;
right: 0px;
opacity: 1!important;
}
a {
color: #7c2828!important;
}
code {
color:#3a0909!important;
}

View File

@ -0,0 +1,130 @@
/*!
Theme: GitHub Dark Dimmed
Description: Dark dimmed theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Colors taken from GitHub's CSS
*/
.hljs, pre, pre code {
color: #adbac7!important;
background: #22272e!important;
}
.hljs-doctag,
.hljs-keyword,
.hljs-meta .hljs-keyword,
.hljs-template-tag,
.hljs-template-variable,
.hljs-type,
.hljs-variable.language_,
.pl-k {
/* prettylights-syntax-keyword */
color: #f47067!important;
}
.hljs-title,
.hljs-title.class_,
.hljs-title.class_.inherited__,
.hljs-title.function_i,
.pl-s {
/* prettylights-syntax-entity */
color: #dcbdfb!important;
}
.hljs-attr,
.hljs-attribute,
.hljs-literal,
.hljs-meta,
.hljs-number,
.hljs-operator,
.hljs-variable,
.hljs-selector-attr,
.hljs-selector-class,
.hljs-selector-id,
.pl-e {
/* prettylights-syntax-constant */
color: #6cb6ff!important;
}
.hljs-regexp,
.hljs-string,
.hljs-meta .hljs-string,
.pl-s {
/* prettylights-syntax-string */
color: #96d0ff!important;
}
.hljs-built_in,
.hljs-symbol,
.pl-c1 {
/* prettylights-syntax-variable */
color: #f69d50!important;
}
.hljs-comment,
.hljs-code,
.hljs-formula,
.pl-c {
/* prettylights-syntax-comment */
color: #768390!important;
}
.hljs-name,
.hljs-quote,
.hljs-selector-tag,
.hljs-selector-pseudo {
/* prettylights-syntax-entity-tag */
color: #8ddb8c!important;
}
.hljs-subst {
/* prettylights-syntax-storage-modifier-import */
color: #adbac7!important;
}
.hljs-section {
/* prettylights-syntax-markup-heading */
color: #316dca!important;
font-weight: bold!important;
}
.hljs-bullet {
/* prettylights-syntax-markup-list */
color: #eac55f!important;
}
.hljs-emphasis {
/* prettylights-syntax-markup-italic */
color: #adbac7!important;
font-style: italic!important;
}
.hljs-strong {
/* prettylights-syntax-markup-bold */
color: #adbac7!important;
font-weight: bold!important;
}
.hljs-addition {
/* prettylights-syntax-markup-inserted */
color: #b4f1b4!important;
background-color: #1b4721!important;
}
.hljs-deletion {
/* prettylights-syntax-markup-deleted */
color: #ffd8d3!important;
background-color: #78191b!important;
}
.hljs-char.escape_,
.hljs-link,
.hljs-params,
.hljs-property,
.hljs-punctuation,
.hljs-tag {
/* purposely ignored */
}

58
docs/md/probleme.md Normal file
View File

@ -0,0 +1,58 @@
# Bekannte Probleme im Entwicklungsprozess mit TibiCMS
In diesem Abschnitt werden bekannte Probleme während des Entwicklungsprozesses mit TibiCMS beschrieben und Lösungsansätze aufgezeigt. Sollten ein Problem auftauchen, welches hier nicht beschrieben ist, ist es gerne gesehen, wenn dieses Problem hier dokumentiert wird.
## Probleme mit CollectionEntry Objekten
### Problemstellung
Wenn ein Objekt vom Typ CollectionEntry zurückgegeben wird, zeigt sich teilweise eine eingeschränkte Kompatibilität mit JavaScript. Beispielsweise funktioniert die push-Methode nicht bei Attributen, die einen Array-Datentyp speichern.
### Ursache
Die Problematik entsteht durch die Übersetzung des Objekts von Go nach JavaScript. Hierbei werden die Attribute nicht als native JavaScript-Objekte, sondern als Go-Objekte übersetzt, was zu einer eingeschränkten Kompatibilität führt. Insbesondere unterstützen sie nicht gängige Methoden wie push, pop etc. Ein weiteres Problem ist, dass bei diesen Inkompatibilitäten _keine Fehlermeldungen generiert_ werden, sondern schlichtweg _keine Aktion_ erfolgt.
### Lösungsansatz
Beim Manipulieren von CollectionEntry-Objekten innerhalb eines Hooks sollte man zur Sicherheit eine tiefe Kopie (Deep Copy) des Objekts erstellen, um es vollständig in ein JavaScript-Objekt zu konvertieren. Die Verwendung von {...} reicht hierbei nicht aus, da dies nur die oberste Ebene des Objekts konvertiert. JSON.stringify und JSON.parse sind nur dann zu empfehlen, wenn die Reihenfolge der Attribute irrelevant ist. Für Fälle, in denen die Reihenfolge wichtig ist, existiert bereits eine spezielle Funktion, die dies beibehält. Diese Funktion ist unter dem Namen „obj2str“ in der Utils-Datei zu finden.
### Warum die Ursache nicht behoben wurde
Die Ursache wurde nicht behoben, da die Standardmäßige, uneingeschränkte übersetzung von Go zu JS einen performancemäßigen totalschaden darstellt.
## Probleme mit SSR kompilierung
### Problemstellung
Website wird nur teilweise oder gar nicht gerendert.
### Ursache
In meinem Fall war das Problem, dass ein Button element in einem anderen Button Element enthalten war. Dies hat dazu geführt, dass der Compiler manchmal die Vollständige Seite nicht gerendert hat, manchmal auch nur vereinzelte Elemente.
### Lösungsansatz
Semantisch arbeiten.
### Warum die Ursache nicht behoben wurde
In dem fall liegt der Programmcode fehler nicht bei uns, da die Kompillierung von svelte übernommen wird.
## Probleme mit Variablen werten in #each loops
### Problemstellung
Wenn in Svelte in einem each block die länge der liste, über die iteriert wurde, verändert wird, kann es zu fehlern kommen, wenn kein einzigartiger key für jedes element angegeben wurde.
### Ursache
Die Ursache ist, dass svelte die liste nicht neu rendert, sondern nur die die properties neu zuordnet. Svelte behandelt keinen key wie den idx als key. Sprich ursprünglich gab es 3 Elemente El1, El2, El3. Wenn nun El1 entfernt wird, wird in der DOM tatsächlich El3 entfernt, da 2 ja nun der key ist, der nicht mehr existiert (idx 2/element 3) und die properties von El3 zu El2 und von El2 zu El1 zugewiesen. Das bedeutet, dass nicht reaktive variablen weiterhin den Wert von der vorherigen Komponente beeinhalten!
### Lösungsansatz
Die einfachste Lösung wäre eine Id als key zu haben, die einzigartig für das jeweilige Element ist, da svelte dann in der DOM genau das Element entfernt und nicht das letzte. Hat man das nicht und
möchte , dass der inhalt bei Veränderung der Array länge tatsächlich neu gerendert wird, so muss man explizit ein {#key} block drum rum bauen mit den jeweiligen Wert wo es neu gerendert werden soll (key nutzt ebenfalls reactive statements....).
### Warum die Ursache nicht behoben wurde
Gibt keine Lösung, da es standard Svelte verhalten ist. Gibt nur den vorgestellten workaround

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -0,0 +1,3 @@
Folgende Angaben sind in der `assets`-Sektion der [config.yml](./config.yml.md) geführt als Liste möglich:
!!!include(../api/assets/dist.yml)!!!

View File

@ -0,0 +1,17 @@
# collections
Die Konfiguration einer Kollektion sollte zur besseren Übersicht innerhalb einer gesonderten Datei im Unterorder **api/collections/** erfolgen und via `!include` in die [config.yml](./config.yml.md) eingebunden werden.
## Grundlegender Aufbau
Eine solche Datei hat folgenden Aufbau:
!!!include(../api/collections/medialib.yml)!!!
### siehe
- [fields](./collections/fields.md)
- [indexes](./collections/indexes.md)
- [hooks](./collections/hooks.md)
- [imageFilter](./collections/imageFilter.md)
- [meta](./collections/meta.md)

Binary file not shown.

View File

@ -0,0 +1,295 @@
# fields
Felder im _tibi-server_ müssen einen bestimmten Datentyp haben. Über den _tibi-admin_ können die Felder über Widgets in unterschiedlichen Ausprägungen dargestellt werden (view-Widgets), bzw. dem Benutzer eine Eingabe abverlangen (input-Widgets).
Es gibt grundlegende Angaben, die jedes Feld haben muss um vom _tibi-server_ akzeptiert zu werden. Darüber hinaus kann auch jedes Feld ein `meta` Objekt haben, was dem _tibi-admin_ mitteilt, wie er dieses Feld für Ausgabe und Eingabe behandel soll.
Zunächst folgt der grundlegende Aufbau des Feld-Objektes:
```yaml
# Der Name des Feldes wird in der Datenbank zum Objekt ebenso
# wie in der Ein- und Ausgabe über die API verwendet.
name: date
# Über "type" wird der Datentyp in der Datenbank festgelegt.
# Mögliche Typen sind weiter unten aufgelistet.
type: date
# Direkt am Feld kann eine Index-Definition erfolgen.
# Folgende mögliche Werte können ihn die Liste aufgenommen werden:
# "single" - Standard-Index für diese Feld
# "unique" - Das Feld muss einen eindeutigen Wert haben
# "text" - Alle "text"-Indexanganben aller Felder werden zu einem
# gemeinsamen Volltext-Index kombiniert
#
# Die Angabe des Volltextindex ist besser unter "collections.X.indexes"
# vorzunehmen.
index:
- single
# Jede Datenübertragung an des Server wird validiert, d.h. es werden
# keine Datentypen angenommen, die nicht zu "type" passen.
# Darüber hinaus kann via "validator" eine zusätzliche Validierung
# vorgenommen werden.
# Dazu gibt es ein extra Kapitel.
validator:
required: true
eval: new Date($this) > new Date()
# Und natürlich gibt es auch hier ein "meta" Objekt zur Steuerung
# des tibi-admin.
meta:
# Das "label" des Feldes wird als Label vor dem Widget verwendet.
label:
de: Titel
en: title
# Abgelkeitet vom "type" gibt es Standard-Widgets. für spezielle
# Aufgaben stehen aber eine Hand voll Widgets bereit, die später
# beschrieben werden.
widget: date
# Standardwerte für neue Enträge können entweder direkt angegeben
# werden oder via Javascript client-seitig generiert werden.
# In den Kontext injizierte Variablen werden später beschrieben.
defaultValue:
# Das Ergebnis von "eval" wird hier als Standardwert verwendet.
# (hier das aktuelle Datum)
eval: new Date()
# Sollen Felder abhängig von bestimmten Bedingungen ein- oder
# ausgeblendet werden, geschieht das über Anweisungen in "dependsOn".
dependsOn:
# Das Feld wird nur eingeblendet wenn der Wert von "type"
# (auf gleicher Ebene wie das Feld "date" selbst)
# gleich "news" ist.
eval: $parent?.type == "news"
```
## validator Objekt
<video width="100%" controls muted autoplay loop>
<source src="validator.webm" type="video/webm">
</video>
Wie im Beispiel von **fields/date.yml** unter `validator` zu sehen ist, wird dort ein Datum nach dem aktuellen erwartet. Wie der Validator sich auf die UI auswirkt, ist im obigen Video zu sehen.
Das `validator` Objekt wird _tibi-server_ seitig genutzt um die Daten zu validieren. Da das `validator` Objekt dem _tibi-admin_ ebenso zur Verfügung steht, kann vorab eine client-seitige Validierung zusätzlich durchgeführt werden.
Attribute des Objektes:
| Attribut | Datentyp | Beschreibung |
| ----------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `required` | boolean | wenn `true`, dann ist zwingend eine Eingabe zu diesem Feld nötig |
| `allowZero` | boolean | in Kombination mit `required: true`, wenn `true`, dann ist der jeweilige "Null"-Wert des Datentyps erlaubt<br><br>z.B. `type: string` erlaubt den leeren String und `type: number` erlaubt `0` |
| `eval` | string | Javascript-Code der zu true evaluieren muss um den Wert des Feldes als gültig zu definieren |
### eval-Attribut
Der Javascript-Code in diesem Attribut kann folgende Rückgabe-Werte haben:
| Wert | Bedeutung |
| -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `true` | Der Wert des Feldes ist gültig |
| `false` | Der Wert des Feldes ist ungültig |
| `"Text"` | Wird ein String zurückgegeben ist, wird der Wert es Feldes ebenso als ungültig erachtet und der String selbst ist eine benutzerdefinierte Fehlermeldung, die in der Serverantwort gelesen werden kann. |
Da der `eval` Code serverseitig immer ausgeführt wird und ein Fehlschlag zwangsläufig zum Abbruch der Serveraktion führt, ist es wichtig, dass der Validator berücksichtigt wird.
Optional kann der Code auch zusätzlich über eine Lauffähigkeit ohne Fehler (z.B. keine Verwendung nicht vorhandender Kontext-Variablen oder Verwendung von `try ... catch`) im _tibi-admin_ verfügen. Das hat den Vorteil, dass eine Vorab-Validierung stattfindet, bevor der Datensatz an der Server gesendet wird.
Sollte der `eval` Code im _tibi-admin_ nicht lauffähig sein (nicht abgefangene Exception), wird der Validator clientseitig ingoriert und nur die serverseitige Prüfung beeinflusst die Aktion.
#### siehe
- [Server Javascript Kontext](./../../server-javascript-kontext/allgemeines.md)
## dependsOn
<video width="100%" controls muted autoplay loop>
<source src="dependsOn.webm" type="video/webm">
</video>
Obige Darstellung wie im Video wird beispielsweise durch folgende Feld-Konfiguration erreicht:
```yaml
# in einer Kollektions-Konfiguration
fields:
- name: type
type: string
meta:
label:
de: Typ
en: Type
widget: select
choices:
- name:
de: Standardseite
en: Standard page
id: page
- name:
de: News
en: News
id: news
- name: title
type: string
meta:
label:
de: Titel
en: Title
- name: date
type: date
meta:
label:
de: Titel
en: title
widget: date
defaultValue:
eval: new Date()
dependsOn:
eval: $parent?.type == "news"
```
`meta.dependsOn` kann als Objekt mit `eval`-Attribut für Javascript oder als `string` mit dem Feldnamen (Punktschreibweise, z.B. `"additionalData.author"`) angegeben werden.
Wird der Feldname verwendet wird nur geprüft, ob das Feld belegt ist. TODO
Die `eval` Variante verwendet als Javascript-Kontext Variablen die auf folgenden Seite beschrieben werden:
- [Admin Javascript Kontext](./../../admin-javascript-kontext/allgemeines.md)
- [collection.meta..eval](./../../admin-javascript-kontext/collection.meta..eval.md)
- [field.meta..eval](./../../admin-javascript-kontext/field.meta..eval.md)
Die Rückgabe des Javascript-Codes beeinflusst die Einblendung des betroffenen Feldes in folgender Weise:
| Rückgabe | Bedeutung |
| -------- | -------------------------- |
| `true` | Das Feld wird angezeigt |
| `false` | Das Feld wird ausgeblendet |
## defaultValue
Für die Vorlegung neu anzulegender Datensätze kann in `field.meta.defaultValue` direkt der Standardwert hinterlegt werden, oder über `field.meta.defaultValue.eval` ein Javascript-Code angegeben werden, der den Wert ermittelt. Die Rückgabe des Javascript-Codes, sowie auch die direkte Vergabe des Wertes muss dem Datentyp des Feldes entsprechen.
Der Javascript-Kontext ist der gleiche wie bei `field.meta.dependsOn.eval`.
## containerProps
Um Felder auf breiten Bildschirmen eine schmalere Breite zu geben, wird das containerProps Attribut empfohlen. Es hat ein class Attribut, welches klassen ins HTML injiziert. Es gibt außerdem noch das Layout attribut mit breakBefore und breakAfter, welche dafür sorgen, dass vorher bzw. nachher keine weiteren HTML Elemente platz finden. Hier ist des weiteren das size Objekt drin, welches 3 attribute hat. Die attribute sollten col-1 bis col-12 beeinhalten, diese klassen werden ins html injiziert, können also dem zufolge auch misbraucht werden. Die klassen bei den attributen werden bei unterschiedlichen Bildschirmbreiten aktiv.
```yaml
containerProps:
#optional class prop
layout:
breakBefore: false
breakAfter: false
size:
default: "col-8"
small: "col-12"
large: "col-4"
```
## inputProps
Wenn man das Input element direkt bearbeiten möchte (Bspw. readonly oder ähnliches), so kann man diese hier als Objekt übergeben:
## hide
möchte man, dass ein bestimmtes Feld nicht im TibiAdmin sichtbar ist, so muss man die property hide auf true setzen.
```yaml
inputProps: { readonly: true, placeholder: { de: "Wert wird automatisch gesetzt", en: "Value is set automatically" } }
```
## direction
Für type Object[] gibt es im Meta objekt das direction attribut, dies kann entweder:
- `horizontal`: flex-direction: row
oder
- `vertical`: flex-direction: column
annehmen.
## metaElements
Möchte man bestimmte Elemente über das Zahnrad greifbar machen (bei type: Object[]), so kann man dies über dieses Attribut tun. Es ist entweder über eine Liste, oder über tablist möglich.
!!!include(../api/collections/fields/info.yml)!!!
## folding
Das folding Objekt ist ebenfalls ein Teil im Meta object und dient dazu, type ObjectArray einen Wert in den Header im HTML einzuschreiben (von den einzelnen Objekten). Es wird vorallem dazu genutzt, die Rows bzw. Columns der Website rein zu rendern, um praktisch ein direktes Prview zu haben. Ebenfalls gibt es das force attribut, welches dafür sorgt, dass die objekte IMMER geöffnet sind und man sie nicht schließen kann. Sinnvoll für Rows. die Generelle struktur verdeutlicht folgendes Code Beispiel:
```yaml
folding:
force: false
previewUnfolded:
raw: true
eval: |
//js
(() => {
return $this?.title ? "<h2 style=\"\">" + $this.title + "</h2>" : ""
})()
//!js
previewFolded:
eval: |
//js
(async () => {
const { getRenderedElement, Row } = await import($projectBase + "_/assets/dist/admin.mjs")
const container = getRenderedElement(Row, {
props: {
row: Object.assign({}, $this),
contentId: $?.id,
apiBaseURL: $projectBase,
},
addCss: [
$projectBase + "_/assets/dist/index.css",
$projectBase + "_/assets/dist/admin.css",
],
})
let style = "max-width: 1220px;"
container.style = style
return container
})()
//!js
```
Hierbei ist raw dafür da, das ganze als HTML direkt zu rendern, wenn es true ist. Der prefiewFolded bereich rendert letzten endes die Seite selbst, für diese Funktionallität ist mehreres notwendig.
- `Row und Column Komponenten`: Dies sind letzenendes jene komponenten die gerendert werden, daher muss man sie natürlich auch bereits programmiert haben.
- `admin.ts file`: Dieses file wird im src Folder platziert und durch ES-Build über den oben genutzen Pfad verfügbar gemacht. Hier ist ein Beispiel:
```ts
import Row from "./components/Row.svelte"
import Col from "./components/Col.svelte"
function getRenderedElement(component, options?: { props: { [key: string]: any }; addCss?: string[] }) {
const el = document.createElement("div")
el.attachShadow({ mode: "open" })
const target = document.createElement("body")
el.shadowRoot.appendChild(target)
options?.addCss?.forEach((css) => {
const link = document.createElement("link")
link.rel = "stylesheet"
link.href = css
link.type = "text/css"
el.shadowRoot.appendChild(link)
})
new component({
target: target,
props: options?.props,
})
return el
}
export { getRenderedElement, Row, Col }
```
Das props Attribut nimmt ein Objekt entgegen, welches als keys die namen hat, wie in der svelte Komponente über export exportiert wurde. Die Komponenten werden in eine Shadow Dom geladen, um sie seperat vom restlichen code halten zu können.

View File

@ -0,0 +1,43 @@
# Datentypen
Via `type` wird der Datentyp des Feldes definiert. Diese Angaben sind für den Tibi Server relevant. Folgende Datentypen sind möglich:
## string
String wird für Zeichenketten verwendet. Das Standardwidget ohne weitere Angabe ist bei der Ausgabe die direkte Textausgabe und bei der Eingabe ein HTML `<input ...>` Element mit dem Attribut `type="text"`.
## number
Number wird sowohl für ganze Zahlen, wie auch für Gleitkommawerte definiert. Auch hier ist das Standard-Widget für die Eingabe ein HTML `<input ...>` Element, allerdings mit dem Attribut `type="number"`.
## boolean
Ein boolcher Wert, also `true` oder `false`, wird über den Typ `"boolean"` definiert und standardmäßig als Checkbox dargestellt.
## date
`"date"` als Datentyp kann sowohl Datumsangabe mit, als auch ohne Uhrzeit aufnehmen. Das Standardwidget ist die einfache Datumseingabe ohne Uhrzeit.
## file
Der Datentyp `"file"` ist für Dateiuploads vorgesehen. Es daher standardmäßig ein Datei-Auswahl-Dialog als Widget für die Eingabe angeboten.
## string[]
Für `"string[]` Arrays ist die Angabe des Widgets zwingend notwendig.
## number[]
Auch für `"number[]"` Arrays wird die Widget-Angabe erwartet.
## object
`"object"` ist ein spezieller Datentyp der zur Strukturierung der API und der Eingabe dient. Dieser Datentyp fasst `subFields` zusammen.
## object[]
Wie `"object"` fasst auch das `"object[]"` Array `subFields` zusammen. Diese allerdings als Liste von Objekten, anstatt als Einzelobjekt.
## any
Felder vom Typ `"any"` können beliebige Daten aufnehmen. Die Validierung schlägt auf Basis der Typ-Validierung hier nie fehl.

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -0,0 +1,131 @@
# Widgets
Die Verwendung von Widgets innerhalb der Anwendung tibi-admin dient zur Handhabung von Dateninputs und -outputs. Das genutzte Widget wird über das meta.widget Feld in der Konfiguration spezifiziert. Dabei wird der Name des Widgets als Textzeichenkette (String) angegeben.
Es ist zu beachten, dass nicht jedes Widget für jeden Datentyp geeignet ist. Im Weiteren werden die kompatiblen Datentypen für jedes Widget aufgeführt. Zusätzlich werden spezielle Konfigurationsmöglichkeiten für jedes Widget erläutert.
## Texteingabefeld-Widgets: string / text / input
Diese Bezeichnungen stehen alle für dasselbe Widget. Es handelt sich hierbei um ein Texteingabefeld. Dieses Widget wird für den Datentyp String verwendet. Sollte ein größeres Textfeld (Textarea) anstatt eines einfachen Eingabefeldes (Input) gewünscht sein, so kann dies erreicht werden, indem das Attribut multiline im inputProps Objekt auf true gesetzt wird.
!!!include(../api/collections/fields/pageTitle.yml)!!!
## Numerische Eingabefeld-Widgets: number / int / integer / float / double
Diese unterschiedlichen Bezeichnungen stehen alle für dasselbe Widget. Hierbei handelt es sich um ein Eingabefeld für Zahlen. Es wird für den Datentyp Number verwendet.
!!!include(../api/collections/fields/manualSort.yml)!!!
## Auswahl-Widgets: boolean / bool / check / switch / checkbox
Diese verschiedenen Bezeichnungen repräsentieren dasselbe Widget. Dieses Widget wird in Form einer Auswahlbox (Checkbox) dargestellt und wird für den Datentyp Boolean verwendet.
!!!include(../api/collections/fields/active.yml)!!!
## Auswahl-Widgets für mehrere Optionen: select / selectArray
Diese beiden Widgets sind im Prinzip das Gleiche, nur mit unterschiedlichen Namen. Intern wird die Unterscheidung zwischen Mehrfachauswahl und einfacher Auswahl anhand des Datentyps getroffen. Bei Datentypen mit einem "[]" am Ende wird die Mehrfachauswahl verwendet. Der Einsatz von selectArray ist für String-Arrays vorgesehen, select für einzelne Strings. Aktuell sind nur Strings möglich, da das Element alle Werte zu Strings konvertiert. Anpassungen sind jedoch bei Bedarf möglich. Es ist wichtig zu beachten, dass das name-Attribut den visuell dargestellten Wert darstellt, während die id den gespeicherten Wert repräsentiert. Wenn choices als Objekt angegeben wird, wird eine Anfrage an den spezifizierten Endpunkt mit den angegebenen Parametern gesendet und das gemappte name-Attribut davon angezeigt. Die id der ausgewählten Elemente wird intern als String gespeichert. Weiterhin ist die Angabe von chipStyle: (style) möglich. Dieser wird als Stil in das Element gerendert und ermöglicht zum Beispiel die visuelle Darstellung von Flaggen.
!!!include(../api/collections/fields/contentType.yml)!!!
## Bezug zu anderen Datenbankeinträgen: foreignKey
Dieses Widget wird verwendet, um eine Referenz zu einem anderen Datenbankeintrag herzustellen. Neben der Angabe von widget: foreignKey gibt es das foreign Attribut, welches die referenzierte Sammlung (collection) angibt. Zudem gibt es ein id Feld, welches die spezifische id für die Sicherheitsüberprüfung angibt. Wird hier "id" angegeben, wird es automatisch auf \_id gemappt, da dies der Name des ID-Feldes in MongoDB ist. Des Weiteren gibt es eine subNavigation, die die Struktur des Modals spezifiziert und neben dem Üblichen a) modal heißen sollte (Konvention) und b) einen defaultCallback haben sollte, der ausgelöst wird, wenn auf den Eintrag geklickt wird. Für die Auswahl gibt es auf dem Fensterobjekt (window Objekt) eine selectEntry Methode, die den ForeignEntry auswählt. Es gibt auch ein sort Attribut, falls die Auswahlmöglichkeiten sortiert werden sollen. Dieses wird einfach an die Anfrage angehängt. Wenn die zurückgegebenen Felder eingeschränkt werden sollen, kann eine Projektion (projection) für die Sammlung spezifiziert werden. Schließlich gibt es das render Attribut, welches ein Objekt ist und ein eval Feld enthält. Hier kann man unter anderem auf $foreignEntry und somit auf alle Werte der ausgewählten Projektion zugreifen. Der zurückgegebene Wert wird schließlich gerendert. Wenn das HTML roh gerendert werden soll, kann das raw Attribut auf true gesetzt werden.
!!!include(../api/collections/fields/images.yml)!!!
```yml
meta: #... in medialib collection
subNavigation:
- name: modalForeign # Name des Eingabefelds oder der Ansicht.
defaultSort: # Standard-Sortierkriterien, die angewendet werden, wenn keine anderen Sortierkriterien spezifiziert sind.
field: "path" # Standardmäßig wird nach dem "path"-Feld sortiert.
order: "ASC" # Standardmäßig wird in aufsteigender Reihenfolge (ASC) sortiert.
views: # Liste der Ansichten, die in diesem Feld angezeigt werden können.
- type: table # Es wird eine Tabellenansicht verwendet.
mediaQuery: "(min-width: 0px)" # Die Tabellenansicht wird nur angezeigt, wenn die Bildschirmbreite mindestens 0px beträgt.
columns: # Liste der Spalten, die in der Tabelle angezeigt werden.
- source: file
defaultCallback: # Standard-Callback-Funktion, die ausgeführt wird, wenn keine andere spezifiziert ist.
eval: | # Der Code wird als JavaScript evaluiert.
//js
(entry) => {
parent.selectEntry(entry)
}
//!js
```
Setzt man defaultCollectionViews auf true, so könnte das ergebnis wie folgt aussehen:
![defaultCollectionViews auf ture](foreign.png)
## Datums-Widgets: date / dateTime
Diese beiden Widgets können für den Typ "date" verwendet werden. date erzeugt ein Widget (nur das Datum), während dateTime ein Widget erzeugt (Datum und Uhrzeit).
!!!include(../api/collections/fields/from.yml)!!!
## Textbearbeitungs-Widgets: richtext / html
Diese beiden Bezeichnungen stehen für dasselbe Widget. Es handelt sich um ein Textfeld (Textarea) mit erweiterten Bearbeitungsmöglichkeiten (ähnlich wie in Word), wobei die Eingabe als HTML in einen String geladen wird. Das HTML kann auch manuell angepasst werden, indem die "source" Checkbox aktiviert wird.
!!!include(../api/collections/fields/text.yml)!!!
## Datei-Upload-Widgets: file / image / mediaLibraryFile
Diese verschiedenen Bezeichnungen stehen alle für das gleiche Widget. Es wird für den Datentyp File verwendet.
!!!include(../api/collections/fields/file.yml)!!!
## Mehrfachauswahl-Widgets: checkboxArray
Hierbei handelt es sich um eine Reihe von Auswahlboxen (Checkboxen). Jede einzelne Auswahlbox spiegelt das Array choices wider. Dies entspricht genau dem, was auch im selectArray geschieht, nur dass es hier anders dargestellt wird.
!!!include(../api/collections/fields/excludedDays.yml)!!!
## Eingabe mit Vorschlägen: chipArray
Dieses Widget hat eine ähnliche Funktion wie select, wird jedoch visuell anders dargestellt. Es bietet ein Eingabefeld, in dem nur Elemente akzeptiert werden, wenn ein Objekt im choices Array den gleichen name Wert wie das Eingabeelement hat. Darüber hinaus kann man im meta Objekt autocomplete auf true setzen, um die Autovervollständigung zu aktivieren. Dadurch werden dem Benutzer die möglichen Einträge angezeigt und können direkt ausgewählt werden, was die Benutzerfreundlichkeit erhöht.
```yml
name: tags # Name des Eingabefelds.
type: string[] # Datentyp des Eingabefelds.
meta:
label: { de: "Tags", en: "Tags" } # Feldlabel.
widget: chipArray # Verwendetes Widget.
choices: # Auswahlmöglichkeiten.
- name: "Tech" # Anzeigename der Auswahl.
id: "tech" # Wert der Auswahl.
- name: "Wissenschaft" # Anzeigename der Auswahl.
id: "science" # Wert der Auswahl.
autocomplete: true # Option für Autovervollständigung.
```
## object / objectArray / object[] / containerLessObjectArray / containerLessObject
Dieses Widget erfordert die weitere Angabe von subFields, die außerhalb des meta Objekts spezifiziert werden müssen. Hier werden die Felder angegeben, die in diesem Objekt enthalten sein sollen. containerLess bedeutet, dass das Objekt in der UI nicht dargestellt wird, und nur der Inhalt ausgegeben. Dadurch wird übermäßiges verschachteln unterbunden.
!!!include(../api/collections/fieldLists/formular/checkboxGroup.yml)!!!
## grid
Für Datentyp object[], dient als übersichtliche object[] alternative, speziell für pagebuilder entwickelt.
!!!include(../api/collections/fields/rows.yml)!!!
## jsonField
Wird für Daten genutzt, wo man die Struktur nicht absehen kann.
!!!include(../api/collections/fields/form.yml)!!!
## tabs
Dieses Widget hat im Prinzip die gleiche Funktion wie dasjenige in der Collection Meta-Konfiguration, ist jedoch etwas anders strukturiert. Ähnlich wie beim object Widget werden subFields verwendet, wobei das label von jedem subField der jeweilige Tab-Name ist. Würde man type auf number setzen, so hätte man in diesem Fall einfach einen Tab mit dem Namen "xyz" und ein number Feld im Tab mit dem gleichen Namen. Sinnvoller ist es natürlich, type auf object zu setzen, um mehrere Felder in einen Tab zu packen.
```yml
type: object
name: formular
meta:
label:
de: Formular
en: Form
widget: jsonField
```
# useDefaultArray
Wenn ein belibiger Datentyp in einem Array gefordert ist, so kann man jedes beliebige Widget dafür nutzten, indem man useDefaultArray: true benutzt. Damit kann jedes widget in das defaultArray widget gepackt werden. Wird Object[] in kombination mit useDefaultArray verwendet, so wird die einfache Objektdarstellung in diese darstellung implementiert.
![useDefaultArray auf true](defaultArray.png)
!!!include(../api/collections/fields/emailCC.yml)!!!

View File

@ -0,0 +1,112 @@
# hooks
Hooks in Tibi sind spezielle Funktionen, die bestimmte Teile der HTTP-Anfragen und -Antworten manipulieren können. Sie erlauben Ihnen, den Datenfluss und die Abläufe zu bestimmten Zeitpunkten im Lebenszyklus einer HTTP-Anfrage zu beeinflussen.
Jeder Hook ist einer bestimmten HTTP-Methode (z.B. GET, POST, PUT, DELETE) und einem bestimmten Schritt in diesem Prozess zugeordnet. Die verfügbaren Schritte variieren je nach Methode und können beinhalten:
- read (GET)
- return (GET, POST, PUT, DELETE)
- bind (POST, PUT)
- validate (POST, PUT)
- create (POST)
- update (PUT)
- delete (DELETE)
Jeder dieser Schritte wird an einem spezifischen Punkt während der Verarbeitung einer HTTP-Anfrage oder -Antwort ausgeführt. Die genaue Reihenfolge und das Verhalten der Hooks ist in dem betrag zur Collection definiert.
## Hook Implementierung
Jeder Hook ist in einer separaten JavaScript-Datei implementiert, die im hooks-Ordner Ihres Projekts gespeichert ist. Der Pfad zu dieser Datei wird in der jeweiligen collection yml Datei angegeben.
Ein Hook ist eine Funktion, die eine context-Variable zur Verfügung hat, welche Informationen und Methoden für die aktuelle Anfrage bereitstellt. Der Rückgabewert dieser Funktion wird verwendet, um die Verarbeitung der Anfrage oder Antwort zu beeinflussen.
Zwei spezielle Typen, `HookResponse` und `HookException`, werden in Hooks verwendet, um Daten zu manipulieren und Fehler zu signalisieren.
### HookResponse
Die HookResponse ist das Objekt, das von einem Hook zurückgegeben wird. Es kann verwendet werden, um Daten zu manipulieren, die in die Datenbank geschrieben oder an den Benutzer zurückgegeben werden.
Es beinhaltet:
- `data`: Daten, die in die Datenbank geschrieben werden. Sie können diese Daten im Hook ändern, bevor sie in die Datenbank geschrieben werden.
- `results`: Daten, die an den Benutzer zurückgegeben werden. Sie können diese Daten im Hook ändern, bevor sie an den Benutzer zurückgegeben werden. (return hook)
### HookException
Eine HookException ist ein Fehler, der in einem Hook geworfen werden kann. Sie können eine HookException verwenden, um einen Fehler zu signalisieren und die Verarbeitung der Anfrage oder Antwort zu stoppen. Er kann aber auch verwendet werden, um einen 200er zu werfen und zu verhindern, dass irgendetwas in die Datenbank geschrieben wird. Dies ist bei einer "actions" collection vom vorteil.
Eine HookException kann folgende Eigenschaften haben:
- `status`:
HTTP-Statuscode des Fehlers.
- `html`:
HTML-Nachricht des Fehlers.
- `message`:
Textnachricht des Fehlers.
- `bytes`:
Binäre Daten des Fehlers.
- `json`:
JSON-Daten des Fehlers.
- `file`:
Dateipfad der Fehlermeldung.
- `log`:
Wenn true, wird der Fehler im Serverprotokoll aufgezeichnet.
### Hook Beispiel
Hier ist ein Beispiel für einen Hook, der die GET-Methode bearbeitet:
```js
;(function () {
/** @type {HookResponse}*/ // @ts-ignore
let hookResponse
let request = context.request()
if (request.query("rateIt")) {
let orderNumber
orderNumber = Number(request.query("orderNumber"))
if (isNaN(orderNumber))
throw {
status: 400,
message: "Invalid order number.",
}
/** @type {Order} */ // @ts-ignore
let order = context.db.find("order", {
filter: {
sequence: orderNumber,
},
})[0]
if (!order)
throw {
status: 400,
message: "No entry with this order number.",
}
if (order.deliveryAddress.postcode != request.query("postalcode"))
throw {
status: 403,
message: "Error",
}
hookResponse = {
filter: {
orderId: order.id,
},
}
return hookResponse
}
})()
```
In diesem Beispiel wird zuerst die Anfrage analysiert und eine Bedingung überprüft. Wenn die Bedingung erfüllt ist, wird ein bestimmtes Element in der Datenbank gesucht. Wenn das Element gefunden wird und bestimmte Kriterien erfüllt, wird ein HookResponse-Objekt erstellt und zurückgegeben. Wenn während des Prozesses Fehler auftreten, werden entsprechende HookException-Objekte geworfen.
Hier ist ein beispiel für einen Posthook, welcher ein dynamisches formular validiert (Post create):
!!!include(../api/hooks/forms/post_create.js)!!!
Hier ist ein beispiel für einen Posthook, welcher ein dynamisches formular abschickt (Post return):
!!!include(../api/hooks/forms/post_return.js)!!!

View File

@ -0,0 +1,29 @@
# imageFilter Objekt
Die Bildmanipulation von hochgeladen Bildern zu einer Kollektion kann über das `imageFilter` Objekt definiert werden.
Der Filter wird angewandt, wenn an die Bild-URL der Parameter `filter=...` angehangen wird.
Der Prozess selbst erfolgt beim ersten Abruf des Bildes und wird zwischengespeichert.
Eine beispielhafte Konfiguration, die die Bilder nur verkleinert sieht so aus:
!!!include(../api/collections/fields/imageFilter.yml)!!!
Folgende Attribute können Filter-Eintrage haben, wobei `fit` und `fill` exklusiv sind:
| Attribut | Typ | Beschreibung |
| ------------ | ---------------------------------------------------------- | ------------------------------------------------------------------- |
| `fit` | boolean | passt das Bild in ein Rechteck ein |
| `fill` | boolean | streckt/staucht das Bild, so dass es das Rechteck komplett ausfüllt |
| `height` | number | Höhe des Rechtecks |
| `width` | number | Breite des Rechtecks |
| `blur` | number | Verwischungsgrad |
| `brightness` | number | Helligkeit |
| `contrast` | number | Konrast |
| `gamma` | number | Gamma-Wert |
| `saturation` | number | Sättigung |
| `sharpen` | number | Schärfe |
| `invert` | boolean | Farben invertieren |
| `grayscale` | boolean | Schwarz-Weiß |
| `resampling` | "lanczos"<br>"nearestNeighbor"<br>"linear"<br>"catmullRom" | Resampling-Algorithmus |
| `quality` | number | Ausgabequalität 0..100 |

View File

@ -0,0 +1,30 @@
# indexes Liste
Die indexes-Anweisung in der Konfigurationsdatei einer Sammlung (collection.yml) ist ein Array von Indexdefinitionen. Indizes werden in Datenbanken verwendet, um die Suchleistung zu optimieren. Indem Sie die richtigen Indizes definieren, können Sie die Effizienz Ihrer Anwendung verbessern. Es ist NICHT MÖGLICH einen gesetzten Index über die yml Datei wieder zu entfernen, hierfür muss man direkt in die Datenbank gehen.
Jede Indexdefinition ist ein Objekt mit bestimmten Eigenschaften:
- `name`:
Ein eindeutiger Name für den Index. Es ist optional, wird jedoch empfohlen, um den Index später leicht identifizieren zu können.
- `key`:
Bestimmt, auf welche Felder der Index angewendet werden soll. Dies kann ein einfacher String sein, wenn der Index nur ein Feld umfasst, oder ein Array von Strings, wenn der Index mehrere Felder umfasst.
Zum Beispiel key: ["$text:$**"] definiert einen Volltextindex über alle Felder. Der spezielle Operator $text wird verwendet, um einen Volltextindex zu erstellen, und der Operator $\*\* bezeichnet alle Felder in der Sammlung.
Eine andere mögliche Indexdefinition könnte so aussehen: key: ["file.type"]. Dies würde einen Index auf dem Feld type innerhalb des Unterobjekts file erstellen.
- `unique`:
Wenn auf true gesetzt, erzwingt dies, dass der Index eindeutige Werte enthält. Wenn Sie versuchen, einen Eintrag mit einem bereits indizierten Wert hinzuzufügen, wird ein Fehler ausgelöst und der Vorgang abgebrochen.
- `background`:
Wenn auf true gesetzt, erstellt die Datenbank den Index im Hintergrund, um die Leistungsauswirkungen auf andere Operationen zu minimieren.
- `defaultLanguage`:
Wird verwendet, um die Sprache für Textindizes festzulegen. Dies ist wichtig für die Volltextsuche, da verschiedene Sprachen unterschiedliche Tokenisierungs- und Stemmungsregeln haben.
Ein Beispiel für die Verwendung von Indizes in der Sammlungskonfigurationsdatei könnte so aussehen:
!!!include(../api/collections/fields/textIndex.yml)!!!
In diesem Beispiel wird ein Textindex namens textindex erstellt, der alle Felder der Sammlung abdeckt. Der Index wird im Hintergrund erstellt und verwendet Deutsch als Standardtextsprache.

View File

@ -0,0 +1,76 @@
# meta Objekt
Wie bereits an anderer Stelle beschrieben, dient das `meta` Objekt zur Definition von Merkmalen, die im _tibi-admin_ finden. Zum Anlegen der Struktur in der Datenbank und Definition der API haben diese Angaben keine Relevanz.
Folgende Angaben sind möglich:
!!!include(../api/collections/democol/meta.yml)!!!
## views Liste
`views` werden für die Darstellung der Kollektion-Daten im _tibi-admin_ benötigt. Die Auswahl des passenden View erfolgt über CSS Media-Queries.
Optionale Unternavigationen können eigene `views` haben.
Möchte man, dass in der view selection Navbar vor den View namen bestimme Icons angezeigt werden, so kann man diese einfach per muiIcon im jeweiligen View Objekt angeben.
Folgende möglche Einträge für `views` gibt es derzeit:
### simpleList
!!!include(../api/collections/fields/medialibSimpleList.yml)!!!
### table
!!!include(../api/collections/fields/medialibTable.yml)!!!
### cardList
!!!include(../api/collections/fields/medialibCardList.yml)!!!
## quickEdit
!!!include(../api/collections/fields/quickEditMedialib.yml)!!!
### dashboardSimpleList
Fürs dashboard type: table
```yml
type: dashboardSimpleList
mediaQuery: "(max-width: 600px)"
primaryText: email
secondaryText: subject
```
### dashboardTable
Fürs dashboard type: table
```yml
type: dashboardTable
mediaQuery: "(min-width: 600px)"
columns:
- subject
- file
- file
- subject
- file
```
## tablist
Wird die `tablist` verwendet, ist sicher zu stellen, dass alle Felder in der Definition aufgenommen werden. Werden Felder nicht in die `tablist` aufgenommen, sind diese weiterhin in einer Gesamtliste unterhalb der Tabs und bringen das Layout durcheinander.
## multiupload
Der mutliupload kann bei jedem view type verwendet werden. Über $file kann man in eval auf das aktuelle file Objekt zugreifen. Hier ist eine Beispielscollection, welchen diesen verwendet.
!!!include(../api/collections/medialib.yml)!!!
## backups
im meta Objekt einer collection können backups für diese collection konfiguriert werden. Die backups werden in der Datenbank gespeichert und können über das tibi-admin in der selben collection angewandt werden. Wird ein collectoneintrag gelöscht, kann man diesen über den gelöschte einträge checkbox wiederherstellen.
folgende collection ist ein beispiel für eine backup collection sowie die aktvierung der backupfunktion in einer "normalen" collection (Im Meta objekt...).
!!!include(../api/collections/fields/backup.yml)!!!
!!!include(../api/collections/backups.yml)!!!

Binary file not shown.

View File

@ -0,0 +1,34 @@
# config.yml
Die Datei **config.yml** ist der Einstieg in die API-Konfiguration eines Projekts. Die Datei kann sich an einem beliebigen Ort befinden. Die einzige Bedingung ist, dass sie durch den tibi-server lesbar ist.
Es hat sich jedoch als günstig erwiesen bei Webprojekten die Datei und alle anderen Datein, die zur API-Konfiguration gehören, im ordner [api/](./ordnerstruktur.md) unterhalb des eigentlichen Webprojektes anzuordnen. Die Quellen des Frontends und der API können somit in ein Mono-Repo eingecheckt werden.
## Aufbau
!!!include(../api/config.yml)!!!
Der Namespace legt die eigentliche Projektbezeichnung und den Datenbankkontext fest.
Er sollte nach Projektinitialisierung auf dem tibi-server nicht mehr angepasst werden.
In den Projekteinstellungen im tibi-server kann der Namespace durch einen Datenbankeintrag
Überschrieben werden.
Über die Bezeichnung des Namespace plus einen Prefix der in der globalen Server-Konfig
hinterlegt ist, definiert sich der Datenbank-Name innerhalb der MongoDB.
Das "meta"-Objekt ist frei definierbar, wird aber vom tibi-admin in spezieller Form erwartet.
Mögliche Angaben, die der tibi-admin versteht, sind hier mit aufgeführt.
Das imageUrl objekt definiert den Pfad zu einer Bilddatei die als Projektbild im tibi-admin verwendet wird
"collections" ist eine Auflistung von Kollektions-Konfigurationen.
Hier bietet sich eine Auslagerung und Einbindung via YAML-Tag "!include" an.
Unter "jobs" können Jobs definiert werden, die regelmäßig ausgeführt werden sollen.
Werden Dateien innerhalb vom tibi-admin benötigt, bietet es sich an diese über
"assets"-Pfade erreichbar zu machen
### siehe
- [dashboard](./dashboard.md)
- [collections](./collections.md)
- [jobs](./jobs.md)
- [assets](./assets.md)

View File

@ -0,0 +1,511 @@
# dashboard
# Übersicht
Die bereitgestellte Konfiguration ist eine Spezifikation für ein Dashboard-Layout und seine Komponenten. Dieses Layout bestimmt die Anzeige und Interaktion von verschiedenen Datenvisualisierungen, vor allem in Form von Diagrammen (Graphen). Die Konfiguration ist in zwei Hauptabschnitte unterteilt: "majorItems" und "minorItems". Die "majorItems" sind größere, prominentere Darstellungen von Daten, während die "minorItems" kleinere, weniger prominente Datenelemente repräsentieren. Jedes Element innerhalb dieser Abschnitte ist ein einzelnes Modul oder eine Komponente auf dem Dashboard und kann verschiedene Arten von Datenvisualisierungen darstellen, einschließlich Linien-, Balken-, Kreis- (donut) und Flächendiagramme. Generell ist zu sagen, dass bei jeder string angabe auch I18n verwendet werden kann und bei nahezu jeder String angabe ist ebenfalls die eval angabe möglich. Sollten Nutzer keinen Zugriff auf die Daten haben dürfen, so wird das bestimmte Diagramm nicht gerendert und stattdessen ein Placeholder mit Zugriff verweigert angezeigt.
# Elementbeschreibungen
## type
Der Typ des Dashboard-Elements ist ein entscheidendes Attribut.
- ˋgraphˋ:
Wenn der Typ "graph" ist, wird das Element als Diagramm dargestellt. Dies ermöglicht eine Vielzahl von Visualisierungen wie Linien-, Balken-, Kuchen-, Donut- oder Flächendiagramme, abhängig vom graphType.
- ˋswiperˋ:
Der Typ "swiper" erstellt ein Karussell-ähnliches Element, das eine Reihe von anderen Elementen enthält, die durchgeblättert werden können. Jedes Element innerhalb des "swiper"-Typs wird genauso konfiguriert wie ein normales Dashboard-Element, was bedeutet, dass sie jeweils ihren eigenen type, title, etc. haben können.
- ˋreferenceˋ:
Die "reference"-Typ Elemente sind Verweise auf Collections.
- ˋcomparisonˋ:
Außerdem gibt es den type "comparison", wobei man ein timespan auswählt und dieser timespan und der timespan davor mit einander auf einen bestimmten Wert verglichen werden.
- ˋtableˋ:
Desweiteren gibt es einen type "table" wobei über das additionalApiParams Objekt mit dem limit attribut gesetzt wird wie viele Einträge von der ausgewählten collection Angezeigt werden sollen. Per Default sind es 5
- ˋsectionTitleˋ
Der "sectionTitle" type ist wie der name schon sagt, ein Titel für einen Abschnitt vom dashboard
## title
Der Titel eines Elements ist ein Objekt, das einen eval, value, contentBefore und contentAfter haben kann. value repräsentiert den Hauptteil des Titels, während contentBefore und contentAfter optionale Textstücke sind, die vor bzw. nach dem Haupttitel platziert werden. Eval kann als ersatz für value verwendet werden. Auch ist eine normale String angabe möchglich. Diese wird hier, sowie überall anders auch in die genutzte sprache konvertiert, daher ist die angabe über das {de:”xyz”, en:”xyz”} sinnvoll.
## subTitle
Dies ist ein Untertitel für das Dashboard-Element.
## additionalApiParams
Dies ist ein Objekt und kann folgende Parameter haben:
```ts
interface APIParams {
offset?: number
limit?: number
sort?: string
filter?: {
[key: string]: any
}
projection?: string
count?: 1
}
```
Sollte es komntextabhängig bereits automatisch genertierte Filter Objekt attirbute geben, so werden, wenn man welche angibt, seine eigenen über Object.assign zu diesen hinzu gefügt.
## subNavigation
Das Attrbiut bei type: reference dient dazu, den Link zu einer bestimmten subKategorie zu machen. Nimmt den index der gewüpnschten subNavigation als Wert.
## graphType
Das Attribut graphType bestimmt die spezifische Art der Datenvisualisierung für ein Element vom Typ "graph". Die möglichen Werte sind "line" (Linien-Diagramm), "bar" (Balkendiagramm), "donut" (Kreisdiagramm), “pie” (Kuchendiagramm) und "area" (Flächendiagramm).
## xAxis und yAxis
Diese Attribute definieren die Daten, die auf den Achsen des Diagramms angezeigt werden. xAxis immer "timeline", was bedeutet, dass die Daten über die Zeit dargestellt werden. yAxis kann “sum” oder “amount” sein. Sum summiert die Werte des angegebenen feldes im time interval, wohingegen amount die Menge des angegebenen Feldes im time interval aufsummiert und angibt. Möchte man die menge von den Kollektionen angeben, so ist kein weiterer schritt nötig. Möchte man angeben, wie oft ein feld in den kollektionen vor kommt, so hat man 2 möglichkeiten. Wenn das feld einmal pro kollektion vor kommt, so gibt man einfach über den field parameter den pfad dorthin an also bspw. wenn im root einfach nur xyz oder xyz.yxz.... Wenn das feld aber in einer object[] struktur ist, so gibt man den pfad dorthin an (über path) und dann ausgehend von dem objekt wird die field angabe genutzt. Bei sum ist das prinzip das selbe.
## containerProps
Um Felder auf breiten Bildschirmen eine schmalere Breite zu geben, wird das containerProps Attribut empfohlen. Es hat ein class Attribut, welches klassen ins HTML injiziert. Es gibt außerdem noch das Layout attribut mit breakBefore und breakAfter, welche dafür sorgen, dass vorher bzw. nachher keine weiteren HTML Elemente platz finden. Hier ist des weiteren das size Objekt drin, welches 3 attribute hat. Die attribute sollten col-1 bis col-12 beeinhalten, diese klassen werden ins html injiziert, können also dem zufolge auch misbraucht werden. Die klassen bei den attributen werden bei unterschiedlichen Bildschirmbreiten aktiv.
```yaml
containerProps:
#optional class prop
layout:
breakBefore: false
breakAfter: false
size:
default: "col-8"
small: "col-12"
large: "col-4"
```
## graphs
Ein Array von Objekten, wobei jedes Objekt ein einzelnes Diagramm darstellt, das innerhalb des Dashboard-Elements dargestellt wird. Bei meherer Angabe werden auch mehrere graphen im selben chart angezeigt, für unterschiedliche y Achsen im selben Chart ist multipleYAxes auf true zu setzen, hier ist dann auch die YAxisTitle vorgenommen. Jedes dieser Diagrammobjekte hat mehrere Attribute, darunter field,path, dateTimeField, collection und graphName. field gibt an, welches Datenfeld aus der Sammlung zur Erzeugung des Diagramms verwendet werden soll. Path gibt den weg zum feld an, wenn das feld direkt im objekt liegt, so ist “this” oder garkeine angabe valide. dateTimeField bestimmt das Feld, das die Zeitskala für das Diagramm liefert. collection ist der Name der Datenkollektion, aus der die Daten bezogen werden. Schließlich definiert graphName den Namen des Diagramms.
## style
Ein Objekt, das CSS-Stilinformationen für das Dashboard-Element enthält. Es ist für die reference elemente gedacht.
## collection
Dieses Feld bezieht sich auf die Datenquelle oder Sammlung, auf die das Dashboard-Element zugreifen wird.
## timeInterval
Dieses Feld definiert den Zeitraum, der im Diagramm angezeigt wird. Die möglichen Werte können "day", "month", "year" sein, je nachdem, welche Granularität für die Datenvisualisierung gewünscht wird.
## timespan
Der Zeitraum für die Vergleichsdaten. Es kann "QTD" für das aktuelle Quartal und das Quartal davor sein, "MTD" für den aktuellen Monat und den Monat davor sein oder YTD für das aktuelle Jahr und das Jahr davor sein. Immer nur bis zum heutigem Tag dieser Zeitspanne, also bspw. vom 1.1.2023 bis zum 28.5.2023 wäre YTD wobei das vergleichs Jahr dann vom 1.1.2022 bis zum 28.5.2022 wäre:
```js
switch (input) {
case "YTD": // Year to date
begin = new Date(date.getFullYear(), 0, 1)
end = currentDate
lastBegin = new Date(date.getFullYear() - 1, 0, 1)
lastEnd = new Date(date.getFullYear() - 1, date.getMonth(), date.getDate())
break
case "QTD": // Quarter to date
let quarterStartMonth = Math.floor(date.getMonth() / 3) * 3
begin = new Date(date.getFullYear(), quarterStartMonth, 1)
end = currentDate
lastBegin = new Date(date.getFullYear(), quarterStartMonth - 3, 1)
lastEnd = new Date(date.getFullYear(), date.getMonth() - 3, date.getDate())
break
case "MTD": // Month to date
begin = new Date(date.getFullYear(), date.getMonth(), 1)
end = currentDate
lastBegin = new Date(date.getFullYear(), date.getMonth() - 1, 1)
lastEnd = new Date(date.getFullYear(), date.getMonth() - 1, date.getDate())
break
}
```
## until
Dieses Feld definiert den Endpunkt des Zeitintervalls für die Datenvisualisierung. Mögliche Werte sind "lastWeek", “lastMonth”, “lastYear” und "allTime", welches jedoch default ist. Zum Beispiel, wenn until auf "lastMonth" gesetzt ist, wird das Diagramm maximal Daten bis zum letzten Monat anzeigen. Außerdem sind alle Optionen darunter für den Nutzer auswählbar. Um dies zu unterbinden, muss das filter attribut auf false gesetzt werden.
## defaultUntil
Dieses Attribut setzt den default Filter. Es ist anzumerken, dass mit jedem Filter change eine Neue Request einhergeht, und immer mit dem aktuellem Filter requested wird, dieser sollte also ggf. für perfomance per default niedrig gesetzt werden.
## filter
Wird filter auf false gesetzt, so ist der Filter nicht länger sichtbar.
## multipleYAxes
Dieses Boolean-Feld gibt an, ob das Diagramm mehrere Y-Achsen haben soll. Wenn auf true gesetzt, hat jedes Diagramm im graphs Array eine eigene Y-Achse haben. Hierfür ist dann eine Achsenbeschriftung sinnvoll. Diese wird dann mit yAxisTitle im jeweiligen Graphs objekt angegeben.
## graphBaseColor
Dies definiert die Basisfarbe des Diagramms. Die Farbe muss in einem gültigen CSS-Farbformat angegeben werden.
## value
Im Kontext des title-Feldes repräsentiert value den Hauptteil des Titels. In Bezug auf Diagramme, insbesondere bei Donut- und Kuchendiagrammen, repräsentiert value den numerischen Wert, der im Diagramm dargestellt wird. Hier sind mögliche werte: “total”, welches die Summe vom ausgewählten feld bis zu until ausgibt, “amount”, welches das gleiche macht, nur nicht summiert sondern zählt und “count” wobei die Einträge selbst gezählt werden.
## valueType
Wird bei "comparison" verwendet, um den vergleich relativ oder absolut vorzunehmen. Valide angaben sind: "relative" und "absolute".
## path
Dieses Feld kann verwendet werden, um den genauen Pfad zu der spezifischen Datenstruktur zu definieren, welches in der Sammlung für die Datenvisualisierung als referenz Array für den field parameter verwendet wird. Dies ist also sinvoll, wenn das Feld in einem Object[] ist, andernfalls ist bei field die angabe über xyz.yxz.zyx erwünscht. Ist das feld im root oder über den field parameter erreichbar, so kann man path leeer lassen oder "this" angeben.
## dateTimeField
Dies ist das Feld, das den Zeitstempel in der Datensammlung repräsentiert. Es wird verwendet, um die Zeitskala für das Diagramm zu liefern. Hier ist das erwünschte Datumsfeld in der collection auszuwählen.
## limit
gibt an wie viele Elemente maximal returned werden sollen.
## newPageRef
boolean, wenn explizit auf false, dann wird bei type reference kein Neue Seite hinzufügen button angezeigt.
# Code Beispiel
```yaml
dashboard:
majorItems: # Liste der Hauptelemente des Dashboards
- type: sectionTitle
title: "Overview" #i18n or eval
- type: swiper
containerProps:
class: "random-class"
layout:
breakBefore: false # no html elements before
breakAfter: false #no html elmenets after
size: #different classes depending on innerWidth
default: "col-8"
small: "col-12"
large: "col-4"
elements:
- type: comparison
collection: contact_form
timespan: "QTD" #QTD & MTD with last quarter, month or year
value: total
valueType: absolute #oder relative (prozentual)
path: paymentValues
additionalApiParams: #Nimmt APIParams entgegen und fügt sie zusätzlich ein, filter Objekt Parameter wird ggf. zum automatisch generierten Assigned, also vorsicht!
limit: 3
dateTimeField: Date
field: paymentValue
title: toller titel #string or eval
subTitle: { de: "YTD Vergleich zum Vorjahr", en: "YTD comparison with last year" }
- type: comparison
collection: contact_form
timespan: "QTD" #QTD & MTD with last quarter, month or year
value: total
valueType: absolute #oder relative (prozentual)
dateTimeField: Date
field: paymentValue
title: toller titel #i18n or eval
subTitle: { de: "YTD Vergleich zum Vorjahr", en: "YTD comparison with last year" }
- type: swiper
css:
graph:
wrapper:
eval: |
`
background-color: green ;
`
containerProps:
class: "random-class"
layout:
breakBefore: false # no html elements before
breakAfter: true #no html elmenets after
size: #different classes depending on innerWidth
default: "col-8"
small: "col-12"
large: "col-4"
elements:
- type: comparison
collection: contact_form
timespan: "QTD" #QTD & MTD with last quarter, month or year
value: total
valueType: relative #oder relative (prozentual)
path: paymentValues
dateTimeField: Date
field: paymentValue
title: toller titel #string or eval
subTitle: { de: "YTD Vergleich zum Vorjahr", en: "YTD comparison with last year" }
- type: comparison
collection: contact_form
timespan: "QTD" #QTD & MTD with last quarter, month or year
value: total
valueType: relative #oder relative (prozentual)
dateTimeField: Date
field: paymentValue
title: toller titel #string or eval
subTitle: { de: "YTD Vergleich zum Vorjahr", en: "YTD comparison with last year" }
- type: sectionTitle
title: "Details"
- type: graph # Art des Elements, hier ein Graph
css:
graph:
wrapper:
eval: |
`
background-color: yellow ;
`
title: # Titel des Graphen
#eval anstelle von value möglich
value: total #o. amount Haupttitel des Graphen
contentAfter: "€" # Nach dem Haupttitel hinzugefügte Inhalte
contentBefore: "xyz" # Vor dem Haupttitel hinzugefügte Inhalte
timeInterval: "day" # Zeitintervall der Daten im Graphen
until: "lastMonth" # Ende des Zeitintervalls (Ab dem aktuellen Datum)
graphType: "line" # Art des Graphen, hier ein Liniendiagramm
graphBaseColor: "#ff0000" # Basisfarbe des Graphen
subTitle: { de: "Umsatz", en: "sales volume" } # Untertitel des Graphen, mehrsprachig
xAxis: timeline # Art der x-Achse, hier eine Zeitachse
class: "random-class" #Class added
additionalApiParams:
limit: 2
containerProps:
#optional class prop
layout:
breakBefore: false
breakAfter: false
size:
default: "col-8"
small: "col-12"
large: "col-4"
graphs: # Liste der Graphen in diesem Element
- yAxis: sum # Art der y-Achse, hier eine Summe
field: paymentValue # Feld der Daten für den Graphen
dateTimeField: Date # Feld für den Zeitstempel der Daten
yAxisTitle: Graph titel # Titel der y-Achse
collection: contact_form # Sammlung, aus der die Daten stammen
graphName: { de: "Umsatz", en: "sales volume" } # Name des Graphen, mehrsprachig
- graphName: { de: "Umsatz anderes feldes", en: "Sum of other values" }
path: paymentValues # Pfad zu den Daten im Feld
yAxis: sum
dateTimeField: Date
field: paymentValue
collection: contact_form
- type: graph
title:
value: total
contentAfter: "€"
contentBefore: "xyz"
timeInterval: "day"
until: "lastMonth"
graphType: "line"
graphBaseColor: "#ff0000"
subTitle: { de: "Umsatz", en: "sales volume" }
xAxis: timeline
multipleYAxes: true # Option für mehrere y-Achsen
containerProps:
#optional class prop
layout:
breakBefore: false
breakAfter: false
size:
default: "col-8"
small: "col-12"
large: "col-4"
graphs:
- yAxis: sum
yAxisTitle: Summe nr 1
graphType: "bar" # Art des Graphen, hier ein Balkendiagramm
field: paymentValue
dateTimeField: Date
collection: contact_form
graphName: { de: "Umsatz", en: "sales volume" }
- graphName: { de: "Umsatz anderes feldes", en: "Sum of other values" }
path: paymentValues
yAxisTitle: Summe nr 2
yAxis: sum
graphType: "line"
dateTimeField: Date
field: paymentValue
collection: contact_form
- type: swiper # Art des Elements, hier ein Swiper
containerProps:
#optional class prop
layout:
breakBefore: false
breakAfter: false
size:
default: "col-8"
small: "col-12"
large: "col-4"
elements: # Liste der Elemente in diesem Swiper
- type: graph
title:
value: total
contentAfter: "€"
contentBefore: "xyz"
until: "lastMonth"
graphType: "donut" # Art des Graphen, hier ein Donut-Diagramm
value: total # Summe aller werte in spezifiziertem Feld, welche dann im Diagramm dargestellt werden
graphBaseColor: "#ff0000"
subTitle: { de: "Umsatz", en: "sales volume" }
graphs:
- field: paymentValue
dateTimeField: Date
collection: contact_form
graphName: { de: "Umsatz", en: "sales volume" }
- graphName: { de: "Umsatz anderes feldes", en: "Sum of other values" }
path: paymentValues
dateTimeField: Date
field: paymentValue
collection: contact_form
- type: graph
title:
value: total
contentAfter: "€"
contentBefore: "xyz"
until: "lastMonth"
graphType: "pie" # Art des Graphen, hier ein Kuchendiagramm
value: total
graphBaseColor: "#ff0000"
subTitle: { de: "Umsatz", en: "sales volume" }
graphs:
- field: paymentValue
dateTimeField: Date
collection: contact_form
graphName: { de: "Umsatz", en: "sales volume" }
- graphName: { de: "Umsatz anderes feldes", en: "Sum of other values" }
path: paymentValues
dateTimeField: Date
field: paymentValue
collection: contact_form
- containerProps:
#optional class prop
layout:
breakBefore: false
breakAfter: false
size:
default: "col-8"
small: "col-12"
large: "col-4"
type: graph
title:
value: total
contentAfter: "€"
subTitle: { de: "Umsatz", en: "sales volume" }
xAxis: timeline
timeInterval: "day"
graphType: "area" # Art des Graphen, hier ein Flächendiagramm
graphs:
- field: paymentValue
dateTimeField: Date
yAxis: sum
collection: contact_form
path: "this" # Pfad zu den Daten im Feld, hier das aktuelle Objekt, keine Angabe hat den gleichen Wert
graphName: { de: "Umsatz", en: "sales volume" }
- type: swiper
containerProps:
#optional class prop
layout:
breakBefore: false
breakAfter: true
size:
default: "col-8"
small: "col-12"
large: "col-4"
elements:
- class: col-6
type: graph
subTitle: { de: "Produktmenge", en: "Amount of products" }
xAxis: timeline
timeInterval: "day"
filter: false #deaktiviert die Filter möglichkeit für den Nutzer beim diagramm, normalerweise aktiviert. Hierbei sind alle kombinationen x >= until möglich
dateTimeField: Date
until: "lastMonth"
graphType: "bar"
graphs:
- graphName: { de: "Menge", en: "Amount" }
yAxis: amount # Art der y-Achse, hier die Anzahl von allen Feldern im spezifiziertem intervall
dateTimeField: Date
collection: contact_form
- class: col-8
type: graph
filter: false
title:
value: total
contentAfter: "€"
subTitle: { de: "Umsatz", en: "sales volume" }
xAxis: timeline
timeInterval: "day"
dateTimeField: Date
graphType: "line"
until: "lastMonth"
graphs:
- field: paymentValue
yAxis: sum # Art der y-Achse, hier eine Summe
collection: contact_form # Sammlung, aus der die Daten stammen
dateTimeField: Date # Feld für den Zeitstempel der Daten
path: "this" # Pfad zu den Daten im Feld, hier das aktuelle Objekt
graphName: { de: "Umsatz", en: "sales volume" } # Name des Graphen, mehrsprachig
- type: table #shows entries from specified collection
containerProps:
#optional class prop
layout:
breakBefore: false
breakAfter: true
size:
default: "col-8"
small: "col-12"
large: "col-7"
collection: contact_form
title: Konaktformular Titel
subTitle: subtitel oder so
additionalApiParams:
limit: 3
- collection: content # Sammlung, aus der die Daten für das nächste Element stammen
type: reference # Art des Elements, hier ein Referenz-Element
style: # Stil des Elements
upper: rgba(3, 50, 59, 0.7) # Farbe des oberen Teils
lower: rgba(3, 50, 59) # Farbe des unteren Teils
- collection: content # Wiederholung der vorherigen Elemente
type: reference
style:
upper: rgba(3, 50, 59, 0.7)
lower: rgba(3, 50, 59)
- collection: contact_form
subNavigation: 1
type: reference
style:
upper: rgba(3, 50, 59, 0.7)
lower: rgba(3, 50, 59)
minorItems: # Liste der Nebenelemente des Dashboards
- collection: contact_form # Referenz auf collections
subNavigation: 0
- collection: contact_form # Wiederholung der vorherigen Nebenelemente
subNavigation: 0
- collection: contact_form
subNavigation: 0
- collection: contact_form
subNavigation: 0
- collection: contact_form
subNavigation: 0
- collection: contact_form
newPageRef: false
```
![Resultierende Dashboard](./dashboard.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

View File

@ -0,0 +1,13 @@
# jobs
In dem ein oder anderen Projekt werden sicherlich Jobs im Hintergrund benötigt, die zu bestimmten Zeiten oder Intervallweise ausgeführt werden müssen (z.B. Datenbereinigung). Diese Jobs können innerhalb der [config.yml](./config.yml.md) definiert werden.
Wie in allen YAML-Definitionen können auch die Jobs via `!include` ausgelagert werden.
Der Aufbau eines Jobs ausgelagert in einer Datei sieht beispielsweise folgendermaßen aus:
!!!include(../api/jobs/lighthouse.yml)!!!
Die Möglichkeiten innerhalb der Javascript-Datei werden im Kapitel [Javascript Kontext](./../server-javascript-kontext/allgemeines.md) beschrieben. Ein Beispiel für eine solche Datei wäre die folgende:
!!!include(../api/jobs/lighthouse.js)!!!

View File

@ -0,0 +1,65 @@
# Ordnerstruktur
Als Konvention für neue Projekte hat sich folgende Ordnerstruktur etabliert:
![Ordnerstruktur](api-ordner.png)
Die Aufteilung der YAML-Konfiguration ist durch den YAML-Tag `!include` möglich. Genaueres dazu wird auf den nachfolgenden Seiten beschrieben.
## /api
Der Einstiegsordner in die Konfiguration ist frei wählbar. "/api" innerhalb des Projektrepositories hat sich jedoch bewährt.
Die Einstiegsdatei in die Gesamt-Konfiguration liegt hier und heißt [config.yml](./config.yml.md). In dieser können Umgebungsvariablen erstetzt werden, welche in [config.yml.env](./config.yml.md) definiert sind.
Ebenso sind alle nachfolgenden Unterordner beliebig zu benennen. Da aber ein JSON-Schema und VSCode-Konfiguration zur Validierung der YAML Dateien existiert, ist folgende Struktur hilfreich.
### JSON-Schema
Das JSON-Schema ist in die package.json einzubinden via:
```json
...
"devDependencies": {
...,
"tibi-types": "https://gitbase.de/cms/tibi-types.git"
},
...
```
Die im Projekt liegende VSCode-Konfig sollte dementsprechend ergänzt werden:
```json
...
"yaml.schemas": {
"node_modules/tibi-types/schemas/api-config/config.json": "api/config.y*ml",
"node_modules/tibi-types/schemas/api-config/collection.json": "api/collections/*.y*ml",
"node_modules/tibi-types/schemas/api-config/field.json": "api/collections/fields/*.y*ml",
"node_modules/tibi-types/schemas/api-config/fieldArray.json": "api/collections/fieldLists/*.y*ml"
},
"yaml.customTags": ["!include scalar"],
...
```
Sollte Yarn2 verwendet werden ist die Verlinkung von **node_modules** nötig. Dazu ist folgendes in der **.yarnrc.yml** einzutragen:
```yaml
...
nodeLinker: node-modules
```
## /api/collections
Bei Aufteilung der Kollektionskonfigurationen in einzelne Dateien, sollten diese in diesem Ordner gespeichert werden. Für jede Kollektion sollte eine eigene Datei verwendet werden, hier im Beispiel [api/collections/democol.yml](./collections.md).
### /api/collections/fields
Sollten Feldkonfigurationen wieder verwendet werden, können diese im [api/collections/fields/](./collections/fields.md) Unterordner gepeichert werden. Diese sind pro Feldkonfiguration als einzelne Datei aufzuführen.
### /api/hooks
Jede Javascript-Datei, die einen Hook bedient sollte im Unterordner benannt nach der Kollektion im Ordner [api/hooks/](./collections/hooks.md) sein. Der Name der Datei sollte sich nach den Hook richten. Z.B. **get_return.js** ist zustängig für den GET-Hook nach dem Lesen der Daten, bevor diese zurück gegeben werden. Mehr dazu unter [Hooks](./collections/hooks.md).
### /api/templates
Ist es nötig im Projekt Templates zu rendern (z.B. für den Email-Versand), sind diese im Ordner **templates** gut aufgehoben.

27
docs/md/restapi/assets.md Normal file
View File

@ -0,0 +1,27 @@
# /assets
Die /assets-API ist dazu gedacht, den Zugriff auf bestimmte Ordnerpfade zu ermöglichen, die direkt über den Tibi-Server erreichbar sind. Diese Pfade werden in der Konfigurationsdatei (config.yml) definiert und relativ zu dieser Datei interpretiert. Jeder dieser Pfade wird durch einen eindeutigen Namen identifiziert, der in der URL verwendet wird.
## URL-Struktur
Die Struktur der URL für den Zugriff auf die Assets ist wie folgt:
- TIBI-SERVER-URL/api/v1/_/NAMESPACE/_/assets/NAME/
Hierbei steht NAME für den in der Konfigurationsdatei festgelegten Namen für den Pfad. Wenn beispielsweise ein Pfad mit dem Namen _dist_ definiert ist, der auf den Ordner ../frontend/_dist_ relativ zur config.yml zeigt, würde die entsprechende URL so aussehen:
- TIBI-SERVER-URL/api/v1/_/NAMESPACE/_/assets/_dist_/
## Zugriffsmethode
Über die /assets-API ist ausschließlich ein unbeschränkter Lesezugriff (GET-Methode) möglich. Dies bedeutet, dass Sie über diese API Dateien aus den definierten Pfaden abrufen können, aber keine Änderungen vornehmen oder Dateien hochladen können.
Konfigurationsbeispiel
In der config.yml könnten Sie einen Asset-Pfad wie folgt definieren:
```yaml
name: _dist_
path: ../frontend/_dist_
```
Dies würde den Zugriff auf Dateien im Ordner ../frontend/_dist_ relativ zur config.yml über die URL TIBI-SERVER-URL/api/v1/_/NAMESPACE/_/assets/_dist_/ ermöglichen.

View File

@ -0,0 +1,239 @@
# `/_/NAMESPACE/COLLECTION`
Dieser Endpoint ermöglicht Interaktionen mit den Collectionen, die flexible Strukturen zur Organisation und Kategorisierung von Daten darstellen. Sie können einen Collectioneintrag abrufen, aktualisieren, erstellen und löschen. Jede Collection wird durch einen eindeutigen Namespace und Namen identifiziert.
## GET /{namespace}/{collection}
Diese Anforderung ruft alle Einträge einer bestimmten Collection ab. Sie nimmt den Namespace und den Namen der Collection sowie optionale API-Parameter für die Anpassung der Anfrage als Parameter an.
### Antwort
Die Antwort ist ein Objekt mit folgenden Eigenschaften:
- `data`: Ein Array von CollectionEntry Objekten.
- `count`: Die Gesamtzahl der Einträge.
```ts
interface Collection {
name: string
meta?: CollectionMeta
permissions?: {
public?: CollectionPermission
user?: CollectionPermission
[token: string]: CollectionPermission
}
projections?: {
[projectionName: string]: {
select: {
[field: string]: 1 | 0
}
}
}
fields: CollectionField[]
}
```
## GET /{namespace}/{collection}/{id}
Diese Anforderung ruft einen bestimmten Eintrag in einer Collection ab. Sie nimmt den Namespace und Namen der Collection sowie die ID des abzurufenden Eintrags als Parameter an.
### Anforderungsparameter
- `id`: Die ID des abzurufenden Eintrags.
### Antwort
Die Antwort ist das entsprechende CollectionEntry Objekt.
## PUT /{namespace}/{collection}/{id}
Diese Anforderung aktualisiert die Daten eines vorhandenen Eintrags in einer Collection. Sie nimmt den Namespace und den Namen der Collection, die ID des zu aktualisierenden Eintrags und ein Änderungssatz-Objekt, das die zu aktualisierenden Daten enthält, als Parameter an.
### Anforderungsparameter
id: Die ID des zu aktualisierenden Eintrags.
data: Ein Änderungssatz-Objekt, das die zu aktualisierenden Daten enthält.
### Antwort
Die Antwort ist das aktualisierte CollectionEntry Objekt.
## POST /{namespace}/{collection}
Diese Anforderung erstellt einen neuen Eintrag in einer Collection. Sie nimmt den Namespace und den Namen der Collection und ein Datenobjekt, das die Informationen des neuen Eintrags enthält, als Parameter an. Optional kann eine Funktion für den Fortschritt des Uploads übergeben werden.
### Anforderungsparameter
- `data`: Ein Datenobjekt, das die Informationen des neuen Eintrags enthält.
### Antwort
Die Antwort ist das neu erstellte CollectionEntry Objekt.
## DELETE /{namespace}/{collection}/{id}
Diese Anforderung löscht einen vorhandenen Eintrag in einer Collection. Sie nimmt den Namespace und den Namen der Collection und die ID des zu löschenden Eintrags als Parameter an.
### Anforderungsparameter
- `id`: Die ID des zu löschenden Eintrags.
### Antwort
Die Antwort ist ein boolean, das true zurückgibt, wenn das Löschen erfolgreich war.
Jede Collection besteht aus mehreren Feldern (CollectionField), die verschiedene Datenpunkte repräsentieren. Jedes Feld hat einen Namen, einen Typ und ggf. eine Reihe von Subfeldern. Die Metadaten eines Feldes (CollectionFieldMeta) können zusätzliche Informationen über das Feld enthalten, wie z.B. ein Label, Hilfstexte, Widget-Typen, InputProps und mehr.
Eine Collection kann auch Metadaten (CollectionMeta) enthalten, die Informationen über die Collection selbst enthalten.
```ts
interface CollectionNavigation {
label?: I18Text
muiIcon?: string
defaultSort?: {
field: string
order?: "ASC" | "DESC" | "MANUALLY"
}
defaultImageFilter?: string
views?: View[]
filter?: { [key: string]: any }
defaultCallback?:
| "edit"
| "view"
| {
eval: string
}
}
interface CollectionMeta extends CollectionNavigation {
singleton: boolean
rowIdentTpl?:
| string
| {
twig: string
}
subNavigation?: CollectionNavigation[]
tablist?: {
activeTab?: string
tabs: CollectionMetaTab[]
}
[key: string]: any
}
interface CollectionMetaTab {
name: string
label: I18Text
dependsOn?: DependsOn
subFields: {
source: string
name?: string
}[]
_resolvedSubFields?: CollectionField[]
_hide?: boolean
}
interface EntryViewTab {
name: string
meta: {
[key: string]: any
}
}
interface CollectionPermission {
methods?: MethodPermission
filter?: any
validProjections?: string[]
}
interface CollectionField {
name: string
type?: string
index?: ("single" | "unique")[]
meta?: CollectionFieldMeta
subFields?: CollectionField[]
}
type CssWithEval = string | { [key: string]: string | number } | { eval: string }
interface ContainerProps {
class?: string
style?: string
layout?: {
breakBefore: boolean
breakAfter: boolean
size: {
default: string
small: string
large: string
}
}
}
interface CollectionFieldMeta {
source?: string
label?: I18Text
helperText?: I18Text
widget?: string
valueMap?: {
[value: "string" | number]: {
name?: I18Text
muiIcon?: string
style?: string
}
}
containerProps?: ContainerProps
choices?: ArrayFieldChoice[] | EndpointOptions
filter?: boolean | FilterConfig
defaultValue?: DefaultValue
addAllowed?: boolean
props?: {
step?: string | number
[key: string]: string | number | boolean
}
inputProps?: {
step?: string | number
[key: string]: any
}
dependsOn?: DependsOn
css?: {
input?: {
wrapper?: CssWithEval
element?: CssWithEval
foreignEntry?: CssWithEval
}
view?: {
wrapper?: CssWithEval
element?: CssWithEval
foreignEntry?: CssWithEval
}
filter?: {
wrapper?: CssWithEval
element?: CssWithEval
foreignEntry?: CssWithEval
}
}
folding?: {
unfolded?: boolean
previewFolded?:
| string
| {
eval: string
raw?: boolean
}
previewUnfolded?:
| string
| {
eval: string
raw?: boolean
}
}
[key: string]: any
}
```

63
docs/md/restapi/login.md Normal file
View File

@ -0,0 +1,63 @@
# `/login`
## POST /login
Dieser Endpoint ermöglicht es Benutzern, sich in das System einzuloggen. Dabei wird eine Authentifizierung durchgeführt und bei erfolgreicher Authentifizierung ein Token zurückgegeben, der für nachfolgende API-Aufrufe verwendet wird.
## Anforderungsparameter
Der /login Endpoint erwartet folgende Daten im Body:
- `username`: Der Benutzername des Benutzers, der sich anmelden möchte. Typ: String.
- `password`: Das Passwort des Benutzers, der sich anmelden möchte. Typ: String.
Die Daten müssen als LoginData Objekt übergeben werden.
```ts
const loginData: LoginData = {
username: "IhrBenutzername",
password: "IhrPasswort",
}
```
## Antwort
Bei erfolgreicher Anmeldung gibt der /login Endpoint ein LoginResponse Objekt zurück. Dieses Objekt enthält:
- `token`: Ein Authentifizierungstoken, das für nachfolgende API-Aufrufe verwendet wird. Typ: String.
- `user`: Ein User Objekt, das Informationen über den eingeloggten Benutzer enthält.
```ts
interface User {
id: string // Eindeutige ID des Benutzers
insertTime: string // Zeitpunkt der Erstellung des Benutzerkontos
updateTime: string // Letzter Zeitpunkt der Aktualisierung des Benutzerkontos
username: string // Benutzername des Benutzers
role: number // Rolle des Benutzers im System (0=admin, 1 = editor, 2 = user)
permissions: any[] // Berechtigungen des Benutzers
meta: {
// Weitere optionale Benutzerinformationen
[key: string]: any
}
}
```
## Beispielaufruf
```ts
const loginData: LoginData = {
username: "IhrBenutzername",
password: "IhrPasswort",
}
postLogin(loginData)
.then((response) => {
console.log("Erfolgreiche Anmeldung! Token: ", response.token)
console.log("Benutzerinformationen: ", response.user)
})
.catch((error) => {
console.log("Fehler beim Login: ", error)
})
```

View File

@ -0,0 +1,99 @@
# `/project`
Dieser Endpoint bietet eine Schnittstelle für den Zugriff und die Manipulation von Projektdaten. Benutzer können Projekte erstellen, abrufen, aktualisieren und löschen.
## GET /project
Mit dieser Funktion können Sie eine Liste aller Projekte abrufen. Diese Funktion nimmt optionale Parameter an, die verwendet werden können, um die abgerufenen Projekte zu sortieren oder zu filtern.
Diese request ruft eine Liste aller Projekte ab. Sie kann optionale Parameter verwenden, um die abgerufenen Projekte zu sortieren oder zu filtern.
### Antwort
Die Antwort auf diese request ist ein Objekt mit zwei Eigenschaften:
- `data`: Ein Array von Projekt-Objekten.
- `count`: Die Gesamtzahl der Projekte die es gibt.
Jedes Projekt-Objekt hat die folgenden Eigenschaften:
```ts
interface Project {
id?: string // Eindeutiger Identifikator für das Projekt
insertTime?: string // Zeitpunkt der Erstellung des Projekts
updateTime?: string // Letzter Zeitpunkt der Aktualisierung des Projekts
configFile: string // Pfad zur config.yml des Projekts
name: string // Name des Projekts
description: string // Beschreibung des Projekts
users?: string[] // Array von Benutzer-IDs mit Zugriff auf das Projekt
api?: ProjectAPI // Zusätzliche Projektinformationen
yourPermissions?: {
// Berechtigungen des aktuellen Benutzers für das Projekt
[collectionName: string]: MethodPermission
}
}
```
## PUT /project/{id}
Diese Anforderung aktualisiert ein vorhandenes Projekt. Sie nimmt die ID des zu aktualisierenden Projekts und ein Änderungsset als Parameter an. Das Änderungsset ist ein Objekt, das die zu ändernden Eigenschaften und ihre neuen Werte enthält.
Anforderungsparameter
- `id`: Die ID des zu aktualisierenden Projekts.
- `data`: Ein Objekt, das die zu ändernden Eigenschaften und ihre neuen Werte enthält.
### Antwort
Die Antwort ist das aktualisierte Project Objekt.
## DELETE /project/{id}
Diese Anforderung löscht ein vorhandenes Projekt. Sie nimmt die ID des zu löschenden Projekts als Parameter an.
### Anforderungsparameter
- `id`: Die ID des zu löschenden Projekts.
### Antwort
Die Antwort ist ein Objekt mit einer message Eigenschaft, die eine Bestätigungsnachricht enthält.
## POST /project
Diese Anforderung erstellt ein neues Projekt. Sie nimmt ein Objekt als Parameter an, das die Eigenschaften des zu erstellenden Projekts enthält.
Anforderungsparameter
- `data`: Ein Objekt vom typ Project.
### Antwort
Die Antwort ist ein Objekt mit einer message Eigenschaft, die eine Bestätigungsnachricht enthält.
```ts
interface MethodPermission {
get: boolean
post: boolean
put: boolean
delete: boolean
}
interface ProjectPermission {
name: string
label: I18Text
}
type ProjectImageUrl = string | EvalObject
interface ProjectAPI {
isOnline: boolean
namespace: string
meta?: {
imageUrl?: ProjectImageUrl
permissions?: ProjectPermission[]
dashboard?: Dashboard
[key: string]: any
}
collections: Collection[]
}
```

81
docs/md/restapi/user.md Normal file
View File

@ -0,0 +1,81 @@
# `/user`
Dieser Endpoint bietet eine Schnittstelle für den Zugriff und die Manipulation von Benutzerdaten. Sie können Benutzerinformationen abrufen, aktualisieren, erstellen und löschen. Jeder Benutzer wird durch eine eindeutige Benutzer-ID identifiziert.
## GET /user
Diese Anforderung ruft eine Liste aller Benutzer ab. Sie kann optionale Parameter verwenden, um die abgerufenen Benutzerdaten zu sortieren oder zu filtern.
### Antwort
Die Antwort auf diese Anforderung ist ein Users Objekt mit folgenden Eigenschaften:
- `data`: Ein Array von User Objekten.
- `count`: Die Gesamtzahl der Benutzer.
Jedes User Objekt hat die folgenden Eigenschaften:
```ts
interface User {
id: string // Eindeutiger Identifikator des Benutzers
insertTime: string // Zeitpunkt der Erstellung des Benutzers
updateTime: string // Letzter Zeitpunkt der Aktualisierung des Benutzers
username: string // Benutzername des Benutzers
role: number // Rolle des Benutzers, repräsentiert durch eine Zahl
permissions: any[] // Array von Berechtigungen des Benutzers
meta: {
// Zusätzliche Informationen über den Benutzer
[key: string]: any
}
}
```
## GET /user/{id}
Diese Anforderung ruft einen bestimmten Benutzer ab. Sie nimmt die ID des abzurufenden Benutzers als Parameter an.
Anforderungsparameter
- `id`: Die ID des abzurufenden Benutzers.
### Antwort
Die Antwort ist das entsprechende User Objekt.
## POST /user
Diese Anforderung erstellt einen neuen Benutzer. Sie nimmt ein Objekt als Parameter an, das die Eigenschaften des zu erstellenden Benutzers enthält.
### Anforderungsparameter
- `data`: Ein Objekt, das die Eigenschaften des zu erstellenden Benutzers enthält.
### Antwort
Die Antwort ist ein Objekt, das das neu erstellte User Objekt enthält.
## PUT /user/{id}
Diese Anforderung aktualisiert die Daten eines vorhandenen Benutzers. Sie nimmt die ID des zu aktualisierenden Benutzers und ein Objekt, das die zu aktualisierenden Daten enthält, als Parameter an.
### Anforderungsparameter
- `id`: Die ID des zu aktualisierenden Benutzers.
- `data`: Ein Objekt, das die zu aktualisierenden Daten enthält.
### Antwort
Die Antwort ist ein Objekt, das das aktualisierte User Objekt enthält.
## DELETE /user/{id}
Diese Anforderung löscht einen vorhandenen Benutzer. Sie nimmt die ID des zu löschenden Benutzers als Parameter an.
### Anforderungsparameter
`id`: Die ID des zu löschenden Benutzers.
### Antwort
Die Antwort ist ein boolean, das true zurückgibt, wenn das Löschen erfolgreich war.

View File

@ -0,0 +1,71 @@
Das HookContext-Interface bietet ein zentrales Objekt, um Zugang zu verschiedenen Attributen und Paketen zu erhalten. Es wird als context in den Hooks des tibi-server genutzt und stellt wichtige Funktionalitäten zur Verfügung, die zur Manipulation der Daten und Abläufe in den HTTP-Methoden und Schritten der API benötigt werden.
Das HookContext-Interface setzt sich aus mehreren Schnittstellen zusammen, darunter GetHookData, GetHookGetOnlyData, PostHookData, JobData, request und mehrere third-party Pakete. Hierbei sind mit ausnahme von den request und den third-party-paketen attributen alle attribute direkt auf dem context Objekt vorhanden. Request ist eine funktion die ihre Objekte zurückgibt und ist somit rechenaufwendig. Daher ist es ratsam bei mehrfacher nutzung des request attributs, sich den Wert in einer variable zwischen zu speichern. Die Attribute auf den paketen werden über context.paketname.attribut aufgerufen.
## GetHookData
Das GetHookData-Interface enthält Attribute, die spezifisch für die Manipulation von GET-Anfragen in den Hooks sind:
- `filter`: Ein Filter-Objekt, das die Anfragebedingungen für die zu abrufenden Dokumente enthält.
- `selector`: Ein Selektor-Objekt, das festlegt, welche Felder in den zurückgegebenen Dokumenten enthalten sein sollten.
- `offset`: Ein numerischer Wert, der den Startpunkt für die Rückgabe von Dokumenten festlegt.
- `limit` Ein numerischer Wert, der die maximale Anzahl von Dokumenten festlegt, die zurückgegeben werden sollen.
- `sort`: Ein String oder ein Array von Strings, der die Sortierreihenfolge der zurückgegebenen Dokumente bestimmt.
- `pipelineMod`: Eine Funktion, die es ermöglicht, die MongoDB-Abfragepipeline zu modifizieren.
## PostHookData Interface
Das PostHookData-Interface enthält ein Attribut, das speziell für POST- und PUT-Hooks relevant ist:
`data`: Das Dokument oder die Daten, die gesendet wurden. Dies ist ein CollectionDocument-Objekt und wird in die Datenbank geschrieben. Will man den Datenbankeinträge im hook also modifizieren, so muss dieses Objekt modifiziert werden. Mann muss dieses Objekt idealerweise also einfach modifiziert in dem HookResponse data attribut abspeichern, dies wird nämlich zwischen allen Hooks immer ausgetauscht. Demzufolge ist das veränderte data Objekt dann auch so im nächsten Hook verfügbar.
## JobConfig und JobData Interfaces
Das JobConfig-Interface definiert die Konfiguration für einen Cron-Job, diese ist jene Konfig die im zugehörigen yaml definiert wurde:
- `meta`: Ein Metadatenobjekt.
- `cron`: Ein String, der die Cron-Job-Intervallspezifikation enthält.
- `type`: Der Typ des Jobprogramms. Derzeit wird nur "javascript" unterstützt.
- `file`: Der Pfad zur Datei des Jobprogramms.
Das JobData-Interface enthält ein job-Attribut, das ein JobConfig-Objekt ist und die Konfiguration des aktuellen Jobs repräsentiert.
## request
Die request Funktion liefert ein Objekt zurück, das Informationen über die HTTP-Anfrage enthält:
- `method`: Die HTTP-Methode der Anfrage (GET, POST, PUT, DELETE etc.).
- `remoteAddr`: Die remote IP-Adresse der Anfrage.
- `clientIp()`: Funktion zum Abrufen der Client-IP-Adresse der Anfrage.
- `host`: Der Host der Anfrage.
- `url`: Die vole URL der Anfrage.
- `path`: Der Pfad der Anfrage.
- `param(p: string)`: Funktion zum Abrufen von URL-Parametern.
- `query(q: string`: Funktion zum Abrufen von URL-Abfrageparametern.
- `header(h: string)`: Funktion zum Abrufen von HTTP-Headern.
- `body()`: Funktion zum Abrufen des HTTP-Body.
- `bodyBytes()`: Funktion zum Abrufen des HTTP-Body als byte array. Wird z.B. für die Umwandlung von iso8859 zu utf8 genutzt.
## Pakete
Jedes der folgenden Attribute ist ein Paket, das spezifische Funktionen bereitstellt:
- `db`: Stellt Funktionen zur Interaktion mit der Datenbank zur Verfügung (DbPackage).
- `smtp`: Bietet Funktionen zum Senden von E-Mails (SmtpPackage).
- `fs`: Bietet Funktionen zum Interagieren mit dem Dateisystem (FsPackage).
- `tpl`: Stellt Funktionen zum Arbeiten mit Templates zur Verfügung (TplPackage).
- `http`: Bietet Funktionen zum Senden von HTTP-Anfragen (HttpPackage).
- `debug`: Stellt Funktionen zum Debuggen zur Verfügung (DebugPackage).
- `response`: Bietet Funktionen zur Manipulation der HTTP-Antwort (ResponsePackage).
- `user`: Bietet Funktionen zum Arbeiten mit Benutzern (UserPackage).
- `bcrypt`: Stellt Funktionen zum Hashen und Überprüfen von Passwörtern zur Verfügung (BcryptPackage)`
- `jwt`: Bietet Funktionen zum Arbeiten mit JSON Web Tokens (JwtPackage).
- `charset`: Bietet Funktionen zur Arbeit mit Zeichensätzen (CharsetPackage).
- `image`: Bietet Funktionen zur Arbeit mit Bildern (ImagePackage).
- `xml`: Stellt Funktionen zum Arbeiten mit XML-Daten zur Verfügung (XmlPackage).
- `cookie`: Bietet Funktionen zum Arbeiten mit Cookies (CookiePackage).
- `pdf`: Bietet Funktionen zum Arbeiten mit PDF-Dokumenten (PdfPackage).
- `crypt`: Bietet Funktionen für Kryptografie (CryptoPackage).
- `json`: Bietet Funktionen zum Arbeiten mit JSON-Daten (JsonPackage).
Diese Pakete erweitern die Möglichkeiten des Context Objektes und ermöglichen es, eine Vielzahl von Aufgaben auszuführen. Für detaillierte Informationen zu jedem Paket, siehe die spezifische Dokumentation des jeweiligen Pakets.

Some files were not shown because too many files have changed in this diff Show More