Compare commits

...

No commits in common. "main" and "feature/wpf_client_part_1" have entirely different histories.

129 changed files with 14031 additions and 2 deletions

436
.gitignore vendored Normal file
View File

@ -0,0 +1,436 @@
Skip to content
Pull requests
Issues
Codespaces
Marketplace
Explore
@danielschick
github /
gitignore
Public
Fork your own copy of github/gitignore
Code
Pull requests 371
Actions
Security
Insights
Beta Try the new code view
gitignore/VisualStudio.gitignore
@n0099
n0099 [VisualStudio.gitignore] remove a trailing space
Latest commit 491040e Jan 26, 2022
History
165 contributors
@shiftkey
@arcresu
@aroben
@bbodenmiller
@HassanHashemi
@haacked
@niik
@AArnott
@sayedihashimi
@saschanaz
@bdougie
@OsirisTerje
398 lines (319 sloc) 6.7 KB
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp

26
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,26 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Flask",
"type": "python",
"request": "launch",
"module": "flask",
"env": {
"FLASK_APP": "src/server/BreCal",
"FLASK_DEBUG": "1",
"SECRET_KEY" : "zdiTz8P3jXOc7jztIQAoelK4zztyuCpJ" // https://randomkeygen.com/
},
"args": [
"run" //,
// "--no-debugger",
//"--no-reload"
],
"jinja": true,
"justMyCode": true
}
]
}

View File

@ -1,2 +1,23 @@
# brecal
Bremen calling
# Bremen Calling
___
Projekt zur verbesserten Kommunikation der maritimen Partner bei Schiffsanläufen in Bremen.
## Anforderungen
## Architektur
Die Architektur besteht aus einer Datenbank und einem in Python implementierten Backend, das eine API zu Verfügung stellt. Diese API ist als OpenAPI 3.0 spezifiziert.
Die Anwendung selbst kommuniziert nur über diese API mit der Datenbank. Es sind damit unterschiedliche Anwendungsplattformen denkbar, etwa eine Web-, Mobile- oder Windows Desktop Anwendung.
In dieser [Folie](docs/Architektur.pptx) ist ein Bild / Überblick enthalten.
Ein erster Gedanke des Datenbank-Layouts sieht folgendermaßen aus:
![image](docs/datenbank.jpeg)
## Entwicklung
### Postman
Zum Debuggen der Flask App verwende ich dieses Tutorial:
https://code.visualstudio.com/docs/python/tutorial-flask#_create-a-project-environment-for-the-flask-tutorial

Binary file not shown.

BIN
docs/AMPELKONZEPT_V1.docx Normal file

Binary file not shown.

BIN
docs/Ablaufplan.pptx Normal file

Binary file not shown.

BIN
docs/Arbeitspakete.xlsx Normal file

Binary file not shown.

BIN
docs/Architektur.pptx Normal file

Binary file not shown.

BIN
docs/BremenCalling.pptx Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
docs/Gesamtansicht.pptx Normal file

Binary file not shown.

BIN
docs/UserStories.xlsx Normal file

Binary file not shown.

BIN
docs/datenbank.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,426 @@
{
"info": {
"_postman_id": "9242b2d1-196b-4b2e-af57-c0e9eb141dba",
"name": "BreCal",
"description": "Bremen Calling relevant API calls",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"_exporter_id": "10427908"
},
"item": [
{
"name": "Login user",
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.environment.set(\"LOGON_TOKEN\", responseBody)"
],
"type": "text/javascript"
}
}
],
"request": {
"auth": {
"type": "noauth"
},
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"username\" : \"Londo\",\r\n \"password\" : \"Hallowach\"\r\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{SCHEMA}}{{PATH}}/login",
"host": [
"{{SCHEMA}}{{PATH}}"
],
"path": [
"login"
]
}
},
"response": []
},
{
"name": "Participant GET",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{LOGON_TOKEN}}",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"url": {
"raw": "{{SCHEMA}}{{PATH}}/participant?user_id=1",
"host": [
"{{SCHEMA}}{{PATH}}"
],
"path": [
"participant"
],
"query": [
{
"key": "user_id",
"value": "1"
}
]
}
},
"response": []
},
{
"name": "Shipcalls GET",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{LOGON_TOKEN}}",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"url": {
"raw": "{{SCHEMA}}{{PATH}}/shipcalls",
"host": [
"{{SCHEMA}}{{PATH}}"
],
"path": [
"shipcalls"
]
}
},
"response": []
},
{
"name": "Shipcalls POST",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{LOGON_TOKEN}}",
"type": "string"
}
]
},
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"ship_id\" : 1,\r\n \"type\" : 1,\r\n \"eta\" : \"2023-07-23T07:18:19\",\r\n \"voyage\" : \"43B\",\r\n \"tug_required\" : false,\r\n \"pilot_required\" : true,\r\n \"flags\" : 0,\r\n \"pier_side\" : false,\r\n \"bunkering\" : true,\r\n \"recommended_tugs\" : 2\r\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{SCHEMA}}{{PATH}}/shipcalls",
"host": [
"{{SCHEMA}}{{PATH}}"
],
"path": [
"shipcalls"
]
}
},
"response": []
},
{
"name": "Shipcalls PUT",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{LOGON_TOKEN}}",
"type": "string"
}
]
},
"method": "PUT",
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"id\" : 2, \r\n \"recommended_tugs\" : 3\r\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{SCHEMA}}{{PATH}}/shipcalls",
"host": [
"{{SCHEMA}}{{PATH}}"
],
"path": [
"shipcalls"
]
}
},
"response": []
},
{
"name": "Berths GET",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{LOGON_TOKEN}}",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"url": {
"raw": "{{SCHEMA}}{{PATH}}/berths",
"host": [
"{{SCHEMA}}{{PATH}}"
],
"path": [
"berths"
]
}
},
"response": []
},
{
"name": "Notifications GET",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{LOGON_TOKEN}}",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"url": {
"raw": "{{SCHEMA}}{{PATH}}/notifications?participant_id=1",
"host": [
"{{SCHEMA}}{{PATH}}"
],
"path": [
"notifications"
],
"query": [
{
"key": "participant_id",
"value": "1"
}
]
}
},
"response": []
},
{
"name": "Ships GET",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{LOGON_TOKEN}}",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"url": {
"raw": "{{SCHEMA}}{{PATH}}/ships",
"host": [
"{{SCHEMA}}{{PATH}}"
],
"path": [
"ships"
]
}
},
"response": []
},
{
"name": "Times GET",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{LOGON_TOKEN}}",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"url": {
"raw": "{{SCHEMA}}{{PATH}}/times?shipcall_id=1",
"host": [
"{{SCHEMA}}{{PATH}}"
],
"path": [
"times"
],
"query": [
{
"key": "shipcall_id",
"value": "1"
}
]
}
},
"response": []
},
{
"name": "Times POST",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{LOGON_TOKEN}}",
"type": "string"
}
]
},
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"start_planned\" : \"2023-04-18T07:18:19\",\r\n \"end_planned\" : \"2023-04-18T09:18:19\", \r\n \"shipcall_id\" : 1,\r\n \"participant_id\" : 1\r\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "{{SCHEMA}}{{PATH}}/times",
"host": [
"{{SCHEMA}}{{PATH}}"
],
"path": [
"times"
]
}
},
"response": []
},
{
"name": "Times PUT",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{LOGON_TOKEN}}",
"type": "string"
}
]
},
"method": "PUT",
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"start_planned\" : \"2023-05-18T07:18:19\",\r\n \"end_planned\" : \"2023-05-18T09:18:19\", \r\n \"id\" : 1\r\n}"
},
"url": {
"raw": "{{SCHEMA}}{{PATH}}/times",
"host": [
"{{SCHEMA}}{{PATH}}"
],
"path": [
"times"
]
}
},
"response": []
},
{
"name": "Times DELETE",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "{{LOGON_TOKEN}}",
"type": "string"
}
]
},
"method": "DELETE",
"header": [],
"url": {
"raw": "{{SCHEMA}}{{PATH}}/times?id=3",
"host": [
"{{SCHEMA}}{{PATH}}"
],
"path": [
"times"
],
"query": [
{
"key": "id",
"value": "3"
}
]
}
},
"response": []
}
],
"auth": {
"type": "bearer"
},
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
""
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
""
]
}
}
]
}

6653
misc/BreCalApi.cs Normal file

File diff suppressed because it is too large Load Diff

602
misc/BreCalApi.yaml Normal file
View File

@ -0,0 +1,602 @@
openapi: '3.0.0'
info:
version: '1.0.0'
title: 'Bremen calling API'
description: Administer DEBRE ship calls, times and notifications
termsOfService: "https://www.bsmd.de/" # url to terms page
contact:
name: "Bremen calling API"
url: "https://www.textbausteine.net"
email: "info@textbausteine.net"
license:
name: "Use at your own risk"
url: "https://www.bsmd.de/license"
servers:
# tutorial: https://idratherbewriting.com/learnapidoc/pubapis_openapi_step3_servers_object.html
- url : "https://brecal.bsmd-emswe.eu/"
description: "Test server hosted on vcup"
paths:
# tutorial: https://idratherbewriting.com/learnapidoc/pubapis_openapi_step4_paths_object.html
/login:
post:
summary: Returns a JWT session token and user data if successful
requestBody:
description: Login credentials
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/credentials'
responses:
200:
description: Successful response
content:
application/json:
schema:
$ref: '#/components/schemas/login_result'
400:
$ref: '#/components/responses/400'
403:
$ref: '#/components/responses/403'
500:
$ref: '#/components/responses/500'
503:
$ref: '#/components/responses/503'
/shipcalls:
get:
summary: Gets a list of ship calls
#parameters:
# - name: participant_id
# in: query
# required: true
# description: "**Id of participant**. *Example: 2*. Id of participant entity requesting ship calls"
# schema:
# type: integer
responses:
200:
description: ship call list
content:
application/json:
schema:
$ref: '#/components/schemas/shipcalls'
400:
$ref: '#/components/responses/400'
401:
$ref: '#/components/responses/401'
500:
$ref: '#/components/responses/500'
503:
$ref: '#/components/responses/503'
post:
summary: Create a new ship call
requestBody:
description: Creates a new ship call. **Do not** provide id parameter.
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/shipcall'
responses:
400:
$ref: '#/components/responses/400'
401:
$ref: '#/components/responses/401'
500:
$ref: '#/components/responses/500'
503:
$ref: '#/components/responses/503'
put:
summary: Updates a ship call
requestBody:
description: Creates a new ship call. The id parameter is **required**.
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/shipcall'
responses:
400:
$ref: '#/components/responses/400'
401:
$ref: '#/components/responses/401'
500:
$ref: '#/components/responses/500'
503:
$ref: '#/components/responses/503'
/ships:
get:
summary: gets a list of registered shipcalls
responses:
200:
description: list of ships
content:
application/json:
schema:
$ref: '#/components/schemas/ship_list'
400:
$ref: '#/components/responses/400'
401:
$ref: '#/components/responses/401'
500:
$ref: '#/components/responses/500'
503:
$ref: '#/components/responses/503'
/participant:
get:
summary: gets a particular participant entry corresponding to user id
parameters:
- name: user_id
in: query
required: false
description: "**Id of user**. *Example: 2*. User id returned by verify call."
schema:
type: integer
responses:
200:
description: ship call list
content:
application/json:
schema:
$ref: '#/components/schemas/participant_list'
400:
$ref: '#/components/responses/400'
401:
$ref: '#/components/responses/401'
500:
$ref: '#/components/responses/500'
503:
$ref: '#/components/responses/503'
/times:
get:
summary: Get all recorded times for a a ship call
parameters:
- name: shipcall_id
in: query
description: "**Id**. *Example: 42*. Id of referenced ship call."
schema:
type: integer
responses:
200:
description: list of recorded times
content:
application/json:
schema:
$ref: '#/components/schemas/times_list'
400:
$ref: '#/components/responses/400'
401:
$ref: '#/components/responses/401'
500:
$ref: '#/components/responses/500'
503:
$ref: '#/components/responses/503'
post:
summary: Create a new times entry for a ship call
requestBody:
description: Times entry that will be added to the ship call. **Do not** provide id parameter.
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/times'
responses:
400:
$ref: '#/components/responses/400'
401:
$ref: '#/components/responses/401'
500:
$ref: '#/components/responses/500'
503:
$ref: '#/components/responses/503'
put:
summary: Update a times entry for a ship call
requestBody:
description: Times entry that will be added to the ship call. The id parameter is **required**.
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/times'
responses:
400:
$ref: '#/components/responses/400'
401:
$ref: '#/components/responses/401'
500:
$ref: '#/components/responses/500'
503:
$ref: '#/components/responses/503'
delete:
summary: Delete a times entry for a ship call.
parameters:
- name: id
in: query
required: true
schema:
$ref: '#/components/schemas/timesId'
responses:
400:
$ref: '#/components/responses/400'
401:
$ref: '#/components/responses/401'
500:
$ref: '#/components/responses/500'
503:
$ref: '#/components/responses/503'
/notifications:
get:
summary: Gets a list of notifications pursuant to a specified participant and ship call
parameters:
- name: participant_id
in: query
required: true
description: "**Id of participant**. *Example: 2*. Id returned through loading of participant"
schema:
type: integer
- name: shipcall_id
in: query
required: true
description: "**Id of ship call**. *Example: 52*. Id given in ship call list"
schema:
$ref: '#/components/schemas/shipcallId'
responses:
200:
description: notification list
content:
application/json:
schema:
$ref: '#/components/schemas/notification'
400:
$ref: '#/components/responses/400'
401:
$ref: '#/components/responses/401'
500:
$ref: '#/components/responses/500'
503:
$ref: '#/components/responses/503'
/berths:
get:
summary: Gets a list of all berths registered
responses:
200:
description: list of berths
content:
application/json:
schema:
$ref: '#/components/schemas/berth_list'
400:
$ref: '#/components/responses/400'
401:
$ref: '#/components/responses/401'
500:
$ref: '#/components/responses/500'
503:
$ref: '#/components/responses/503'
components:
schemas:
credentials:
type: object
required:
- username
- password
properties:
username:
format : string
password:
format : string
timesId:
description: The unique identifier for a times entry
type: integer
shipcallId:
description: The unique identifier of a ship call
type: integer
shipcall:
type: object
required:
- id
- ship_id
- type
- eta
- voyage
- etd
properties:
id:
$ref: '#/components/schemas/shipcallId'
ship_id:
type: integer
type:
type: string
enum:
- incoming
- outgoing
- shifting
eta:
type: string
format: date-time
voyage:
type: string
nullable: true
etd:
type: string
format: date-time
nullable: true
arrival_berth_id:
type: integer
nullable: true
departure_berth_id:
type: integer
nullable: true
tug_reguired:
type: boolean
nullable: true
pilot_required:
type: boolean
nullable: true
flags:
type: integer
nullable: true
pier_side:
type: boolean
nullable: true
bunkering:
type: boolean
nullable: true
replenishing:
type: boolean
nullable: true
draft:
type: number
format: float
nullable: true
tidal_window_from:
type: string
format: date-time
nullable: true
tidal_window_to:
type: string
format: date-time
nullable: true
rain_sensitive_cargo:
type: boolean
nullable: true
recommended_tugs:
type: integer
nullable: true
created:
type: string
format: date-time
modified:
type: string
format: date-time
nullable: true
shipcalls:
type: array
items:
$ref: '#/components/schemas/shipcall'
times:
type: object
description: the id parameter needs to be missing on POST and to be present on PUT (Update) calls, otherwise a 400 response will be generated
required:
- shipcall_id
- participant_id
properties:
id:
type: integer
start_planned:
type: string
format: date-time
end_planned:
type: string
format: date-time
duration_planned:
type: integer
start_actual:
type: string
format: date-time
end_actual:
type: string
format: date-time
shipcall_id:
type: integer
participant_id:
type: integer
created:
type: string
format: date-time
modified:
type: string
format: date-time
nullable: true
times_list:
type: array
items:
$ref: '#/components/schemas/times'
berth:
type: object
description: Ship berth used for a ship call
properties:
id:
type: integer
name1:
type: string
name2:
type: string
berth_list:
type: array
items:
$ref: '#/components/schemas/berth'
ship:
type: object
description: a ship
properties:
id:
type: integer
name:
type: string
imo:
type: integer
callsign:
type: string
participant_id:
type: integer
length:
type: number
format: float
width:
type: number
format: float
created:
type: string
format: date-time
modified:
type: string
format: date-time
nullable: true
ship_list:
type: array
items:
$ref: '#/components/schemas/ship'
notification:
type: object
description: a notification created by the engine if a times entry violates a rule
properties:
id:
type: integer
times_id:
type: integer
participant_id:
type: integer
notification_type:
type: string
enum: [undefined, email, push]
timestamp:
type: string
format: date-time
acknowledged:
type: boolean
created:
type: string
format: date-time
modified:
type: string
format: date-time
nullable: true
notification_list:
type: array
items:
$ref: '#/components/schemas/notification'
participant:
type: object
description: A organisational entity that participates in Bremen Calling
properties:
id:
type: integer
name:
type: string
street:
type: string
postal code:
type: string
city:
type: string
created:
type: string
format: date-time
modified:
type: string
format: date-time
nullable: true
participant_list:
type: array
items:
$ref: '#/components/schemas/participant'
login_result:
type: object
description: result structure of a successful login attempt
properties:
id:
type: integer
participant_id:
type: integer
first_name:
type: string
last_name:
type: string
user_name:
type: string
user_phone:
type: string
exp:
type: number
format: float
token:
type: string
Error:
type: object
required:
- message
properties:
message:
description: A human readable error message
type: string
securitySchemes:
ApiKey:
type: apiKey
in: header
name: Authorization
responses:
400:
description: Invalid input
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
401:
description: Not authorized
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
500:
description: Unexpected error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
503:
description: Not available
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
security:
- ApiKey: []
externalDocs:
url: http://textbausteine.net/
description: Extra documentation and conditions for Bremen Calling

50
misc/Readme.md Normal file
View File

@ -0,0 +1,50 @@
# Database Bremen Calling
## Server
mariadb v 10
## Getting started
- Execute 'create_schema.sql' and import 'sample_data.sql'
If the database is updated or changed, please update these files.
To avoid errors, it is best to drop the entire database, edit the create script and re-import the sample data.
## Creating the dump file
```bash
mysqldump -u [username] -p --no-create-info --complete-insert bremen_calling > sample_data.sql
```
## Removing existing tables
We want only to remove the tables in order to preserve user privileges
```sql
SELECT concat('DROP TABLE IF EXISTS `', table_name, '`;')
FROM information_schema.tables
WHERE table_schema = 'bremen_calling';
```
outputs complete drop statements
```sql
SET FOREIGN_KEY_CHECKS = 0;
-- Your semicolon separated list of DROP statements here
DROP TABLE IF EXISTS `notification`;
DROP TABLE IF EXISTS `role`;
DROP TABLE IF EXISTS `ship`;
DROP TABLE IF EXISTS `participant`;
DROP TABLE IF EXISTS `times`;
DROP TABLE IF EXISTS `role_securable_map`;
DROP TABLE IF EXISTS `user_role_map`;
DROP TABLE IF EXISTS `user`;
DROP TABLE IF EXISTS `securable`;
DROP TABLE IF EXISTS `shipcall_participant_map`;
DROP TABLE IF EXISTS `berth`;
DROP TABLE IF EXISTS `shipcall`;
SET FOREIGN_KEY_CHECKS = 1;
```

View File

@ -0,0 +1,27 @@
{
"id": "a3b2b291-6ec7-4af8-9ba6-57448547f71b",
"name": "Remote BreCal EMSWE",
"values": [
{
"key": "PATH",
"value": "brecal.bsmd-emswe.eu/",
"type": "default",
"enabled": true
},
{
"key": "LOGON_TOKEN",
"value": "",
"type": "any",
"enabled": true
},
{
"key": "SCHEMA",
"value": "https://",
"type": "default",
"enabled": true
}
],
"_postman_variable_scope": "environment",
"_postman_exported_at": "2023-06-27T09:33:59.460Z",
"_postman_exported_using": "Postman/10.15.4"
}

32
misc/add_user.py Normal file
View File

@ -0,0 +1,32 @@
import mysql.connector
import os
import json
import bcrypt
config_path = '../src/server/BreCal/connection_data.json'
print (os.getcwd())
if not os.path.exists(config_path):
print ('cannot find ' + config_path)
exit(1)
f = open(config_path);
connection_data = json.load(f)
mydb = mysql.connector.connect(host=connection_data["host"], user=connection_data["user"],
password = connection_data["password"], database=connection_data["database"])
print(mydb)
# insert a new user
participant_id = 1
first_name = "Londo"
last_name = "Mollari"
user_name = "Londo"
user_email = "l.mollari@centauri.gov"
user_phone = "+01 555 324 2313"
password = "Hallowach"
password_hash = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt( 12 )).decode('utf8')
query = "INSERT INTO user (participant_id, first_name, last_name, user_name, user_email, user_phone, password_hash) VALUES (" + str(participant_id) + ",\"" + first_name + "\",\"" + last_name + "\",\"" + user_name + "\",\"" + user_email + "\",\"" + user_phone + "\",\"" + password_hash + "\")"
with mydb.cursor() as cursor:
cursor.execute(query)
mydb.commit()

BIN
misc/brecal.snk Normal file

Binary file not shown.

209
misc/create_schema.sql Normal file
View File

@ -0,0 +1,209 @@
CREATE DATABASE `bremen_calling` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
USE `bremen_calling`
CREATE TABLE `participant` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(128) DEFAULT NULL,
`street` varchar(128) DEFAULT NULL,
`postal_code` varchar(5) DEFAULT NULL,
`city` varchar(64) DEFAULT NULL,
`flags` int(10) unsigned DEFAULT NULL,
`created` DATETIME NULL DEFAULT current_timestamp(),
`modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='An organization taking part';
CREATE TABLE `user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`participant_id` int(11) UNSIGNED DEFAULT NULL,
`first_name` varchar(45) DEFAULT NULL,
`last_name` varchar(45) DEFAULT NULL,
`user_name` varchar(45) DEFAULT NULL,
`user_email` varchar(128) DEFAULT NULL,
`user_phone` varchar(128) DEFAULT NULL,
`password_hash` varchar(128) DEFAULT NULL,
`api_key` varchar(256) DEFAULT NULL,
`created` DATETIME NULL DEFAULT current_timestamp(),
`modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
INDEX `FK_USER_PART` (`participant_id`),
CONSTRAINT `FK_USER_PART` FOREIGN KEY (`participant_id`) REFERENCES `participant` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='member of a participant';
CREATE TABLE `berth` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(128) NULL DEFAULT NULL COMMENT 'Descriptive name',
`participant_id` INT(10) UNSIGNED NULL DEFAULT NULL COMMENT 'If berth belongs to a participant, reference it here',
`lock` BIT(1) NULL DEFAULT NULL COMMENT 'The lock must be used',
`created` DATETIME NULL DEFAULT current_timestamp(),
`modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
INDEX `FK_BERTH_PART` (`participant_id`),
CONSTRAINT `FK_BERTH_PART` FOREIGN KEY (`participant_id`) REFERENCES `participant` (`id`)
) COMMENT='Berth of ship for a ship call' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `ship` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(45) DEFAULT NULL,
`imo` int(11) DEFAULT NULL,
`callsign` varchar(8) DEFAULT NULL,
`participant_id` INT(11) UNSIGNED NULL DEFAULT NULL,
`length` FLOAT NULL DEFAULT NULL,
`width` FLOAT NULL DEFAULT NULL,
`created` DATETIME NULL DEFAULT current_timestamp(),
`modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
INDEX `FK_SHIP_PARTICIPANT` (`participant_id`),
CONSTRAINT `FK_SHIP_PARTICIPANT` FOREIGN KEY (`participant_id`) REFERENCES `participant` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `shipcall` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`ship_id` INT(11) UNSIGNED NULL DEFAULT NULL,
`type` TINYINT(4) NULL DEFAULT NULL,
`eta` DATETIME NULL DEFAULT NULL,
`voyage` VARCHAR(16) NULL DEFAULT NULL,
`etd` DATETIME NULL DEFAULT NULL,
`arrival_berth_id` INT(10) UNSIGNED NULL DEFAULT NULL,
`departure_berth_id` INT(10) UNSIGNED NULL DEFAULT NULL,
`tug_required` BIT(1) NULL DEFAULT NULL,
`pilot_required` BIT(1) NULL DEFAULT NULL,
`flags` INT(10) UNSIGNED NULL DEFAULT 0,
`pier_side` BIT(1) NULL DEFAULT NULL,
`bunkering` BIT(1) NULL DEFAULT NULL,
`replenishing` BIT(1) NULL DEFAULT NULL,
`draft` FLOAT NULL DEFAULT NULL,
`tidal_window_from` DATETIME NULL DEFAULT NULL,
`tidal_window_to` DATETIME NULL DEFAULT NULL,
`rain_sensitive_cargo` BIT(1) NULL DEFAULT b'0',
`recommended_tugs` INT(11) NULL DEFAULT 0,
`created` DATETIME NULL DEFAULT current_timestamp(),
`modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
INDEX `FK_SHIPCALL_SHIP` (`ship_id`),
INDEX `FK_SHIPCALL_BERTH_ARRIVAL` (`arrival_berth_id`),
INDEX `FK_SHIPCALL_BERTH_DEPARTURE` (`departure_berth_id`),
CONSTRAINT `FK_SHIPCALL_BERTH_ARRIVAL` FOREIGN KEY (`arrival_berth_id`) REFERENCES `berth` (`id`),
CONSTRAINT `FK_SHIPCALL_BERTH_DEPARTURE` FOREIGN KEY (`departure_berth_id`) REFERENCES `berth` (`id`),
CONSTRAINT `FK_SHIPCALL_SHIP` FOREIGN KEY (`ship_id`) REFERENCES `ship` (`id`)
) COMMENT='Incoming, outgoing or moving to another berth' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `times` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`start_planned` datetime DEFAULT NULL,
`end_planned` datetime DEFAULT NULL,
`duration_planned` int(11) DEFAULT NULL,
`start_actual` datetime DEFAULT NULL,
`end_actual` datetime DEFAULT NULL,
`duration_actual` int(11) DEFAULT NULL,
`shipcall_id` int(11) UNSIGNED DEFAULT NULL,
`participant_id` int(11) UNSIGNED DEFAULT NULL,
`created` DATETIME NULL DEFAULT current_timestamp(),
`modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
INDEX `FK_TIME_SHIPCALL` (`shipcall_id`),
INDEX `FK_TIME_PART` (`participant_id`),
CONSTRAINT `FK_TIME_PART` FOREIGN KEY (`participant_id`) REFERENCES `participant` (`id`),
CONSTRAINT `FK_TIME_SHIPCALL` FOREIGN KEY (`shipcall_id`) REFERENCES `shipcall` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='the planned time for the participants work';
CREATE TABLE `shipcall_participant_map` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`shipcall_id` int(10) unsigned DEFAULT NULL,
`participant_id` int(10) unsigned DEFAULT NULL,
`created` DATETIME NULL DEFAULT current_timestamp(),
`modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
INDEX `FK_MAP_PARTICIPANT_SHIPCALL` (`shipcall_id`),
INDEX `FK_MAP_SHIPCALL_PARTICIPANT` (`participant_id`),
CONSTRAINT `FK_MAP_PARTICIPANT_SHIPCALL` FOREIGN KEY (`shipcall_id`) REFERENCES `shipcall` (`id`),
CONSTRAINT `FK_MAP_SHIPCALL_PARTICIPANT` FOREIGN KEY (`participant_id`) REFERENCES `participant` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Associates a participant with a shipcall';
CREATE TABLE `shipcall_tug_map` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`shipcall_id` INT(11) UNSIGNED NOT NULL COMMENT 'Ref to ship call',
`ship_id` INT(11) UNSIGNED NOT NULL COMMENT 'Ref to ship (that is a tug)',
`created` DATETIME NULL DEFAULT current_timestamp(),
`modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
INDEX `FK_SCT_SHIP` (`ship_id`),
INDEX `FK_SCT_SHIPCALL` (`shipcall_id`),
CONSTRAINT `FK_SCT_SHIP` FOREIGN KEY (`ship_id`) REFERENCES `ship` (`id`),
CONSTRAINT `FK_SCT_SHIPCALL` FOREIGN KEY (`shipcall_id`) REFERENCES `shipcall` (`id`)
) COMMENT='Mapping table that assigns tugs to a ship call' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `notification` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`times_id` INT(11) UNSIGNED NOT NULL COMMENT 'times record that caused the notification',
`participant_id` INT(11) UNSIGNED NOT NULL COMMENT 'participant ref',
`acknowledged` BIT(1) NULL DEFAULT b'0' COMMENT 'true if UI acknowledged',
`level` TINYINT(4) NULL DEFAULT NULL COMMENT 'severity of the notification',
`type` TINYINT(4) NULL DEFAULT NULL COMMENT 'Email/UI/Other',
`message` VARCHAR(256) NULL DEFAULT NULL COMMENT 'individual message',
`created` DATETIME NULL DEFAULT current_timestamp(),
`modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
INDEX `FK_NOT_TIMES` (`times_id`),
INDEX `FK_NOT_PART` (`participant_id`),
CONSTRAINT `FK_NOT_PART` FOREIGN KEY (`participant_id`) REFERENCES `participant` (`id`),
CONSTRAINT `FK_NOT_TIMES` FOREIGN KEY (`times_id`) REFERENCES `times` (`id`)
) COMMENT='An entry corresponds to an alarm given by a violated rule during times update' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `role` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL DEFAULT '0' COMMENT 'unique role name',
`description` VARCHAR(255) NULL DEFAULT '0' COMMENT 'role description',
`created` DATETIME NULL DEFAULT current_timestamp(),
`modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
UNIQUE INDEX `name` (`name`)
) COMMENT='logical group of securables for one or more user' DEFAULT CHARSET=utf8mb4 ENGINE=InnoDB;
CREATE TABLE `securable` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) NOT NULL DEFAULT '',
`created` DATETIME NULL DEFAULT current_timestamp(),
`modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
UNIQUE INDEX `name` (`name`)
) COMMENT='Actual permission on a single(!) feature or operation' DEFAULT CHARSET=utf8mb4 ENGINE=InnoDB;
CREATE TABLE `user_role_map` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` INT(10) UNSIGNED NOT NULL DEFAULT 0,
`role_id` INT(10) UNSIGNED NOT NULL DEFAULT 0,
`created` DATETIME NULL DEFAULT current_timestamp(),
`modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
INDEX `FK_USER_ROLE` (`user_id`),
INDEX `FK_ROLE_USER` (`role_id`),
CONSTRAINT `FK_ROLE_USER` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`),
CONSTRAINT `FK_USER_ROLE` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) COMMENT='Assigns a user to a role' DEFAULT CHARSET=utf8mb4 ENGINE=InnoDB;
CREATE TABLE `role_securable_map` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`role_id` INT(10) UNSIGNED NOT NULL,
`securable_id` INT(10) UNSIGNED NOT NULL,
`created` DATETIME NULL DEFAULT current_timestamp(),
`modified` DATETIME NULL DEFAULT NULL ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
INDEX `FK_ROLE_SECURABLE` (`role_id`),
INDEX `FK_SECURABLE_ROLE` (`securable_id`),
CONSTRAINT `FK_ROLE_SECURABLE` FOREIGN KEY (`role_id`) REFERENCES `role` (`id`),
CONSTRAINT `FK_SECURABLE_ROLE` FOREIGN KEY (`securable_id`) REFERENCES `securable` (`id`)
) COMMENT='Assigns securables to roles' DEFAULT CHARSET=utf8mb4 ENGINE=InnoDB;

140
misc/sample_data.sql Normal file
View File

@ -0,0 +1,140 @@
-- MySQL dump 10.19 Distrib 10.3.38-MariaDB, for debian-linux-gnueabihf (armv8l)
--
-- Host: localhost Database: bremen_calling
-- ------------------------------------------------------
-- Server version 10.3.38-MariaDB-0+deb10u1
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Dumping data for table `berth`
--
USE `bremen_calling`;
LOCK TABLES `berth` WRITE;
/*!40000 ALTER TABLE `berth` DISABLE KEYS */;
INSERT INTO `berth` (`id`, `name`) VALUES (1,'Roland Mühle'),(2,'Stahlwerk'),(3,'Kellogs');
/*!40000 ALTER TABLE `berth` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Dumping data for table `notification`
--
LOCK TABLES `notification` WRITE;
/*!40000 ALTER TABLE `notification` DISABLE KEYS */;
/*!40000 ALTER TABLE `notification` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Dumping data for table `participant`
--
LOCK TABLES `participant` WRITE;
/*!40000 ALTER TABLE `participant` DISABLE KEYS */;
INSERT INTO `participant` (`id`, `name`, `street`, `postal_code`, `city`, `flags`, `created`, `modified`) VALUES (1,'Schick Informatik','Gottlieb-Daimler-Str. 8','73614','Schorndorf',42,'2023-04-17 07:18:19',NULL);
/*!40000 ALTER TABLE `participant` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Dumping data for table `role`
--
LOCK TABLES `role` WRITE;
/*!40000 ALTER TABLE `role` DISABLE KEYS */;
INSERT INTO `role` (`id`, `name`, `description`, `created`, `modified`) VALUES (1,'My first role','A very good description','2023-04-17 07:31:57',NULL),(2,'Another role','This role is very nice as well','2023-04-17 07:32:12',NULL);
/*!40000 ALTER TABLE `role` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Dumping data for table `role_securable_map`
--
LOCK TABLES `role_securable_map` WRITE;
/*!40000 ALTER TABLE `role_securable_map` DISABLE KEYS */;
/*!40000 ALTER TABLE `role_securable_map` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Dumping data for table `securable`
--
LOCK TABLES `securable` WRITE;
/*!40000 ALTER TABLE `securable` DISABLE KEYS */;
INSERT INTO `securable` (`id`, `name`, `created`, `modified`) VALUES (1,'First secure thing','2023-04-17 07:38:12',NULL),(2,'Another secure thing','2023-04-17 07:38:22',NULL);
/*!40000 ALTER TABLE `securable` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Dumping data for table `ship`
--
LOCK TABLES `ship` WRITE;
/*!40000 ALTER TABLE `ship` DISABLE KEYS */;
/*!40000 ALTER TABLE `ship` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Dumping data for table `shipcall`
--
LOCK TABLES `shipcall` WRITE;
/*!40000 ALTER TABLE `shipcall` DISABLE KEYS */;
/*!40000 ALTER TABLE `shipcall` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Dumping data for table `shipcall_participant_map`
--
LOCK TABLES `shipcall_participant_map` WRITE;
/*!40000 ALTER TABLE `shipcall_participant_map` DISABLE KEYS */;
/*!40000 ALTER TABLE `shipcall_participant_map` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Dumping data for table `times`
--
LOCK TABLES `times` WRITE;
/*!40000 ALTER TABLE `times` DISABLE KEYS */;
/*!40000 ALTER TABLE `times` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Dumping data for table `user`
--
LOCK TABLES `user` WRITE;
/*!40000 ALTER TABLE `user` DISABLE KEYS */;
INSERT INTO `user` (`id`, `participant_id`, `first_name`, `last_name`, `user_name`, `password_hash`, `api_key`, `created`, `modified`) VALUES (1,1,'Daniel','Schick','dani',NULL,'0815','2023-04-17 07:15:41',NULL);
/*!40000 ALTER TABLE `user` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Dumping data for table `user_role_map`
--
LOCK TABLES `user_role_map` WRITE;
/*!40000 ALTER TABLE `user_role_map` DISABLE KEYS */;
/*!40000 ALTER TABLE `user_role_map` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2023-04-27 9:09:13

16
src/BreCalClient/App.xaml Normal file
View File

@ -0,0 +1,16 @@
<Application x:Class="BreCalClient.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BreCalClient"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources\StringResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace BreCalClient
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

View File

@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

View File

@ -0,0 +1,102 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<SignAssembly>True</SignAssembly>
<StartupObject>BreCalClient.App</StartupObject>
<AssemblyOriginatorKeyFile>C:\git_lager\git_brcal\misc\brecal.snk</AssemblyOriginatorKeyFile>
<AssemblyVersion>0.1.0.0</AssemblyVersion>
<FileVersion>0.1.0.0</FileVersion>
<Title>Bremen calling client</Title>
<Description>A Windows WPF client for the Bremen calling API.</Description>
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\arrow_down_red.png" />
<None Remove="Resources\arrow_right_blue.png" />
<None Remove="Resources\arrow_up_green.png" />
<None Remove="Resources\clipboard.png" />
<None Remove="Resources\clock.png" />
<None Remove="Resources\containership.png" />
<None Remove="Resources\emergency_stop_button.png" />
<None Remove="Resources\ship2.png" />
<None Remove="Resources\trafficlight_green.png" />
<None Remove="Resources\trafficlight_off.png" />
<None Remove="Resources\trafficlight_on.png" />
<None Remove="Resources\trafficlight_red.png" />
<None Remove="Resources\trafficlight_red_yellow.png" />
<None Remove="Resources\trafficlight_yellow.png" />
<None Remove="Resources\umbrella_closed.png" />
<None Remove="Resources\umbrella_open.png" />
<None Remove="Resources\worker2.png" />
</ItemGroup>
<ItemGroup>
<Page Remove="Resources\StringResources.xaml" />
<Page Remove="StringResources.de.xaml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\misc\BreCalApi.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>BreCalApi.yaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="..\..\misc\BreCalApi.yaml" Link="BreCalApi.yaml">
<Generator>OpenApiCodeGenerator</Generator>
<LastGenOutput>BreCalApi.cs</LastGenOutput>
</None>
<Resource Include="Resources\arrow_down_red.png" />
<Resource Include="Resources\arrow_right_blue.png" />
<Resource Include="Resources\arrow_up_green.png" />
<Resource Include="Resources\clipboard.png" />
<Resource Include="Resources\clock.png" />
<Resource Include="Resources\containership.png" />
<Resource Include="Resources\emergency_stop_button.png" />
<Resource Include="Resources\ship2.png" />
<Resource Include="Resources\StringResources.de.xaml">
<Generator>MSBuild:Compile</Generator>
</Resource>
<Resource Include="Resources\StringResources.xaml" />
<Resource Include="Resources\trafficlight_green.png" />
<Resource Include="Resources\trafficlight_off.png" />
<Resource Include="Resources\trafficlight_on.png" />
<Resource Include="Resources\trafficlight_red.png" />
<Resource Include="Resources\trafficlight_red_yellow.png" />
<Resource Include="Resources\trafficlight_yellow.png" />
<Resource Include="Resources\umbrella_closed.png" />
<Resource Include="Resources\umbrella_open.png" />
<Resource Include="Resources\worker2.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.0" />
<PackageReference Include="JsonSubTypes" Version="2.0.1" />
<PackageReference Include="log4net" Version="2.0.15" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Polly" Version="7.2.3" />
<PackageReference Include="RestSharp" Version="108.0.2" />
</ItemGroup>
<ItemGroup>
<Compile Update="Resources\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@ -0,0 +1,16 @@
extensions: designer.cs generated.cs
extensions: .cs .cpp .h
// Copyright (c) 2023 schick Informatik
// Description:
//
extensions: .aspx .ascx
<%--
Copyright (c) 2023 schick Informatik
--%>
extensions: .vb
'Sample license text.
extensions: .xml .config .xsd
<!--
Sample license text.
-->

View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33627.172
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BreCalClient", "BreCalClient.csproj", "{FA9E0A87-FBFB-4F2B-B5FA-46DE2E5E4BCB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FA9E0A87-FBFB-4F2B-B5FA-46DE2E5E4BCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FA9E0A87-FBFB-4F2B-B5FA-46DE2E5E4BCB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FA9E0A87-FBFB-4F2B-B5FA-46DE2E5E4BCB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FA9E0A87-FBFB-4F2B-B5FA-46DE2E5E4BCB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CBF797A4-0CAF-4F01-B0D5-708702165337}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,12 @@
<Window x:Class="BreCalClient.EditShipcallControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BreCalClient"
mc:Ignorable="d"
Title="EditShipcallControl" Height="450" Width="800">
<Grid>
</Grid>
</Window>

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace BreCalClient
{
/// <summary>
/// Interaction logic for EditShipcallControl.xaml
/// </summary>
public partial class EditShipcallControl : Window
{
public EditShipcallControl()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,87 @@
<Window x:Class="BreCalClient.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:BreCalClient"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d"
Title="{DynamicResource textApplicationTitle}" Height="450" Width="800" Loaded="Window_Loaded" Closing="Window_Closing">
<xctk:BusyIndicator Name="busyIndicator" IsBusy="True">
<xctk:BusyIndicator.ProgressBarStyle>
<Style TargetType="ProgressBar">
<Setter Property="Visibility" Value="Collapsed" />
</Style>
</xctk:BusyIndicator.ProgressBarStyle>
<xctk:BusyIndicator.BusyContent>
<Grid Width="320">
<Grid.RowDefinitions>
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Label Content="{DynamicResource textLoginCaption}" Grid.Row="0" Grid.ColumnSpan="2" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" />
<Label Content="{DynamicResource textUsername}" Grid.Row="1" VerticalContentAlignment="Center" />
<Label Content="{DynamicResource textPassword}" Grid.Row="2" VerticalContentAlignment="Center" />
<TextBox Name="textUsername" Grid.Row="1" Grid.Column="1" Margin="2" VerticalContentAlignment="Center" />
<PasswordBox Name="textPassword" Grid.Row="2" Grid.Column="1" Margin="2" VerticalContentAlignment="Center" PasswordChar="*"/>
<Label Name="labelLoginResult" Grid.Row="3" Grid.ColumnSpan="2" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" />
<Button Name="buttonLogin" Content="{DynamicResource textLogin}" Grid.Row="4" Grid.Column="0" Margin="2" Click="buttonLogin_Click" IsDefault="True" />
<Button Name="buttonExit" Content="{DynamicResource textExit}" Grid.Row="4" Grid.Column="1" Margin="2" Click="buttonExit_Click" />
</Grid>
</xctk:BusyIndicator.BusyContent>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel x:Name="stackPanel"/>
</ScrollViewer>
<StatusBar Grid.Row="1">
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem Grid.Column="0">
<TextBlock Name="labelGeneralStatus" FontSize="9"></TextBlock>
</StatusBarItem>
<StatusBarItem Grid.Column="1">
<TextBlock Name="labelVersion"></TextBlock>
</StatusBarItem>
<StatusBarItem Grid.Column="2">
<TextBlock Name="labelUsername"></TextBlock>
</StatusBarItem>
<Separator Grid.Column="3"/>
<StatusBarItem Grid.Column="4">
<TextBlock Name="labelStatusBar"></TextBlock>
</StatusBarItem>
<Separator Grid.Column="5"/>
<StatusBarItem Grid.Column="6">
<ProgressBar Name="generalProgressStatus" Width="90" Height="16"/>
</StatusBarItem>
</StatusBar>
</Grid>
</xctk:BusyIndicator>
</Window>

View File

@ -0,0 +1,194 @@
// Copyright (c) 2023 schick Informatik
// Description: Bremen calling main window
//
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using BreCalClient.misc.Api;
using BreCalClient.misc.Client;
using BreCalClient.misc.Model;
namespace BreCalClient
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private const int SHIPCALL_UPDATE_INTERVAL_SECONDS = 30;
private DefaultApi _api;
private ObservableCollection<ShipcallControlModel> _controlModels = new();
private List<Ship> _ships = new();
private ConcurrentDictionary<int, Ship> _shipLookupDict = new();
private List<Berth> _berths = new();
private ConcurrentDictionary<int, Berth> _berthLookupDict = new();
private List<Participant> _participants = new();
private Dictionary<int, Participant> _participantLookupDict = new();
private CancellationTokenSource _tokenSource = new CancellationTokenSource();
public MainWindow()
{
InitializeComponent();
_api = new DefaultApi();
_api.Configuration.ApiKeyPrefix["Authorization"] = "Bearer";
}
#region event handler
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
_tokenSource.Cancel();
}
private async void buttonLogin_Click(object sender, RoutedEventArgs e)
{
if (string.IsNullOrEmpty(this.textPassword.Password) || string.IsNullOrEmpty(this.textUsername.Text))
{
this.labelLoginResult.Content = Application.Current.FindResource("textUserNamePasswordEmpty").ToString();
return;
}
Credentials credentials = new(username: textUsername.Text.Trim(),
password: textPassword.Password.Trim());
try
{
LoginResult loginResult = await _api.LoginPostAsync(credentials);
if(loginResult != null)
{
if(loginResult.Id > 0)
{
this.busyIndicator.IsBusy = false;
this._api.Configuration.ApiKey["Authorization"] = loginResult.Token;
this.LoadStaticLists();
}
}
}
catch (ApiException ex)
{
this.labelLoginResult.Content = ex.Message;
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void buttonExit_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
#endregion
#region private methods
private async void LoadStaticLists()
{
this._berths = await _api.BerthsGetAsync();
foreach(var berth in this._berths)
_berthLookupDict[berth.Id] = berth;
this._ships = await _api.ShipsGetAsync();
foreach(var ship in this._ships)
_shipLookupDict[ship.Id] = ship;
this._participants = await _api.ParticipantGetAsync();
foreach(Participant participant in this._participants)
this._participantLookupDict[participant.Id] = participant;
_ = Task.Run(() => RefreshShipcalls());
}
public async Task RefreshShipcalls()
{
while (!_tokenSource.Token.IsCancellationRequested)
{
List<Shipcall> shipcalls = await _api.ShipcallsGetAsync();
foreach (Shipcall shipcall in shipcalls)
{
ShipcallControlModel? selectedSCMModel = null;
foreach(ShipcallControlModel scm in this._controlModels)
{
if(scm.Shipcall?.Id == shipcall.Id)
{
selectedSCMModel = scm;
break;
}
}
if(selectedSCMModel != null)
{
selectedSCMModel.Shipcall = shipcall;
}
else
{
// no: create new entry
ShipcallControlModel scm = new ShipcallControlModel();
scm.Shipcall = shipcall;
if (this._shipLookupDict.ContainsKey(shipcall.ShipId))
scm.Ship = this._shipLookupDict[shipcall.ShipId];
_controlModels.Add(scm);
this.Dispatcher.Invoke(new Action(() =>
{
ShipcallControl sc = new ShipcallControl();
sc.Height = 80;
sc.ShipcallControlModel = scm;
sc.TimesRequested += Sc_TimesRequested;
sc.EditRequested += Sc_EditRequested;
this.stackPanel.Children.Add(sc);
}));
}
}
// test for deletion, anything in the display that is not in the lookup result
foreach(ShipcallControlModel scm in this._controlModels)
{
}
await Task.Delay(TimeSpan.FromSeconds(SHIPCALL_UPDATE_INTERVAL_SECONDS), _tokenSource.Token);
}
}
private void Sc_EditRequested(ShipcallControl obj)
{
// TODO: get the shipcall from the control and show the edit control
}
private void Sc_TimesRequested(ShipcallControl obj)
{
// TODO: get the shipcall, load and show a list of the times
}
#endregion
}
}

View File

@ -0,0 +1,233 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace BreCalClient.Resources {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BreCalClient.Resources.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] arrow_down_red {
get {
object obj = ResourceManager.GetObject("arrow_down_red", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] arrow_right_blue {
get {
object obj = ResourceManager.GetObject("arrow_right_blue", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] arrow_up_green {
get {
object obj = ResourceManager.GetObject("arrow_up_green", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] clipboard {
get {
object obj = ResourceManager.GetObject("clipboard", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] clock {
get {
object obj = ResourceManager.GetObject("clock", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] containership {
get {
object obj = ResourceManager.GetObject("containership", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] emergency_stop_button {
get {
object obj = ResourceManager.GetObject("emergency_stop_button", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] ship2 {
get {
object obj = ResourceManager.GetObject("ship2", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] trafficlight_green {
get {
object obj = ResourceManager.GetObject("trafficlight_green", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] trafficlight_off {
get {
object obj = ResourceManager.GetObject("trafficlight_off", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] trafficlight_on {
get {
object obj = ResourceManager.GetObject("trafficlight_on", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] trafficlight_red {
get {
object obj = ResourceManager.GetObject("trafficlight_red", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] trafficlight_red_yellow {
get {
object obj = ResourceManager.GetObject("trafficlight_red_yellow", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] trafficlight_yellow {
get {
object obj = ResourceManager.GetObject("trafficlight_yellow", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] umbrella_closed {
get {
object obj = ResourceManager.GetObject("umbrella_closed", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] umbrella_open {
get {
object obj = ResourceManager.GetObject("umbrella_open", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] worker2 {
get {
object obj = ResourceManager.GetObject("worker2", resourceCulture);
return ((byte[])(obj));
}
}
}
}

View File

@ -0,0 +1,172 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="arrow_down_red" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>arrow_down_red.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="arrow_right_blue" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>arrow_right_blue.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="arrow_up_green" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>arrow_up_green.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="clipboard" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>clipboard.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="clock" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>clock.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="containership" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>containership.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="emergency_stop_button" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>emergency_stop_button.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="ship2" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>ship2.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="trafficlight_green" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>trafficlight_green.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="trafficlight_off" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>trafficlight_off.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="trafficlight_on" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>trafficlight_on.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="trafficlight_red" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>trafficlight_red.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="trafficlight_red_yellow" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>trafficlight_red_yellow.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="trafficlight_yellow" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>trafficlight_yellow.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="umbrella_closed" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>umbrella_closed.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="umbrella_open" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>umbrella_open.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="worker2" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>worker2.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
</root>

View File

@ -0,0 +1,10 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="textLoginCaption">Benutzer Anmeldung</system:String>
<system:String x:Key="textUsername">Benutzername</system:String>
<system:String x:Key="textPassword">Passwort</system:String>
<system:String x:Key="textLogin">Anmelden</system:String>
<system:String x:Key="textExit">Abbrechen</system:String>
<system:String x:Key="textUserNamePasswordEmpty">Benutzername / Password leer!</system:String>
</ResourceDictionary>

View File

@ -0,0 +1,11 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="textLoginCaption">User login</system:String>
<system:String x:Key="textUsername">Username</system:String>
<system:String x:Key="textPassword">Password</system:String>
<system:String x:Key="textLogin">Login</system:String>
<system:String x:Key="textExit">Exit</system:String>
<system:String x:Key="textApplicationTitle">Bremen calling</system:String>
<system:String x:Key="textUserNamePasswordEmpty">Username and/or password empty!</system:String>
</ResourceDictionary>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@ -0,0 +1,106 @@
<UserControl x:Class="BreCalClient.ShipcallControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:BreCalClient"
xmlns:db="clr-namespace:BreCalClient;assembly=BreCalClient"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="800" Loaded="UserControl_Loaded">
<Border BorderBrush="LightGray" Margin="1" BorderThickness="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=".3*"/>
<RowDefinition Height=".3*"/>
<RowDefinition Height=".3*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".4*" />
<ColumnDefinition Width=".1*" />
<ColumnDefinition Width=".4*" />
<ColumnDefinition Width=".1*" />
</Grid.ColumnDefinitions>
<Viewbox Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" HorizontalAlignment="Left">
<Border BorderThickness="1" BorderBrush="AliceBlue" >
<TextBlock x:Name="textBlockShipname" Text="{Binding Ship.Name}" Foreground="DarkBlue" />
</Border>
</Viewbox>
<Grid Grid.Row="2" Grid.Column="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".2*" />
<ColumnDefinition Width=".3*" />
<ColumnDefinition Width=".2*" />
<ColumnDefinition Width=".3*" />
</Grid.ColumnDefinitions>
<Viewbox HorizontalAlignment="Left" Margin="4,0,0,0">
<TextBlock Text="IMO:"/>
</Viewbox>
<Viewbox Grid.Column="1" HorizontalAlignment="Left" Margin="4,0,0,0">
<TextBlock Text="{Binding Ship.Imo}" />
</Viewbox>
<Viewbox Grid.Column="2" HorizontalAlignment="Left">
<TextBlock Text="Callsign:" />
</Viewbox>
<Viewbox Grid.Column="3" HorizontalAlignment="Left" Margin="4,0,0,0">
<TextBlock Text="{Binding Ship.Callsign}" />
</Viewbox>
</Grid>
<Viewbox Grid.Column="1" Grid.Row="0">
<TextBlock Text="ETA:" />
</Viewbox>
<Viewbox Grid.Column="2" Grid.Row="0" HorizontalAlignment="Left">
<TextBlock Text="{Binding Shipcall.Eta}" />
</Viewbox>
<Viewbox Grid.Column="1" Grid.Row="1">
<TextBlock Text="ETD:" />
</Viewbox>
<Viewbox Grid.Column="2" Grid.Row="1" HorizontalAlignment="Left">
<TextBlock Text="{Binding Shipcall.Etd}" />
</Viewbox>
<Grid Grid.Row="2" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*" />
<ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions>
<Button x:Name="buttonListTimes" Click="buttonListTimes_Click" Grid.Column="0" Background="Transparent" ToolTip="List times" Margin="1" BorderThickness="0" HorizontalAlignment="Right">
<Image Source="./Resources/clock.png" />
</Button>
<Button x:Name="buttonEditShipcall" Click="buttonEditShipcall_Click" Grid.Column="1" Background="Transparent" ToolTip="Edit shipcall" Margin="1" BorderThickness="0" HorizontalAlignment="Left">
<Image Source="./Resources/clipboard.png" />
</Button>
</Grid>
<Viewbox Grid.Column="2" Grid.Row="3">
<TextBlock Text="" />
</Viewbox>
<Image Margin="2" Grid.Column="3" Grid.Row="0" Grid.RowSpan="3">
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="{Binding NotFolderImage}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding LightMode}" Value="{x:Static db:ShipcallControlModel+TrafficLightMode.OFF}">
<Setter Property="Source" Value="./Resources/trafficlight_off.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding LightMode}" Value="{x:Static db:ShipcallControlModel+TrafficLightMode.RED}">
<Setter Property="Source" Value="./Resources/trafficlight_red.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding LightMode}" Value="{x:Static db:ShipcallControlModel+TrafficLightMode.RED_YELLOW}">
<Setter Property="Source" Value="./Resources/trafficlight_red_yellow.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding LightMode}" Value="{x:Static db:ShipcallControlModel+TrafficLightMode.GREEN}">
<Setter Property="Source" Value="./Resources/trafficlight_green.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding LightMode}" Value="{x:Static db:ShipcallControlModel+TrafficLightMode.ALL}">
<Setter Property="Source" Value="./Resources/trafficlight_on.png"/>
</DataTrigger>
<DataTrigger Binding="{Binding LightMode}" Value="{x:Static db:ShipcallControlModel+TrafficLightMode.YELLOW}">
<Setter Property="Source" Value="./Resources/trafficlight_yellow.png"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Grid>
</Border>
</UserControl>

View File

@ -0,0 +1,69 @@
// Copyright (c) 2023 schick Informatik
// Description: Custom control to display a ship call
//
using System;
using System.Windows;
using System.Windows.Controls;
namespace BreCalClient
{
/// <summary>
/// Interaction logic for ShipcallControl.xaml
/// </summary>
public partial class ShipcallControl : UserControl
{
#region Construction
public ShipcallControl()
{
InitializeComponent();
}
#endregion
#region events
public event Action<ShipcallControl>? EditRequested;
public event Action<ShipcallControl>? TimesRequested;
#endregion
#region Properties
/// <summary>
/// this is our datasource
/// </summary>
public ShipcallControlModel? ShipcallControlModel { get; set; }
#endregion
#region event handler
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = this.ShipcallControlModel;
}
private void buttonListTimes_Click(object sender, RoutedEventArgs e)
{
if(this.TimesRequested != null)
{
this.TimesRequested(this);
}
}
private void buttonEditShipcall_Click(object sender, RoutedEventArgs e)
{
if (this.EditRequested != null)
{
this.EditRequested(this);
}
}
#endregion
}
}

View File

@ -0,0 +1,77 @@
// Copyright (c) 2023 schick Informatik
// Description: Container model for shipcall related info
//
using BreCalClient.misc.Model;
using System;
namespace BreCalClient
{
/// <summary>
/// Container model to aggregate separate models for the Shipcall control data binding
/// </summary>
public class ShipcallControlModel
{
public enum TrafficLightMode
{
OFF,
GREEN,
YELLOW,
RED,
RED_YELLOW,
ALL
};
[Flags]
public enum StatusFlags
{
RED,
GREEN,
YELLOW,
BLINK_1,
BLINK_2
};
public Shipcall? Shipcall { get; set; }
public Ship? Ship { get; set; }
public string Test { get { return "Gurkensalat"; } }
public TrafficLightMode LightMode
{
get
{
if(IsFlagSet(StatusFlags.RED))
{
if(IsFlagSet((StatusFlags)StatusFlags.YELLOW))
{
if(IsFlagSet(StatusFlags.GREEN))
{
return TrafficLightMode.ALL;
}
return TrafficLightMode.RED_YELLOW;
}
return TrafficLightMode.RED;
}
if(IsFlagSet(StatusFlags.YELLOW))
return TrafficLightMode.YELLOW;
if(IsFlagSet(StatusFlags.GREEN))
return TrafficLightMode.GREEN;
return TrafficLightMode.OFF;
}
}
#region private helper
private bool IsFlagSet(StatusFlags flag)
{
if(this.Shipcall == null) return false;
return (this.Shipcall.Flags & (int) flag) != 0;
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
</configuration>

9
src/RoleEditor/App.xaml Normal file
View File

@ -0,0 +1,9 @@
<Application x:Class="RoleEditor.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RoleEditor"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace RoleEditor
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

View File

@ -0,0 +1,10 @@
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]

View File

@ -0,0 +1,288 @@
// Copyright (c) 2017 schick Informatik
// Description: DataGrid mit etwas "verbesserten" Funktionen
//
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Controls.Primitives;
using System.Collections.Generic;
using brecal.model;
using System.Globalization;
using System.Threading;
namespace RoleEditor
{
/// <summary>
/// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
///
/// Step 1a) Using this custom control in a XAML file that exists in the current project.
/// Add this XmlNamespace attribute to the root element of the markup file where it is
/// to be used:
///
/// xmlns:enictrl="clr-namespace:ENI2.Controls"
///
///
/// Step 1b) Using this custom control in a XAML file that exists in a different project.
/// Add this XmlNamespace attribute to the root element of the markup file where it is
/// to be used:
///
/// xmlns:enictrl="clr-namespace:ENI2.Controls;assembly=ENI2.Controls"
///
/// You will also need to add a project reference from the project where the XAML file lives
/// to this project and Rebuild to avoid compilation errors:
///
/// Right click on the target project in the Solution Explorer and
/// "Add Reference"->"Projects"->[Browse to and select this project]
///
///
/// Step 2)
/// Go ahead and use your control in the XAML file.
///
/// <MyNamespace:ENIDataGrid/>
///
/// </summary>
public class ENIDataGrid : DataGrid
{
/*
static ENIDataGrid()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ENIDataGrid), new FrameworkPropertyMetadata(typeof(ENIDataGrid)));
}
*/
// das hier bildet 1:1 das Kontext-Menü des ANSW ab
public event Action<DbEntity>? EditRequested;
public event Action<DbEntity>? DeleteRequested;
public event Action? CreateRequested;
public event Action? RefreshGrid;
public event Action<DbEntity>? PrintRequested;
public event Action<DbEntity>? ExportRequested;
public event Action<DbEntity>? ShowTextRequested;
public void Initialize()
{
this.MouseDoubleClick += dataGrid_MouseDoubleClick;
this.PreviewKeyDown += ENIDataGrid_PreviewKeyDown;
this.ContextMenu = new ContextMenu();
this.CanUserAddRows = false;
this.IsReadOnly = false;
MenuItem addItem = new MenuItem();
addItem.Header = RoleEditor.Resources.ResourceManager.GetString("textAdd");
addItem.Icon = new Image { Source = Util.LoadImage(RoleEditor.Resources.add) };
addItem.Click += new RoutedEventHandler(this.addItem);
this.ContextMenu.Items.Add(addItem);
MenuItem deleteItem = new MenuItem();
deleteItem.Header = RoleEditor.Resources.ResourceManager.GetString("textDelete");
deleteItem.Icon = new Image { Source = Util.LoadImage(RoleEditor.Resources.delete) };
deleteItem.Click += this.deleteItem;
this.ContextMenu.Items.Add(deleteItem);
MenuItem editItem = new MenuItem();
editItem.Header = RoleEditor.Resources.ResourceManager.GetString("textEdit");
editItem.Icon = new Image { Source = Util.LoadImage(RoleEditor.Resources.edit) };
editItem.Click += this.editItem;
this.ContextMenu.Items.Add(editItem);
/*
this.ContextMenu.Items.Add(new Separator());
MenuItem printItem = new MenuItem();
printItem.Header = RoleEditor.Resources.ResourceManager.GetString("textPrint");
printItem.Icon = new Image { Source = Util.LoadImage(RoleEditor.Resources.printer) };
printItem.Click += this.printItem;
this.ContextMenu.Items.Add(printItem);
MenuItem exportItem = new MenuItem();
exportItem.Header = RoleEditor.Resources.ResourceManager.GetString("textExport");
exportItem.Icon = new Image { Source = Util.LoadImage(RoleEditor.Resources.floppy_disk_blue) };
exportItem.Click += this.exportItem;
this.ContextMenu.Items.Add(exportItem);
MenuItem showTextItem = new MenuItem();
showTextItem.Header = RoleEditor.Resources.ResourceManager.GetString("textShowAsText");
showTextItem.Icon = new Image { Source = Util.LoadImage(RoleEditor.Resources.document_plain) };
showTextItem.Click += this.showTextItem;
this.ContextMenu.Items.Add(showTextItem);
*/
}
private void ENIDataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
{
if(sender is ENIDataGrid)
{
var grid = sender as ENIDataGrid;
if (Key.Delete == e.Key)
this.deleteItem(null, null);
}
}
#region public
public DataGridRow GetRow(int index)
{
DataGridRow row = (DataGridRow)this.ItemContainerGenerator.ContainerFromIndex(index);
if(row == null)
{
this.UpdateLayout();
this.ScrollIntoView(this.Items[index]);
row = (DataGridRow)this.ItemContainerGenerator.ContainerFromIndex(index);
}
return row;
}
public DataGridCell? GetCell(DataGridRow row, int column)
{
if (row != null)
{
DataGridCellsPresenter? presenter = GetVisualChild<DataGridCellsPresenter>(row);
if (presenter == null)
{
this.ScrollIntoView(row, this.Columns[column]);
presenter = GetVisualChild<DataGridCellsPresenter>(row);
}
DataGridCell? cell = (DataGridCell?)presenter?.ItemContainerGenerator.ContainerFromIndex(column);
return cell;
}
return null;
}
public DataGridCell? GetCell(int rowIndex, int columnIndex)
{
DataGridRow row = this.GetRow(rowIndex);
return this.GetCell(row, columnIndex);
}
#endregion
#region protected
protected void addItem(object sender, RoutedEventArgs e)
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-us");
if (!this.IsReadOnly)
{
this.CreateRequested?.Invoke();
}
e.Handled = true;
}
protected void deleteItem(object? sender, RoutedEventArgs? e)
{
if((this.SelectedItems != null) && (this.SelectedItems.Count > 0) && !this.IsReadOnly)
{
MessageBoxResult result = MessageBox.Show("Are your sure?", "Please confirm", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
List<DbEntity> deleteList = new List<DbEntity>();
foreach (DbEntity deleteItem in this.SelectedItems)
deleteList.Add(deleteItem);
foreach (DbEntity deleteItem in deleteList)
{
if (deleteItem != null)
{
this.DeleteRequested?.Invoke(deleteItem);
}
}
this.RefreshGrid?.Invoke();
}
}
}
protected void editItem(object sender, RoutedEventArgs e)
{
if((this.SelectedItems != null) && (this.SelectedItems.Count == 1) && !this.IsReadOnly)
{
if (this.SelectedItems[0] is DbEntity selectedEntity)
this.EditRequested?.Invoke(selectedEntity);
}
}
protected void printItem(object sender, RoutedEventArgs e)
{
if ((this.SelectedItems != null) && (this.SelectedItems.Count == 1) )
{
if (this.SelectedItems[0] is DbEntity selectedEntity)
this.PrintRequested?.Invoke(selectedEntity);
}
}
protected void exportItem(object sender, RoutedEventArgs e)
{
if ((this.SelectedItems != null) && (this.SelectedItems.Count == 1))
{
if (this.SelectedItems[0] is DbEntity selectedEntity)
this.ExportRequested?.Invoke(selectedEntity);
}
}
protected void showTextItem(object sender, RoutedEventArgs e)
{
if ((this.SelectedItems != null) && (this.SelectedItems.Count == 1))
{
if (this.SelectedItems[0] is DbEntity selectedEntity)
this.ShowTextRequested?.Invoke(selectedEntity);
}
}
#endregion
#region private
private void dataGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (sender != null)
{
if ((sender is DataGrid grid) && (grid.SelectedItems != null) && (grid.SelectedItems.Count == 1) && !this.IsReadOnly)
{
DataGridRow? dgr = grid.ItemContainerGenerator.ContainerFromItem(grid.SelectedItem) as DataGridRow;
if (grid.SelectedItem is DbEntity selectedEntity)
this.EditRequested?.Invoke(selectedEntity);
}
}
}
#endregion
#region private static
private static T? GetVisualChild<T>(Visual parent) where T : Visual
{
T? child = default;
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual? v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
#endregion
}
}

View File

@ -0,0 +1,41 @@
<Window x:Class="RoleEditor.EditBerthDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RoleEditor"
mc:Ignorable="d"
Title="Edit berth" Height="160" Width="450" Loaded="Window_Loaded">
<Grid x:Name="berthGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".3*" />
<ColumnDefinition Width=".6*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<Label Content="Name" HorizontalAlignment="Right" />
<TextBox x:Name="textBoxName" Grid.Column="1" Margin="2" VerticalContentAlignment="Center" Text="{Binding Name, Mode=OneWay}" />
<Label Content="Participant / Terminal" HorizontalAlignment="Right" Grid.Row="1" />
<Grid Grid.Row="1" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="28" />
</Grid.ColumnDefinitions>
<ComboBox x:Name="comboBoxParticipants" Margin="2" SelectedItem="{Binding Participant, Mode=OneWay}" />
<Button x:Name="buttonResetParticipant" Grid.Column="1" Margin="2" Click="buttonResetParticipant_Click">
<Image Source="./Resources/delete2.png"/>
</Button>
</Grid>
<Label Content="Uses lock" HorizontalAlignment="Right" Grid.Row="2" />
<CheckBox x:Name="checkBoxLock" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" Margin="2" IsChecked="{Binding Path=Lock, Mode=OneWay}"/>
<StackPanel Grid.Column="1" Grid.Row="4" Orientation="Horizontal" FlowDirection="RightToLeft">
<Button x:Name="buttonCancel" Width="80" Content="Cancel" Margin="2" Click="buttonCancel_Click" />
<Button x:Name="buttonOK" Width="80" Content="OK" Margin="2" Click="buttonOK_Click"/>
</StackPanel>
</Grid>
</Window>

View File

@ -0,0 +1,51 @@
using brecal.model;
using System.Collections.Generic;
using System.Windows;
namespace RoleEditor
{
/// <summary>
/// Interaction logic for EditBerthDialog.xaml
/// </summary>
public partial class EditBerthDialog : Window
{
public EditBerthDialog()
{
InitializeComponent();
}
public Berth Berth { get; set; } = new Berth();
public List<Participant> Participants { get; } = new List<Participant>();
private void buttonCancel_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = false;
this.Close();
}
private void buttonOK_Click(object sender, RoutedEventArgs e)
{
this.Berth.Name = this.textBoxName.Text.Trim();
this.Berth.Lock = this.checkBoxLock.IsChecked;
this.Berth.Participant = this.comboBoxParticipants.SelectedItem as Participant;
if (this.Berth.Participant != null)
this.Berth.Participant_Id = this.Berth.Participant.Id;
else
this.Berth.Participant_Id = null;
this.DialogResult = true;
this.Close();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = this.Berth;
this.comboBoxParticipants.ItemsSource = this.Participants;
}
private void buttonResetParticipant_Click(object sender, RoutedEventArgs e)
{
this.comboBoxParticipants.SelectedItem = null;
}
}
}

View File

@ -0,0 +1,52 @@
<Window x:Class="RoleEditor.EditShipDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RoleEditor"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
mc:Ignorable="d"
Title="Edit ship" Height="250" Width="500" Loaded="Window_Loaded">
<Grid x:Name="shipGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".3*" />
<ColumnDefinition Width=".6*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="28" />
<RowDefinition Height="*" />
<RowDefinition Height="28" />
</Grid.RowDefinitions>
<Label Content="Name" HorizontalAlignment="Right" />
<TextBox x:Name="textBoxName" Grid.Column="1" Margin="2" VerticalContentAlignment="Center" Text="{Binding Name, Mode=OneWay}" />
<Label Content="Participant / Tug company" HorizontalAlignment="Right" Grid.Row="1" />
<Grid Grid.Row="1" Grid.Column="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="28" />
</Grid.ColumnDefinitions>
<ComboBox x:Name="comboBoxParticipants" Margin="2" SelectedItem="{Binding Participant, Mode=OneWay}" />
<Button x:Name="buttonResetParticipant" Grid.Column="1" Margin="2" Click="buttonResetParticipant_Click">
<Image Source="./Resources/delete2.png"/>
</Button>
</Grid>
<Label Content="IMO" HorizontalAlignment="Right" Grid.Row="2" />
<xctk:IntegerUpDown Name="integerUpDownIMO" Grid.Column="1" Grid.Row="2" Value="{Binding IMO, Mode=OneWay}" Margin="2" Minimum="1000000" Maximum="9999999"/>
<Label Content="Callsign" HorizontalAlignment="Right" Grid.Row="3" />
<TextBox x:Name="textBoxCallsign" Grid.Column="1" Grid.Row="3" Margin="2" VerticalContentAlignment="Center" Text="{Binding Callsign, Mode=OneWay}" />
<Label Content="Length" HorizontalAlignment="Right" Grid.Row="4" />
<xctk:DoubleUpDown Name="doubleUpDownLength" Grid.Row="4" Grid.Column="1" Value="{Binding Length, Mode=OneWay}" Margin="2" Minimum="0" />
<Label Content="Width" HorizontalAlignment="Right" Grid.Row="5" />
<xctk:DoubleUpDown Name="doubleUpDownWidth" Grid.Row="5" Grid.Column="1" Value="{Binding Width, Mode=OneWay}" Margin="2" Minimum="0"/>
<StackPanel Grid.Column="1" Grid.Row="7" Orientation="Horizontal" FlowDirection="RightToLeft">
<Button x:Name="buttonCancel" Width="80" Content="Cancel" Margin="2" Click="buttonCancel_Click" />
<Button x:Name="buttonOK" Width="80" Content="OK" Margin="2" Click="buttonOK_Click"/>
</StackPanel>
</Grid>
</Window>

View File

@ -0,0 +1,55 @@
using brecal.model;
using System.Collections.Generic;
using System.Windows;
namespace RoleEditor
{
/// <summary>
/// Interaction logic for EditShipDialog.xaml
/// </summary>
public partial class EditShipDialog : Window
{
public EditShipDialog()
{
InitializeComponent();
}
public Ship Ship { get; set; } = new Ship();
public List<Participant> Participants { get; } = new List<Participant>();
private void buttonCancel_Click(object sender, RoutedEventArgs e)
{
this.DialogResult = false;
this.Close();
}
private void buttonOK_Click(object sender, RoutedEventArgs e)
{
this.Ship.Name = this.textBoxName.Text.Trim();
this.Ship.Participant = this.comboBoxParticipants.SelectedItem as Participant;
if (this.Ship.Participant != null)
this.Ship.Participant_Id = this.Ship.Participant.Id;
else
this.Ship.Participant_Id = null;
this.Ship.IMO = this.integerUpDownIMO.Value;
this.Ship.Callsign = this.textBoxCallsign.Text.Trim();
this.Ship.Length = this.doubleUpDownLength.Value;
this.Ship.Width = this.doubleUpDownWidth.Value;
this.DialogResult = true;
this.Close();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = this.Ship;
this.comboBoxParticipants.ItemsSource = this.Participants;
}
private void buttonResetParticipant_Click(object sender, RoutedEventArgs e)
{
this.comboBoxParticipants.SelectedItem = null;
}
}
}

View File

@ -0,0 +1,284 @@
<Window x:Class="RoleEditor.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:RoleEditor"
mc:Ignorable="d"
Title="Bremen calling admin editor" Height="600" Width="800" Icon="Resources/lock_preferences.ico" Loaded="Window_Loaded">
<Grid>
<TabControl>
<TabItem Header="Participant, users and roles">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=".5*" />
<RowDefinition Height=".5*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*" />
<ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions>
<GroupBox Header="Participant" Margin="2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160" />
<ColumnDefinition Width=".38*" />
<ColumnDefinition Width=".62*" />
</Grid.ColumnDefinitions>
<ListBox x:Name="listBoxParticipant" Margin="2" Grid.RowSpan="7" SelectionChanged="listBoxParticipant_SelectionChanged">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem x:Name="menuItemNewParticipant" Header="New.." Click="menuItemNewParticipant_Click">
<MenuItem.Icon>
<Image Source="Resources/add.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="menuItemDeleteParticipant" Header="Delete" Click="menuItemDeleteParticipant_Click">
<MenuItem.Icon>
<Image Source="Resources/delete2.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
<Label Content="Name" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Right"/>
<Label Content="Street" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Right"/>
<Label Content="Postal code" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Right"/>
<Label Content="City" Grid.Row="3" Grid.Column="1" HorizontalAlignment="Right"/>
<Label Content="Active" Grid.Row="4" Grid.Column="1" HorizontalAlignment="Right"/>
<Label Content="Created" Grid.Row="5" Grid.Column="1" HorizontalAlignment="Right"/>
<Label Content="Modified" Grid.Row="6" Grid.Column="1" HorizontalAlignment="Right"/>
<TextBox x:Name="textBoxParticipantName" Grid.Row="0" Grid.Column="2" Margin="2" VerticalContentAlignment="Center" />
<TextBox x:Name="textBoxParticipantStreet" Grid.Row="1" Grid.Column="2" Margin="2" VerticalContentAlignment="Center" />
<TextBox x:Name="textBoxParticipantPostalCode" Grid.Row="2" Grid.Column="2" Margin="2" VerticalContentAlignment="Center" />
<TextBox x:Name="textBoxParticipantCity" Grid.Row="3" Grid.Column="2" Margin="2" VerticalContentAlignment="Center" />
<CheckBox x:Name="checkboxParticipantActive" Grid.Row="4" Grid.Column="2" VerticalAlignment="Center" />
<TextBox x:Name="textBoxParticipantCreated" Grid.Row="5" IsReadOnly="True" IsEnabled="False" Grid.Column="2" Margin="2" VerticalContentAlignment="Center" />
<TextBox x:Name="textBoxParticipantModified" Grid.Row="6" IsReadOnly="True" IsEnabled="False" Grid.Column="2" Margin="2" VerticalContentAlignment="Center" />
<Button x:Name="buttonParticipantSave" Grid.Row="7" Grid.Column="2" Click="buttonParticipantSave_Click" Margin="2">
<DockPanel>
<Image Source="./Resources/disk_blue.png" Margin="0,0,5,0" Height="24" DockPanel.Dock="Left" Width="16"/>
<TextBlock Text="Save" VerticalAlignment="Center" DockPanel.Dock="Right"/>
</DockPanel>
</Button>
</Grid>
</GroupBox>
<GroupBox Header="User" Margin="2" Grid.Row="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160" />
<ColumnDefinition Width=".38*" />
<ColumnDefinition Width=".62*" />
</Grid.ColumnDefinitions>
<ListBox x:Name="listBoxUser" Margin="2" Grid.RowSpan="9" SelectionChanged="listBoxUser_SelectionChanged">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem x:Name="menuItemNewUser" Header="New.." Click="menuItemNewUser_Click">
<MenuItem.Icon>
<Image Source="Resources/add.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="menuItemDeleteUser" Header="Delete" Click="menuItemDeleteUser_Click">
<MenuItem.Icon>
<Image Source="Resources/delete2.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
<Label Grid.Row="0" Grid.Column="1" Content="First name" HorizontalAlignment="Right"/>
<Label Grid.Row="1" Grid.Column="1" Content="Last name" HorizontalAlignment="Right"/>
<Label Grid.Row="2" Grid.Column="1" Content="User name" HorizontalAlignment="Right"/>
<Label Grid.Row="3" Grid.Column="1" Content="Password" HorizontalAlignment="Right"/>
<Label Grid.Row="4" Grid.Column="1" Content="API Key" HorizontalAlignment="Right"/>
<Label Content="Created" Grid.Row="5" Grid.Column="1" HorizontalAlignment="Right"/>
<Label Content="Modified" Grid.Row="6" Grid.Column="1" HorizontalAlignment="Right"/>
<TextBox x:Name="textBoxUserFirstName" Grid.Row="0" Grid.Column="2" Margin="2" VerticalContentAlignment="Center" />
<TextBox x:Name="textBoxUserLastName" Grid.Row="1" Grid.Column="2" Margin="2" VerticalContentAlignment="Center" />
<TextBox x:Name="textBoxUserUserName" Grid.Row="2" Grid.Column="2" Margin="2" VerticalContentAlignment="Center" />
<TextBox x:Name="textBoxUserPassword" Grid.Row="3" Grid.Column="2" Margin="2" VerticalContentAlignment="Center" />
<TextBox x:Name="textBoxUserAPIKey" Grid.Row="4" Grid.Column="2" Margin="2" VerticalContentAlignment="Center" />
<TextBox x:Name="textBoxUserCreated" Grid.Row="5" IsReadOnly="True" IsEnabled="False" Grid.Column="2" Margin="2" VerticalContentAlignment="Center" />
<TextBox x:Name="textBoxUserModified" Grid.Row="6" IsReadOnly="True" IsEnabled="False" Grid.Column="2" Margin="2" VerticalContentAlignment="Center" />
<Button x:Name="buttonUserSave" Grid.Row="7" Grid.Column="2" Click="buttonUserSave_Click" Margin="2">
<DockPanel>
<Image Source="./Resources/disk_blue.png" Margin="0,0,5,0" Height="24" DockPanel.Dock="Left" Width="16"/>
<TextBlock Text="Save" VerticalAlignment="Center" DockPanel.Dock="Right"/>
</DockPanel>
</Button>
</Grid>
</GroupBox>
<GroupBox Header="Role" Margin="2" Grid.Column="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="*"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*" />
<ColumnDefinition Width="60" />
<ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions>
<Label Content="User roles" Grid.Row="0" Grid.Column="0" />
<Label Content="All roles" Grid.Row="0" Grid.Column="2" />
<ListBox x:Name="listBoxUserRoles" Grid.Row="1" Grid.Column="0" Grid.RowSpan="3" Margin="2" />
<ListBox x:Name="listBoxRoles" Grid.Row="1" Grid.Column="2" Grid.RowSpan="3" Margin="2" SelectionChanged="listBoxRoles_SelectionChanged">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem x:Name="menuItemNewRole" Header="New.." Click="menuItemNewRole_Click">
<MenuItem.Icon>
<Image Source="Resources/add.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="menuItemDeleteRole" Header="Delete" Click="menuItemDeleteRole_Click">
<MenuItem.Icon>
<Image Source="Resources/delete2.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
<Button x:Name="buttonAddRole" Margin="2" Grid.Row="1" Grid.Column="1" Click="buttonAddRole_Click">
<Image Source="./Resources/arrow_left_green.png"/>
</Button>
<Button x:Name="buttonRemoveRole" Margin="2" Grid.Row="2" Grid.Column="1" Click="buttonRemoveRole_Click">
<Image Source="./Resources/delete2.png"/>
</Button>
<Label Content="Name" Grid.Row="4" Grid.Column="1" HorizontalAlignment="Right" />
<Label Content="Descr." Grid.Row="5" Grid.Column="1" HorizontalAlignment="Right" />
<TextBox x:Name="textBoxRoleName" Grid.Row="4" Grid.Column="2" Margin="2" VerticalContentAlignment="Center"/>
<TextBox x:Name="textBoxRoleDescription" Grid.Row="5" Grid.Column="2" Grid.RowSpan="2" Margin="2" VerticalContentAlignment="Top"/>
<Button x:Name="buttonSaveRole" Grid.Row="7" Grid.Column="2" Margin="2" Click="buttonSaveRole_Click">
<DockPanel>
<Image Source="./Resources/disk_blue.png" Margin="0,0,5,0" Height="24" DockPanel.Dock="Left" Width="16"/>
<TextBlock Text="Save" VerticalAlignment="Center" DockPanel.Dock="Right"/>
</DockPanel>
</Button>
</Grid>
</GroupBox>
<GroupBox Header="Securable" Margin="2" Grid.Row="1" Grid.Column="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
<RowDefinition Height="*"/>
<RowDefinition Height="28"/>
<RowDefinition Height="28"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*" />
<ColumnDefinition Width="60" />
<ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Role securables" />
<Label Grid.Row="0" Grid.Column="2" Content="Securables" />
<ListBox x:Name="listBoxRoleSecurables" Grid.Row="1" Grid.Column="0" Grid.RowSpan="3" Margin="2" />
<ListBox x:Name="listBoxSecurables" Grid.Row="1" Grid.Column="2" Grid.RowSpan="3" Margin="2" SelectionChanged="listBoxSecurables_SelectionChanged">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem x:Name="menuItemNewSecurable" Header="New.." Click="menuItemNewSecurable_Click">
<MenuItem.Icon>
<Image Source="Resources/add.png" />
</MenuItem.Icon>
</MenuItem>
<MenuItem x:Name="menuItemDeleteSecurable" Header="Delete" Click="menuItemDeleteSecurable_Click">
<MenuItem.Icon>
<Image Source="Resources/delete2.png" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
<Button x:Name="buttonAddSecurable" Margin="2" Grid.Row="1" Grid.Column="1" Click="buttonAddSecurable_Click">
<Image Source="./Resources/arrow_left_green.png"/>
</Button>
<Button x:Name="buttonRemoveSecurable" Margin="2" Grid.Row="2" Grid.Column="1" Click="buttonRemoveSecurable_Click">
<Image Source="./Resources/delete2.png"/>
</Button>
<Label Content="Name" Grid.Row="4" Grid.Column="1" HorizontalAlignment="Right" />
<TextBox x:Name="textBoxSecurableName" Grid.Row="4" Grid.Column="2" Margin="2" VerticalContentAlignment="Center"/>
<Button x:Name="buttonSaveSecurable" Grid.Row="5" Grid.Column="2" Margin="2" Click="buttonSaveSecurable_Click">
<DockPanel>
<Image Source="./Resources/disk_blue.png" Margin="0,0,5,0" Height="24" DockPanel.Dock="Left" Width="16"/>
<TextBlock Text="Save" VerticalAlignment="Center" DockPanel.Dock="Right"/>
</DockPanel>
</Button>
</Grid>
</GroupBox>
</Grid>
</TabItem>
<TabItem Header="Berths">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="28" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Content="Import excel" Margin="2" x:Name="buttonImportBerths" Click="buttonImportBerths_Click" Width="100" HorizontalAlignment="Left" Grid.Row="0"/>
<local:ENIDataGrid x:Name="dataGridBerths" Grid.Row="1" SelectionMode="Single" IsReadOnly="True" AlternatingRowBackground="LightBlue" AutoGenerateColumns="False"
CanUserAddRows="False" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Path=Id}" IsReadOnly="True"/>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" IsReadOnly="True"/>
<DataGridCheckBoxColumn Header="Lock" Binding="{Binding Path=Lock}" IsReadOnly="True"/>
<DataGridTextColumn Header="Terminal" Binding="{Binding Path=Terminal, Mode=OneWay}" IsReadOnly="True"/>
</DataGrid.Columns>
</local:ENIDataGrid>
</Grid>
</TabItem>
<TabItem Header="Ships">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="28" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Content="Import excel" Margin="2" x:Name="buttonImportShipss" Click="buttonImportShipss_Click" Width="100" HorizontalAlignment="Left" Grid.Row="0"/>
<local:ENIDataGrid x:Name="dataGridShips" Grid.Row="1" SelectionMode="Single" IsReadOnly="True" AlternatingRowBackground="LightBlue" AutoGenerateColumns="False"
CanUserAddRows="False" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Path=Id}" IsReadOnly="True"/>
<DataGridTextColumn Header="Name" Binding="{Binding Path=Name}" IsReadOnly="True"/>
<DataGridCheckBoxColumn Header="Tug" Binding="{Binding Path=IsTug, Mode=OneWay}" IsReadOnly="True"/>
<DataGridTextColumn Header="Tug company" Binding="{Binding Path=TugCompany, Mode=OneWay}" IsReadOnly="True"/>
<DataGridTextColumn Header="IMO" Binding="{Binding Path=IMO}" IsReadOnly="True"/>
<DataGridTextColumn Header="Callsign" Binding="{Binding Path=Callsign}" IsReadOnly="True"/>
<DataGridTextColumn Header="Length" Binding="{Binding Path=Length}" IsReadOnly="True"/>
<DataGridTextColumn Header="Width" Binding="{Binding Path=Width}" IsReadOnly="True"/>
</DataGrid.Columns>
</local:ENIDataGrid>
</Grid>
</TabItem>
</TabControl>
</Grid>
</Window>

View File

@ -0,0 +1,574 @@

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using brecal.model;
using brecal.mysql;
namespace RoleEditor
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
#region private fields
private readonly ObservableCollection<Participant> _participants = new ObservableCollection<Participant>();
private readonly ObservableCollection<Role> _roles = new ObservableCollection<Role>();
private readonly ObservableCollection<Securable> _securables = new ObservableCollection<Securable>();
private readonly ObservableCollection<User> _users = new ObservableCollection<User>();
private readonly ObservableCollection<RoleAssignment> _assignedRoles = new ObservableCollection<RoleAssignment>();
private readonly ObservableCollection<SecurableAssignment> _assignedSecurables = new ObservableCollection<SecurableAssignment>();
private readonly ObservableCollection<Berth> _berths = new ObservableCollection<Berth>();
private readonly ObservableCollection<Ship> _ships = new ObservableCollection<Ship>();
private DBManager _dbManager;
#endregion
#region Construction / Loading
public MainWindow()
{
InitializeComponent();
_dbManager = new();
}
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
// try database connection
try
{
// load all participants
List<Participant> participants = await Participant.LoadAll(_dbManager);
foreach(Participant p in participants)
_participants.Add(p);
this.listBoxParticipant.ItemsSource = _participants;
// load all roles
foreach(Role r in await Role.LoadAll(_dbManager))
_roles.Add(r);
this.listBoxRoles.ItemsSource = _roles;
// load all securables
foreach(Securable s in await Securable.LoadAll(_dbManager))
_securables.Add(s);
this.listBoxSecurables.ItemsSource = _securables;
// load all berths
foreach (Berth b in await Berth.LoadAll(_dbManager))
{
_berths.Add(b);
if(b.Participant_Id != null)
{
b.Participant = participants.Where( p => p.Id== b.Participant_Id ).FirstOrDefault();
}
}
this.dataGridBerths.Initialize();
this.dataGridBerths.ItemsSource = _berths;
this.dataGridBerths.CreateRequested += DataGridBerths_CreateRequested;
this.dataGridBerths.EditRequested += DataGridBerths_EditRequested;
this.dataGridBerths.DeleteRequested += DataGridBerths_DeleteRequested;
// load all ships
foreach (Ship s in await Ship.LoadAll(_dbManager))
{
_ships.Add(s);
if(s.Participant_Id != null)
{
s.Participant = participants.Where( p => p.Id == s.Participant_Id ).FirstOrDefault();
}
}
this.dataGridShips.Initialize();
this.dataGridShips.ItemsSource = _ships;
this.dataGridShips.CreateRequested += DataGridShips_CreateRequested;
this.dataGridShips.EditRequested += DataGridShips_EditRequested;
this.dataGridShips.DeleteRequested += DataGridShips_DeleteRequested;
// set other item sources (filled later after selection)
this.listBoxUser.ItemsSource = _users;
this.listBoxRoleSecurables.ItemsSource = _assignedSecurables;
this.listBoxUserRoles.ItemsSource = _assignedRoles;
}
catch (Exception ex)
{
MessageBox.Show($"Database connection couldn't be established: {ex.Message}");
this.Close();
}
}
#region ship context menu callbacks
private void DataGridShips_DeleteRequested(DbEntity obj)
{
if(obj is Ship s)
this._ships.Remove(s);
}
private async void DataGridShips_EditRequested(DbEntity obj)
{
if(obj is Ship s)
{
EditShipDialog esd = new();
esd.Ship = s;
esd.Participants.AddRange(this._participants);
if (esd.ShowDialog() ?? false)
{
await s.Save(_dbManager);
this.dataGridShips.ItemsSource = null;
this.dataGridShips.ItemsSource = _ships;
}
}
}
private async void DataGridShips_CreateRequested()
{
Ship s = new();
EditShipDialog esd = new();
esd.Ship = s;
esd.Participants.AddRange(this._participants);
if(esd.ShowDialog() ?? false)
{
_ships.Add(s);
await s.Save(_dbManager);
this.dataGridShips.ItemsSource = null;
this.dataGridShips.ItemsSource = _ships;
}
}
#endregion
#region berth context menu callbacks
private void DataGridBerths_DeleteRequested(DbEntity obj)
{
if(obj is Berth b)
this._berths.Remove(b);
}
private async void DataGridBerths_EditRequested(DbEntity obj)
{
if(obj is Berth b)
{
EditBerthDialog ebd = new();
ebd.Berth = b;
ebd.Participants.AddRange(this._participants);
if (ebd.ShowDialog() ?? false)
{
await b.Save(_dbManager);
this.dataGridBerths.ItemsSource = null; // extreme stupidity for me not wanting to implement INotifyPropertyChanged
this.dataGridBerths.ItemsSource = _berths;
}
}
}
private async void DataGridBerths_CreateRequested()
{
Berth b = new();
EditBerthDialog ebd = new();
ebd.Berth = b;
ebd.Participants.AddRange(this._participants);
if (ebd.ShowDialog() ?? false)
{
_berths.Add(b);
await b.Save(_dbManager);
this.dataGridBerths.ItemsSource = null;
this.dataGridBerths.ItemsSource = _berths;
}
}
#endregion
#endregion
#region button callbacks
private async void buttonParticipantSave_Click(object sender, RoutedEventArgs e)
{
Participant? p = this.listBoxParticipant.SelectedItem as Participant;
if (p != null)
{
p.Name = this.textBoxParticipantName.Text.Trim();
p.Street = this.textBoxParticipantStreet.Text.Trim();
p.PostalCode = this.textBoxParticipantPostalCode.Text.Trim();
p.City = this.textBoxParticipantCity.Text.Trim();
await p.Save(_dbManager);
this.listBoxParticipant.ItemsSource = null;
this.listBoxParticipant.ItemsSource = _users;
this.listBoxParticipant.SelectedItem = p;
}
}
private async void buttonUserSave_Click(object sender, RoutedEventArgs e)
{
User? u = this.listBoxUser.SelectedItem as User;
if(u != null)
{
u.Firstname = this.textBoxUserFirstName.Text.Trim();
u.Lastname = this.textBoxUserLastName.Text.Trim();
u.Username = this.textBoxUserUserName.Text.Trim();
if(this.textBoxUserPassword.Text.Trim().Length > 0 )
{
var data = Encoding.UTF8.GetBytes(this.textBoxUserPassword.Text.Trim());
using SHA512 sha = SHA512.Create();
byte[] hashedInputBytes = sha.ComputeHash(data);
var hashedInputStringBuilder = new StringBuilder(128);
foreach (var b in hashedInputBytes)
hashedInputStringBuilder.Append(b.ToString("X2"));
u.PasswordHash = hashedInputStringBuilder.ToString();
}
u.APIKey = this.textBoxUserAPIKey.Text.Trim();
await u.Save(_dbManager);
this.listBoxUser.ItemsSource = null;
this.listBoxUser.ItemsSource = _users;
this.listBoxUser.SelectedItem = u;
}
}
private async void buttonAddRole_Click(object sender, RoutedEventArgs e)
{
Role? r = this.listBoxRoles.SelectedItem as Role;
User? u = this.listBoxUser.SelectedItem as User;
if((r != null) && (u != null))
{
// test if assignment is already present
bool foundMatchingAssignment = false;
foreach(RoleAssignment ra in _assignedRoles)
{
if((ra.UserId == u.Id) && (ra.RoleId == r.Id))
{
foundMatchingAssignment = true;
break;
}
}
if(!foundMatchingAssignment)
{
RoleAssignment ra = new RoleAssignment();
ra.UserId = (int) u.Id;
ra.RoleId = (int) r.Id;
ra.AssignedRole = r;
ra.AssignedUser = u;
await ra.Save(_dbManager);
_assignedRoles.Add(ra);
}
}
}
private async void buttonRemoveRole_Click(object sender, RoutedEventArgs e)
{
// remove role from user
RoleAssignment? ra = this.listBoxUserRoles.SelectedItem as RoleAssignment;
if(ra != null)
{
await ra.Delete(_dbManager);
if(_assignedRoles.Contains(ra))
_assignedRoles.Remove(ra);
}
}
private async void buttonAddSecurable_Click(object sender, RoutedEventArgs e)
{
if ((this.listBoxRoles.SelectedItem is Role r) && (this.listBoxSecurables.SelectedItem is Securable s))
{
// test if assignment is already present
bool foundMatchingAssignment = false;
foreach (SecurableAssignment sa in _assignedSecurables)
{
if ((sa.SecurableId == s.Id) && (sa.RoleId == r.Id))
{
foundMatchingAssignment = true;
break;
}
}
if (!foundMatchingAssignment)
{
SecurableAssignment sa = new SecurableAssignment();
sa.SecurableId = (int)s.Id;
sa.RoleId = (int)r.Id;
sa.AssignedRole = r;
sa.AssignedSecurable = s;
await sa.Save(_dbManager);
_assignedSecurables.Add(sa);
}
}
}
private async void buttonRemoveSecurable_Click(object sender, RoutedEventArgs e)
{
SecurableAssignment? sa = this.listBoxRoleSecurables.SelectedItem as SecurableAssignment;
if(sa != null)
{
await sa.Delete(_dbManager);
if (_assignedSecurables.Contains(sa))
_assignedSecurables.Remove(sa);
}
}
private async void buttonSaveSecurable_Click(object sender, RoutedEventArgs e)
{
Securable? s = this.listBoxSecurables.SelectedItem as Securable;
if(s != null)
{
s.Name = this.textBoxSecurableName.Text.Trim();
await s.Save(_dbManager);
this.listBoxSecurables.ItemsSource = null;
this.listBoxSecurables.ItemsSource = _securables;
this.listBoxSecurables.SelectedItem = s;
}
}
private async void buttonSaveRole_Click(object sender, RoutedEventArgs e)
{
Role? r = this.listBoxRoles.SelectedItem as Role;
if(r != null)
{
r.Name = this.textBoxRoleName.Text.Trim();
r.Description = this.textBoxRoleDescription.Text.Trim();
await r.Save(_dbManager);
this.listBoxRoles.ItemsSource = null;
this.listBoxRoles.ItemsSource = _roles;
this.listBoxRoles.SelectedItem = r;
}
}
#endregion
#region listbox selection callbacks
private async void listBoxParticipant_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Participant? p = this.listBoxParticipant.SelectedItem as Participant;
this.textBoxParticipantName.Text = (p != null) ? p.Name : string.Empty;
this.textBoxParticipantStreet.Text = (p != null) ? p.Street : string.Empty;
this.textBoxParticipantPostalCode.Text = (p != null) ? p.PostalCode : string.Empty;
this.textBoxParticipantCity.Text = (p != null) ? p.City : string.Empty;
// this.checkboxParticipantActive.Checked = (p != null) ? p.
this.textBoxParticipantCreated.Text = (p != null) ? p.Created.ToString() : string.Empty;
this.textBoxParticipantModified.Text = (p != null) ? p.Modified.ToString() : string.Empty;
// -> load users for this participant selection
this._users.Clear();
foreach (User u in await User.LoadForParticipant(p, _dbManager))
_users.Add(u);
}
private async void listBoxRoles_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Role? r = this.listBoxRoles.SelectedItem as Role;
this.textBoxRoleName.Text = (r != null) ? r.Name : string.Empty;
this.textBoxRoleDescription.Text = (r != null) ? r.Description : string.Empty;
_assignedSecurables.Clear();
if (r != null)
{
// load assigned securables
foreach (SecurableAssignment sa in await SecurableAssignment.LoadForRole(r, _dbManager))
{
foreach (Securable s in this._securables)
{
if (sa.SecurableId == s.Id)
{
sa.AssignedSecurable = s;
break;
}
}
_assignedSecurables.Add(sa);
}
}
}
private async void listBoxUser_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
User? u = this.listBoxUser.SelectedItem as User;
this.textBoxUserFirstName.Text = (u != null) ? u.Firstname : string.Empty;
this.textBoxUserLastName.Text = (u != null) ? u.Lastname : string.Empty;
this.textBoxUserUserName.Text = (u != null) ? u.Username : string.Empty;
this.textBoxUserAPIKey.Text = (u != null) ? u.APIKey : string.Empty;
this.textBoxUserCreated.Text = (u != null) ? u.Created.ToString() : string.Empty;
this.textBoxUserModified.Text = (u != null) ? u.Modified.ToString() : string.Empty;
this.textBoxUserPassword.Text = string.Empty;
_assignedRoles.Clear();
if (u != null)
{
// load roles assigned to user
foreach (RoleAssignment ra in await RoleAssignment.LoadForUser(u, _dbManager))
{
foreach (Role r in this._roles)
{
if (ra.RoleId == r.Id)
{
ra.AssignedRole = r;
break;
}
}
_assignedRoles.Add(ra);
}
}
}
private void listBoxSecurables_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Securable? s = this.listBoxSecurables.SelectedItem as Securable;
this.textBoxSecurableName.Text = (s != null) ? s.Name : string.Empty;
}
#endregion
#region menuitem callbacks
private async void menuItemDeleteParticipant_Click(object sender, RoutedEventArgs e)
{
try
{
if(this.listBoxParticipant.SelectedItem is Participant p)
{
await p.Delete(_dbManager);
this._participants.Remove(p);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void menuItemNewParticipant_Click(object sender, RoutedEventArgs e)
{
Participant p = new();
this._participants.Add(p);
this.listBoxParticipant.SelectedItem = p;
}
private void menuItemNewUser_Click(object sender, RoutedEventArgs e)
{
Participant? p = this.listBoxParticipant.SelectedItem as Participant;
if(p != null)
{
User u = new();
u.Participant_Id = p.Id;
_users.Add(u);
this.listBoxUser.SelectedItem = u;
}
}
private async void menuItemDeleteUser_Click(object sender, RoutedEventArgs e)
{
try
{
if (this.listBoxUser.SelectedItem is User u)
{
await u.Delete(_dbManager);
this._users.Remove(u);
}
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void menuItemNewRole_Click(object sender, RoutedEventArgs e)
{
Role r = new();
this._roles.Add(r);
this.listBoxRoles.SelectedItem = r;
}
private async void menuItemDeleteRole_Click(object sender, RoutedEventArgs e)
{
try
{
if (this.listBoxRoles.SelectedItem is Role r)
{
await r.Delete(_dbManager);
this._roles.Remove(r);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void menuItemNewSecurable_Click(object sender, RoutedEventArgs e)
{
Securable s = new Securable();
_securables.Add(s);
this.listBoxSecurables.SelectedItem = s;
}
private async void menuItemDeleteSecurable_Click(object sender, RoutedEventArgs e)
{
try
{
if (this.listBoxSecurables.SelectedItem is Securable s)
{
await s.Delete(_dbManager);
this._securables.Remove(s);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
#endregion
#region Excel import
private void buttonImportBerths_Click(object sender, RoutedEventArgs e)
{
}
private void buttonImportShipss_Click(object sender, RoutedEventArgs e)
{
}
#endregion
#region private static helper
private static BitmapImage? LoadImage(byte[] imageData)
{
if (imageData == null || imageData.Length == 0) return null;
var image = new BitmapImage();
using (var mem = new MemoryStream(imageData))
{
mem.Position = 0;
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = null;
image.StreamSource = mem;
image.EndInit();
}
image.Freeze();
return image;
}
#endregion
}
}

277
src/RoleEditor/Resources.Designer.cs generated Normal file
View File

@ -0,0 +1,277 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace RoleEditor {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RoleEditor.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] about {
get {
object obj = ResourceManager.GetObject("about", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] add {
get {
object obj = ResourceManager.GetObject("add", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] arrow_left_green {
get {
object obj = ResourceManager.GetObject("arrow_left_green", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] businessman {
get {
object obj = ResourceManager.GetObject("businessman", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] delete {
get {
object obj = ResourceManager.GetObject("delete", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] delete2 {
get {
object obj = ResourceManager.GetObject("delete2", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] disk_blue {
get {
object obj = ResourceManager.GetObject("disk_blue", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] document_plain {
get {
object obj = ResourceManager.GetObject("document_plain", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] edit {
get {
object obj = ResourceManager.GetObject("edit", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] floppy_disk_blue {
get {
object obj = ResourceManager.GetObject("floppy_disk_blue", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] id_card {
get {
object obj = ResourceManager.GetObject("id_card", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] key1 {
get {
object obj = ResourceManager.GetObject("key1", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] key1_add {
get {
object obj = ResourceManager.GetObject("key1_add", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] lock_preferences {
get {
object obj = ResourceManager.GetObject("lock_preferences", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] printer {
get {
object obj = ResourceManager.GetObject("printer", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] safe {
get {
object obj = ResourceManager.GetObject("safe", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized string similar to Add.
/// </summary>
internal static string textAdd {
get {
return ResourceManager.GetString("textAdd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Delete.
/// </summary>
internal static string textDelete {
get {
return ResourceManager.GetString("textDelete", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Edit.
/// </summary>
internal static string textEdit {
get {
return ResourceManager.GetString("textEdit", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Export.
/// </summary>
internal static string textExport {
get {
return ResourceManager.GetString("textExport", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Print.
/// </summary>
internal static string textPrint {
get {
return ResourceManager.GetString("textPrint", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to _Show as text.
/// </summary>
internal static string textShowAsText {
get {
return ResourceManager.GetString("textShowAsText", resourceCulture);
}
}
}
}

View File

@ -0,0 +1,187 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="about" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\about.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="add" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\add.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="arrow_left_green" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\arrow_left_green.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="businessman" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\businessman.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\delete.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="delete2" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\delete2.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="disk_blue" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\disk_blue.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="document_plain" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\document_plain.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="edit" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\edit.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="floppy_disk_blue" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\floppy_disk_blue.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="id_card" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\id_card.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="key1" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\key1.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="key1_add" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\key1_add.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="lock_preferences" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\lock_preferences.ico;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="printer" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\printer.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="safe" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\safe.png;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</data>
<data name="textAdd" xml:space="preserve">
<value>Add</value>
</data>
<data name="textDelete" xml:space="preserve">
<value>Delete</value>
</data>
<data name="textEdit" xml:space="preserve">
<value>Edit</value>
</data>
<data name="textExport" xml:space="preserve">
<value>Export</value>
</data>
<data name="textPrint" xml:space="preserve">
<value>Print</value>
</data>
<data name="textShowAsText" xml:space="preserve">
<value>_Show as text</value>
</data>
</root>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 928 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 674 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,75 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<ApplicationIcon>Resources\lock_preferences.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<None Remove="Resources\about.png" />
<None Remove="Resources\add.png" />
<None Remove="Resources\arrow_left_green.png" />
<None Remove="Resources\businessman.png" />
<None Remove="Resources\delete.png" />
<None Remove="Resources\delete2.png" />
<None Remove="Resources\disk_blue.png" />
<None Remove="Resources\document_plain.png" />
<None Remove="Resources\edit.png" />
<None Remove="Resources\floppy_disk_blue.png" />
<None Remove="Resources\id_card.png" />
<None Remove="Resources\key1.png" />
<None Remove="Resources\key1_add.png" />
<None Remove="Resources\printer.png" />
<None Remove="Resources\safe.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\brecal.model\brecal.model.csproj" />
<ProjectReference Include="..\brecal.mysql\brecal.mysql.csproj" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources/lock_preferences.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\about.png" />
<Resource Include="Resources\add.png" />
<Resource Include="Resources\arrow_left_green.png" />
<Resource Include="Resources\businessman.png" />
<Resource Include="Resources\delete.png" />
<Resource Include="Resources\delete2.png" />
<Resource Include="Resources\disk_blue.png" />
<Resource Include="Resources\document_plain.png" />
<Resource Include="Resources\edit.png" />
<Resource Include="Resources\floppy_disk_blue.png" />
<Resource Include="Resources\id_card.png" />
<Resource Include="Resources\key1.png" />
<Resource Include="Resources\key1_add.png" />
<Resource Include="Resources\printer.png" />
<Resource Include="Resources\safe.png" />
</ItemGroup>
<ItemGroup>
<Compile Update="Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@ -0,0 +1,37 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.33516.290
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RoleEditor", "RoleEditor.csproj", "{8A8CB0D3-7728-468E-AB11-E811BA5B5BC0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "brecal.model", "..\brecal.model\brecal.model.csproj", "{F3BC5ADC-BF57-47DC-A5D5-CC4A13857DEE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "brecal.mysql", "..\brecal.mysql\brecal.mysql.csproj", "{E88F908B-48C9-46BD-A3AE-C36FBE9EDF1F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8A8CB0D3-7728-468E-AB11-E811BA5B5BC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A8CB0D3-7728-468E-AB11-E811BA5B5BC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A8CB0D3-7728-468E-AB11-E811BA5B5BC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8A8CB0D3-7728-468E-AB11-E811BA5B5BC0}.Release|Any CPU.Build.0 = Release|Any CPU
{F3BC5ADC-BF57-47DC-A5D5-CC4A13857DEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F3BC5ADC-BF57-47DC-A5D5-CC4A13857DEE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F3BC5ADC-BF57-47DC-A5D5-CC4A13857DEE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F3BC5ADC-BF57-47DC-A5D5-CC4A13857DEE}.Release|Any CPU.Build.0 = Release|Any CPU
{E88F908B-48C9-46BD-A3AE-C36FBE9EDF1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E88F908B-48C9-46BD-A3AE-C36FBE9EDF1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E88F908B-48C9-46BD-A3AE-C36FBE9EDF1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E88F908B-48C9-46BD-A3AE-C36FBE9EDF1F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {12E46C08-C5A8-46E8-8A8C-04BA6F197DCB}
EndGlobalSection
EndGlobal

31
src/RoleEditor/Util.cs Normal file
View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
namespace RoleEditor
{
public static class Util
{
public static BitmapImage? LoadImage (byte[] imageData)
{
if (imageData == null || imageData.Length == 0) return null;
var image = new BitmapImage();
using (var mem = new MemoryStream(imageData))
{
mem.Position = 0;
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = null;
image.StreamSource = mem;
image.EndInit();
}
image.Freeze();
return image;
}
}
}

116
src/brecal.model/Berth.cs Normal file
View File

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace brecal.model
{
public class Berth : DbEntity
{
#region Properties
public string? Name { get; set; }
public bool? Lock { get; set; }
public uint? Participant_Id { get; set; }
public Participant? Participant { get; set; }
public string? Terminal { get { if (Participant != null) return Participant.Name; else return "n/a"; } }
#endregion
#region public static methods
public static async Task<List<Berth>> LoadAll(IDBManager manager)
{
List<DbEntity> loadResultList = await manager.Load(SetLoadQuery, LoadElems);
List<Berth> result = new();
foreach (Berth b in loadResultList.Cast<Berth>())
result.Add(b);
return result;
}
public static void SetLoadQuery(IDbCommand cmd, params object?[] list)
{
cmd.CommandText = "SELECT id, name, participant_id, `lock`, created, modified FROM berth";
}
public static List<DbEntity> LoadElems(IDataReader reader)
{
List<DbEntity> result = new List<DbEntity>();
while (reader.Read())
{
Berth b = new();
b.Id = (uint)reader.GetInt32(0);
if (!reader.IsDBNull(1)) b.Name = reader.GetString(1);
if (!reader.IsDBNull(2)) b.Participant_Id = (uint) reader.GetInt32(2);
if (!reader.IsDBNull(3)) b.Lock = reader.GetBoolean(3);
if (!reader.IsDBNull(4)) b.Created = reader.GetDateTime(4);
if (!reader.IsDBNull(5)) b.Modified = reader.GetDateTime(5);
result.Add(b);
}
return result;
}
#endregion
#region DbEntity implementation
public override void SetCreate(IDbCommand cmd)
{
cmd.CommandText = "INSERT INTO berth (participant_id, name, `lock`) VALUES ( @PID, @NAME, @LOCK)";
this.SetParameters(cmd);
}
public override void SetDelete(IDbCommand cmd)
{
cmd.CommandText = "DELETE FROM berth WHERE id = @ID";
IDataParameter idParam = cmd.CreateParameter();
idParam.ParameterName = "ID";
idParam.Value = this.Id;
cmd.Parameters.Add(idParam);
}
public override void SetUpdate(IDbCommand cmd)
{
cmd.CommandText = "UPDATE berth SET name = @NAME, participant_id = @PID, `lock` = @LOCK WHERE id = @ID";
this.SetParameters(cmd);
}
#endregion
#region private methods
private void SetParameters(IDbCommand cmd)
{
IDbDataParameter id = cmd.CreateParameter();
id.ParameterName = "ID";
id.Value = this.Id;
cmd.Parameters.Add(id);
IDbDataParameter pid = cmd.CreateParameter();
pid.ParameterName = "PID";
pid.Value = this.Participant_Id;
cmd.Parameters.Add(pid);
IDbDataParameter name = cmd.CreateParameter();
name.ParameterName = "NAME";
name.Value = this.Name;
cmd.Parameters.Add(name);
IDbDataParameter lockparam = cmd.CreateParameter();
lockparam.ParameterName = "LOCK";
lockparam.Value = this.Lock;
cmd.Parameters.Add(lockparam);
}
#endregion
}
}

View File

@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace brecal.model
{
public abstract class DbEntity
{
/// <summary>
/// DB primary key
/// </summary>
public uint Id { get; set; }
/// <summary>
/// Creation timestamp, if null record is unsaved
/// </summary>
public DateTime? Created { get; set; }
/// <summary>
/// Modified timestamp, if null record was never modified
/// </summary>
public DateTime? Modified { get; set; }
/// <summary>
/// Set query and cmd parameters for an update query
/// </summary>
/// <param name="cmd">CMD created by DB manager</param>
public abstract void SetUpdate(IDbCommand cmd);
/// <summary>
/// set query and cmd parameters for a create query
/// </summary>
/// <param name="cmd">CMD created by DB manager</param>
public abstract void SetCreate(IDbCommand cmd);
/// <summary>
/// set query and cmd parameters for a delete query
/// </summary>
/// <param name="cmd">CMD created by DB manager</param>
public abstract void SetDelete(IDbCommand cmd);
/// <summary>
/// Each database entity must be able to save itself to the database
/// </summary>
public async Task Save(IDBManager manager)
{
if (this.Created.HasValue)
{
await manager.ExecuteNonQuery(this.SetUpdate);
}
else
{
this.Id = (uint)await manager.ExecuteNonQuery(this.SetCreate);
}
}
/// <summary>
/// Each entity must be able to delete itself
/// </summary>
public async Task Delete(IDBManager manager)
{
await manager.ExecuteNonQuery(this.SetDelete);
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace brecal.model
{
public interface IDBManager
{
delegate List<DbEntity> LoadFunc<T>(T entity);
delegate void QueryFunc(IDbCommand cmd, params object?[] args);
Task<List<DbEntity>> Load(QueryFunc prepareAction, LoadFunc<IDataReader> loadAction, params object?[] args);
Task<object?> ExecuteScalar(Action<IDbCommand> prepareAction);
Task<int> ExecuteNonQuery(Action<IDbCommand> prepareAction);
}
}

View File

@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Threading.Tasks;
namespace brecal.model
{
public class Participant : DbEntity
{
#region Enumerations
[Flags]
public enum ParticipantType
{
NONE = 0,
BSMD = 1,
TERMINAL = 2,
PILOT = 4,
AGENCY = 8,
MOORING = 16,
PORT_ADMINISTRATION = 32,
}
#endregion
#region Properties
public string? Name { get; set; }
public string? Street { get; set; }
public string? PostalCode { get; set; }
public string? City { get; set; }
public uint Flags { get; set; }
#endregion
#region public static methods
public static async Task<List<Participant>> LoadAll(IDBManager manager)
{
List<DbEntity> loadResultList = await manager.Load(SetLoadQuery, LoadElems);
List<Participant> result = new();
foreach (Participant p in loadResultList.Cast<Participant>())
result.Add(p);
return result;
}
public static List<DbEntity> LoadElems(IDataReader reader)
{
List<DbEntity> result = new List<DbEntity>();
while (reader.Read())
{
Participant p = new();
p.Id = (uint)reader.GetInt32(0);
if (!reader.IsDBNull(1)) p.Name = reader.GetString(1);
if (!reader.IsDBNull(2)) p.Street = reader.GetString(2);
if (!reader.IsDBNull(3)) p.PostalCode = reader.GetString(3);
if (!reader.IsDBNull(4)) p.City = reader.GetString(4);
if (!reader.IsDBNull(5)) p.Flags = (uint)reader.GetInt32(5);
if (!reader.IsDBNull(6)) p.Created = reader.GetDateTime(6);
if (!reader.IsDBNull(7)) p.Modified = reader.GetDateTime(7);
result.Add(p);
}
return result;
}
public static void SetLoadQuery(IDbCommand cmd, params object?[] list)
{
cmd.CommandText = "SELECT id, name, street, postal_code, city, flags, created, modified FROM participant";
}
#endregion
#region abstract method implementation
public override void SetUpdate(IDbCommand cmd)
{
cmd.CommandText = "UPDATE participant set name = @NAME, street = @STREET, postal_code = @POSTAL_CODE, city = @CITY, flags = @FLAGS WHERE id = @ID";
this.SetParameters(cmd);
}
public override void SetCreate(IDbCommand cmd)
{
cmd.CommandText = "INSERT INTO participant (name, street, postal_code, city, flags) VALUES ( @NAME, @STREET, @POSTAL_CODE, @CITY, @FLAGS)";
this.SetParameters(cmd);
}
public override void SetDelete(IDbCommand cmd)
{
cmd.CommandText = "DELETE FROM participant WHERE id = @ID";
IDataParameter idParam = cmd.CreateParameter();
idParam.ParameterName = "ID";
idParam.Value = this.Id;
cmd.Parameters.Add(idParam);
}
#endregion
#region private methods
private void SetParameters(IDbCommand cmd)
{
IDbDataParameter name = cmd.CreateParameter();
name.ParameterName = "NAME";
name.Value = this.Name;
cmd.Parameters.Add(name);
IDbDataParameter street = cmd.CreateParameter();
street.ParameterName = "STREET";
street.Value = this.Street;
cmd.Parameters.Add(street);
IDataParameter postal_code = cmd.CreateParameter();
postal_code.ParameterName = "POSTAL_CODE";
postal_code.Value = this.PostalCode;
cmd.Parameters.Add(postal_code);
IDataParameter city = cmd.CreateParameter();
city.ParameterName = "CITY";
city.Value = this.City;
cmd.Parameters.Add(city);
IDataParameter flags = cmd.CreateParameter();
flags.ParameterName = "FLAGS";
flags.Value = this.Flags;
cmd.Parameters.Add(flags);
IDataParameter idParam = cmd.CreateParameter();
idParam.ParameterName = "ID";
idParam.Value = this.Id;
cmd.Parameters.Add(idParam);
}
#endregion
#region overrides
public override string ToString()
{
return this.Name ?? $"{base.Id} - {this.GetType().Name}";
}
#endregion
#region public type flag funcs
public bool IsFlagSet(ParticipantType flag)
{
return (this.Flags & (uint)flag) != 0;
}
public void SetFlag(bool value, ParticipantType flag)
{
if (value) this.Flags |= (uint)flag;
else this.Flags &= (uint)~flag;
}
#endregion
}
}

109
src/brecal.model/Role.cs Normal file
View File

@ -0,0 +1,109 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace brecal.model
{
public class Role : DbEntity
{
#region Properties
public string? Name { get; set; }
public string? Description { get; set; }
#endregion
#region public static methods
public static async Task<List<Role>> LoadAll(IDBManager manager)
{
List<DbEntity> loadResultList = await manager.Load(SetLoadQuery, LoadElems);
List<Role> result = new();
foreach (Role r in loadResultList.Cast<Role>())
result.Add(r);
return result;
}
public static void SetLoadQuery(IDbCommand cmd, params object?[] list)
{
cmd.CommandText = "SELECT id, name, description, created, modified FROM role";
}
public static List<DbEntity> LoadElems(IDataReader reader)
{
List<DbEntity> result = new List<DbEntity>();
while (reader.Read())
{
Role r = new();
r.Id = (uint)reader.GetInt32(0);
if (!reader.IsDBNull(1)) r.Name = reader.GetString(1);
if (!reader.IsDBNull(2)) r.Description = reader.GetString(2);
if (!reader.IsDBNull(3)) r.Created = reader.GetDateTime(3);
if (!reader.IsDBNull(4)) r.Modified = reader.GetDateTime(4);
result.Add(r);
}
return result;
}
#endregion
#region overrides
public override void SetUpdate(IDbCommand cmd)
{
cmd.CommandText = "UPDATE role set name = @NAME, description = @DESC WHERE id = @ID";
this.SetParameters(cmd);
}
public override void SetCreate(IDbCommand cmd)
{
cmd.CommandText = "INSERT INTO role (name, description) VALUES ( @NAME, @DESC)";
this.SetParameters(cmd);
}
public override void SetDelete(IDbCommand cmd)
{
cmd.CommandText = "DELETE FROM role WHERE id = @ID";
IDataParameter idParam = cmd.CreateParameter();
idParam.ParameterName = "ID";
idParam.Value = this.Id;
cmd.Parameters.Add(idParam);
}
public override string ToString()
{
return this.Name ?? $"{base.Id} - {this.GetType().Name}";
}
#endregion
#region private methods
private void SetParameters(IDbCommand cmd)
{
IDbDataParameter name = cmd.CreateParameter();
name.ParameterName = "NAME";
name.Value = this.Name;
cmd.Parameters.Add(name);
IDbDataParameter desc = cmd.CreateParameter();
desc.ParameterName = "DESC";
desc.Value = this.Description;
cmd.Parameters.Add(desc);
IDataParameter idParam = cmd.CreateParameter();
idParam.ParameterName = "ID";
idParam.Value = this.Id;
cmd.Parameters.Add(idParam);
}
#endregion
}
}

View File

@ -0,0 +1,110 @@
using System.Data;
namespace brecal.model
{
public class RoleAssignment : DbEntity
{
#region Properties
public int? UserId { get; set; }
public int? RoleId { get; set; }
public Role? AssignedRole { get; set; }
public User? AssignedUser { get; set; }
#endregion
#region public static methods
public static async Task<List<RoleAssignment>> LoadForUser(User? u, IDBManager manager)
{
List<DbEntity> loadResultList = await manager.Load(SetLoadQuery, LoadElems, args: u);
List<RoleAssignment> result = new();
foreach (RoleAssignment ra in loadResultList.Cast<RoleAssignment>())
{
ra.AssignedUser = u;
result.Add(ra);
}
return result;
}
public static void SetLoadQuery(IDbCommand cmd, params object?[] args)
{
cmd.CommandText = "SELECT id, user_id, role_id FROM user_role_map WHERE user_id = @UID";
if (args.Length != 1 || !(args[0] is User))
throw new ArgumentException("loader needs single user as argument");
IDataParameter uid = cmd.CreateParameter();
uid.ParameterName = "UID";
if (args[0] is User u)
uid.Value = u.Id;
cmd.Parameters.Add(uid);
}
public static List<DbEntity> LoadElems(IDataReader reader)
{
List<DbEntity> result = new List<DbEntity>();
while (reader.Read())
{
RoleAssignment ra = new();
ra.Id = (uint)reader.GetInt32(0);
if (!reader.IsDBNull(1)) ra.UserId = reader.GetInt32(1);
if (!reader.IsDBNull(2)) ra.RoleId = reader.GetInt32(2);
result.Add(ra);
}
return result;
}
#endregion
#region overrides
public override void SetUpdate(IDbCommand cmd)
{
throw new NotImplementedException();
}
public override void SetCreate(IDbCommand cmd)
{
cmd.CommandText = "INSERT INTO user_role_map (user_id, role_id) VALUES (@USERID, @ROLEID)";
IDbDataParameter userid = cmd.CreateParameter();
userid.ParameterName = "USERID";
userid.Value = this.UserId;
cmd.Parameters.Add(userid);
IDbDataParameter roleid = cmd.CreateParameter();
roleid.ParameterName = "ROLEID";
roleid.Value = this.RoleId;
cmd.Parameters.Add(roleid);
}
public override void SetDelete(IDbCommand cmd)
{
cmd.CommandText = "DELETE FROM user_role_map WHERE id = @ID";
IDataParameter idParam = cmd.CreateParameter();
idParam.ParameterName = "ID";
idParam.Value = this.Id;
cmd.Parameters.Add(idParam);
}
public override string ToString()
{
if (this.AssignedRole == null)
{
return $"{Id}: <defunct role>";
}
else
{
return AssignedRole.Name ?? AssignedRole.Id.ToString();
}
}
#endregion
}
}

View File

@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace brecal.model
{
public class Securable : DbEntity
{
#region Properties
public string? Name { get; set; }
#endregion
#region public static methods
public static async Task<List<Securable>> LoadAll(IDBManager manager)
{
List<DbEntity> loadResultList = await manager.Load(SetLoadQuery, LoadElems);
List<Securable> result = new();
foreach (Securable s in loadResultList.Cast<Securable>())
result.Add(s);
return result;
}
public static void SetLoadQuery(IDbCommand cmd, params object?[] list)
{
cmd.CommandText = "SELECT id, name, created, modified FROM securable";
}
public static List<DbEntity> LoadElems(IDataReader reader)
{
List<DbEntity> result = new List<DbEntity>();
while (reader.Read())
{
Securable s = new();
s.Id = (uint)reader.GetInt32(0);
if (!reader.IsDBNull(1)) s.Name = reader.GetString(1);
if (!reader.IsDBNull(2)) s.Created = reader.GetDateTime(2);
if (!reader.IsDBNull(3)) s.Modified = reader.GetDateTime(3);
result.Add(s);
}
return result;
}
#endregion
#region overrides
public override void SetUpdate(IDbCommand cmd)
{
cmd.CommandText = "UPDATE securable set name = @NAME WHERE id = @ID";
this.SetParameters(cmd);
}
public override void SetCreate(IDbCommand cmd)
{
cmd.CommandText = "INSERT INTO securable (name) VALUES ( @NAME)";
this.SetParameters(cmd);
}
public override void SetDelete(IDbCommand cmd)
{
cmd.CommandText = "DELETE FROM securable WHERE id = @ID";
this.SetParameters(cmd);
}
public override string ToString()
{
return this.Name ?? $"{base.Id} - {this.GetType().Name}";
}
#endregion
#region private methods
private void SetParameters(IDbCommand cmd)
{
IDbDataParameter name = cmd.CreateParameter();
name.ParameterName = "NAME";
name.Value = this.Name;
cmd.Parameters.Add(name);
IDataParameter idParam = cmd.CreateParameter();
idParam.ParameterName = "ID";
idParam.Value = this.Id;
cmd.Parameters.Add(idParam);
}
#endregion
}
}

View File

@ -0,0 +1,109 @@
using System.Data;
namespace brecal.model
{
public class SecurableAssignment : DbEntity
{
#region Properties
public Role? AssignedRole { get; set; }
public Securable? AssignedSecurable { get; set; }
public int? RoleId { get; set; }
public int? SecurableId { get; set; }
#endregion
#region public static methods
public static async Task<List<SecurableAssignment>> LoadForRole(Role? r, IDBManager manager)
{
List<DbEntity> loadResultList = await manager.Load(SetLoadQuery, LoadElems, args: r);
List<SecurableAssignment> result = new();
foreach (SecurableAssignment sa in loadResultList.Cast<SecurableAssignment>())
{
sa.AssignedRole = r;
result.Add(sa);
}
return result;
}
public static void SetLoadQuery(IDbCommand cmd, params object?[] args)
{
cmd.CommandText = "SELECT id, role_id, securable_id FROM role_securable_map WHERE role_id = @RID";
if (args.Length != 1 || !(args[0] is Role))
throw new ArgumentException("loader needs single role as argument");
IDataParameter rid = cmd.CreateParameter();
rid.ParameterName = "RID";
if (args[0] is Role r)
rid.Value = r.Id;
cmd.Parameters.Add(rid);
}
public static List<DbEntity> LoadElems(IDataReader reader)
{
List<DbEntity> result = new List<DbEntity>();
while (reader.Read())
{
SecurableAssignment sa = new();
sa.Id = (uint)reader.GetInt32(0);
if (!reader.IsDBNull(1)) sa.RoleId = reader.GetInt32(1);
if (!reader.IsDBNull(2)) sa.SecurableId = reader.GetInt32(2);
result.Add(sa);
}
return result;
}
#endregion
#region public overrides
public override void SetUpdate(IDbCommand cmd)
{
throw new NotImplementedException();
}
public override void SetCreate(IDbCommand cmd)
{
cmd.CommandText = "INSERT INTO role_securable_map (securable_id, role_id) VALUES (@SECURABLEID, @ROLEID)";
IDbDataParameter userid = cmd.CreateParameter();
userid.ParameterName = "SECURABLEID";
userid.Value = this.SecurableId;
cmd.Parameters.Add(userid);
IDbDataParameter roleid = cmd.CreateParameter();
roleid.ParameterName = "ROLEID";
roleid.Value = this.RoleId;
cmd.Parameters.Add(roleid);
}
public override void SetDelete(IDbCommand cmd)
{
cmd.CommandText = "DELETE FROM role_securable_map WHERE id = @ID";
IDataParameter idParam = cmd.CreateParameter();
idParam.ParameterName = "ID";
idParam.Value = this.Id;
cmd.Parameters.Add(idParam);
}
public override string ToString()
{
if (this.AssignedSecurable == null)
{
return $"{Id}: <defunct securable>";
}
else
{
return AssignedSecurable.Name ?? AssignedSecurable.Id.ToString();
}
}
#endregion
}
}

145
src/brecal.model/Ship.cs Normal file
View File

@ -0,0 +1,145 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace brecal.model
{
public class Ship : DbEntity
{
#region Properties
public string? Name { get; set; }
public int? IMO { get; set; }
public string? Callsign { get; set; }
public uint? Participant_Id { get; set; }
public Participant? Participant { get; set; }
public double? Length { get; set; }
public double? Width { get; set; }
public bool IsTug { get { return Participant_Id != null; } }
public string? TugCompany
{
get { if (Participant != null) return Participant.Name; else return ""; }
}
#endregion
#region public static methods
public static async Task<List<Ship>> LoadAll(IDBManager manager)
{
List<DbEntity> loadResultList = await manager.Load(SetLoadQuery, LoadElems);
List<Ship> result = new();
foreach (Ship s in loadResultList.Cast<Ship>())
result.Add(s);
return result;
}
public static void SetLoadQuery(IDbCommand cmd, params object?[] list)
{
cmd.CommandText = "SELECT id, name, imo, callsign, participant_id, length, width, created, modified FROM ship";
}
public static List<DbEntity> LoadElems(IDataReader reader)
{
List<DbEntity> result = new List<DbEntity>();
while (reader.Read())
{
Ship s = new();
s.Id = (uint)reader.GetInt32(0);
if (!reader.IsDBNull(1)) s.Name = reader.GetString(1);
if (!reader.IsDBNull(2)) s.IMO = reader.GetInt32(2);
if (!reader.IsDBNull(3)) s.Callsign = reader.GetString(3);
if (!reader.IsDBNull(4)) s.Participant_Id = (uint)reader.GetInt32(4);
if (!reader.IsDBNull(5)) s.Length = reader.GetFloat(5);
if (!reader.IsDBNull(6)) s.Width = reader.GetFloat(6);
if (!reader.IsDBNull(7)) s.Created = reader.GetDateTime(7);
if (!reader.IsDBNull(8)) s.Modified = reader.GetDateTime(8);
result.Add(s);
}
return result;
}
#endregion
#region DbEntity implementation
public override void SetCreate(IDbCommand cmd)
{
cmd.CommandText = "INSERT INTO ship (name, imo, callsign, participant_id, length, width) VALUES ( @NAME, @IMO, @CALLSIGN, @PID, @LENGTH, @WIDTH)";
this.SetParameters(cmd);
}
public override void SetDelete(IDbCommand cmd)
{
cmd.CommandText = "DELETE FROM ship WHERE id = @ID";
IDataParameter idParam = cmd.CreateParameter();
idParam.ParameterName = "ID";
idParam.Value = this.Id;
cmd.Parameters.Add(idParam);
}
public override void SetUpdate(IDbCommand cmd)
{
cmd.CommandText = "UPDATE ship SET name = @NAME, imo = @IMO, callsign = @CALLSIGN, participant_id = @PID, length = @LENGTH, width = @WIDTH WHERE id = @ID";
this.SetParameters(cmd);
}
#endregion
#region private methods
private void SetParameters(IDbCommand cmd)
{
IDbDataParameter id = cmd.CreateParameter();
id.ParameterName = "ID";
id.Value = this.Id;
cmd.Parameters.Add(id);
IDbDataParameter pid = cmd.CreateParameter();
pid.ParameterName = "PID";
pid.Value = this.Participant_Id;
cmd.Parameters.Add(pid);
IDbDataParameter name = cmd.CreateParameter();
name.ParameterName = "NAME";
name.Value = this.Name;
cmd.Parameters.Add(name);
IDbDataParameter imoparam = cmd.CreateParameter();
imoparam.ParameterName = "IMO";
imoparam.Value = this.IMO;
cmd.Parameters.Add(imoparam);
IDataParameter callsign = cmd.CreateParameter();
callsign.ParameterName = "CALLSIGN";
callsign.Value = this.Callsign;
cmd.Parameters.Add(callsign);
IDataParameter length = cmd.CreateParameter();
length.ParameterName = "LENGTH";
length.Value = this.Length;
cmd.Parameters.Add(length);
IDataParameter width = cmd.CreateParameter();
width.ParameterName = "WIDTH";
width.Value = this.Width;
cmd.Parameters.Add(width);
}
#endregion
}
}

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