feat: Document/Reference namespace, infobox sidebar, deploy script, SMW property types

- Add Document: (NS 3000) and Reference: (NS 3002) namespaces with SMW enabled
- Fix SMW namespace config: set smwgNamespacesWithSemanticLinks before wfLoadExtension
- Declare all Doc*/Ref* SMW properties as Has type::Text for correct query results
- Template:DocumentBox — right-floating infobox sidebar with header row styling
- Template:ReferenceBox — new sidebar template for Reference: pages
- MediaWiki:Common.js — filter buttons (Type, Validity area, Scope, Status, Doc type,
  Language, Restricted access), 250-char preview, pagination; removed unused groups
- Document_Index — rewritten with [[Document:+]] SMW queries, grouped by status + scope
- Main_Page — updated Quick navigation, replaced old property-name queries
- scripts/deploy.ps1 — add pages mode (rsync + SSH wiki push), db mode (mysqldump sync),
  updated all mode; 21-page mapping incl. Template:DocumentBox/ReferenceBox
- .deploy.example.env — document new vars (DEPLOY_PHP, DEPLOY_MW_USER, DEPLOY_DB_*)
- scripts/templates/*.wiki — new and updated content pages + category pages
- mediawiki/resources/assets/GxPlex-Logo.png — project logo asset

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Sascha 2026-06-27 21:21:06 +02:00
parent 9ca73776bb
commit 355da16a5b
26 changed files with 1305 additions and 130 deletions

View File

@ -3,11 +3,28 @@
# .deploy.env wird NICHT ins Git-Repository übernommen. # .deploy.env wird NICHT ins Git-Repository übernommen.
# SSH-Verbindung # SSH-Verbindung
DEPLOY_HOST=deine-subdomain.bitpalast.de DEPLOY_HOST=deine-server-ip-oder-domain
DEPLOY_USER=dein-ssh-user DEPLOY_USER=root
# Absoluter Pfad zu mediawiki/ auf dem Server # Absoluter Pfad zu mediawiki/ auf dem Server
DEPLOY_MW_PATH=/var/www/html/gplx/mediawiki DEPLOY_MW_PATH=/var/www/gplx/mediawiki
# Absoluter Pfad zum Projektverzeichnis (eine Ebene über mediawiki/) # Absoluter Pfad zum Projektverzeichnis (eine Ebene über mediawiki/)
DEPLOY_PROJECT_PATH=/var/www/html/gplx DEPLOY_PROJECT_PATH=/var/www/gplx
# PHP auf dem Server (Standard: php)
# DEPLOY_PHP=php
# MediaWiki-Admin-Benutzername für Seiten-Edits (Standard: Gxplex admin)
# DEPLOY_MW_USER=Gxplex admin
# ── Datenbank (nur für -Mode db) ─────────────────────────────────────────────
# Datenbank auf dem Server
DEPLOY_DB_NAME=gplx
DEPLOY_DB_USER=gplx
DEPLOY_DB_PASS=dein-db-passwort
# Lokale Datenbank (Standard: gflex_test / root ohne Passwort)
# LOCAL_DB_NAME=gflex_test
# LOCAL_MYSQLDUMP=D:\xampp\mysql\bin\mysqldump.exe

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -1,27 +1,57 @@
<# <#
.SYNOPSIS .SYNOPSIS
Deployt GPLX MediaWiki auf den Bitpalast-Server. Deployt GPLX MediaWiki auf den IONOS-Server.
.PARAMETER Mode .PARAMETER Mode
skin - Cavendish-Skin synchronisieren (Standard, schnell) skin Cavendish-Skin synchronisieren (schnell)
templates - Wiki-Seiten-Vorlagen synchronisieren pages Alle Wiki-Seiten aus scripts/templates/ auf Server pushen
all - Skin + Templates db Lokalen DB-Dump erstellen und auf Server importieren
initial - Erstinstallation: gesamten MediaWiki-Core übertragen all Skin + Pages
initial Erstinstallation: gesamten MediaWiki-Core übertragen
.EXAMPLE .EXAMPLE
.\deploy.ps1 # Skin deployen .\deploy.ps1 # Skin deployen
.\deploy.ps1 -Mode all # Skin + Templates .\deploy.ps1 -Mode pages # Alle Wiki-Seiten pushen
.\deploy.ps1 -Mode db # DB synchronisieren
.\deploy.ps1 -Mode all # Skin + Pages
.\deploy.ps1 -Mode initial .\deploy.ps1 -Mode initial
#> #>
param( param(
[ValidateSet('skin', 'templates', 'all', 'initial')] [ValidateSet('skin', 'pages', 'db', 'all', 'initial')]
[string]$Mode = 'skin' [string]$Mode = 'skin'
) )
Set-StrictMode -Version Latest Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
# === Config laden === # ── Mapping: Dateiname in scripts/templates/ → Wiki-Seitentitel ──────────────
# Reihenfolge ist relevant: MediaWiki-Systemseiten zuerst, dann Templates,
# dann Inhaltsseiten.
$PAGE_MAP = [ordered]@{
'Sidebar.wiki' = 'MediaWiki:Sidebar'
'Common_js.wiki' = 'MediaWiki:Common.js'
'Editnotice_Advertisement.wiki' = 'MediaWiki:Editnotice-MediaWiki:Advertisement'
'Template_DocumentBox.wiki' = 'Template:DocumentBox'
'Template_ReferenceBox.wiki' = 'Template:ReferenceBox'
'Cat_Clinical.wiki' = 'Category:Clinical'
'Cat_EU_Regulation.wiki' = 'Category:EU Regulation'
'Cat_IVD.wiki' = 'Category:In Vitro Diagnostics'
'Cat_Labelling.wiki' = 'Category:Labelling'
'Cat_MedicalDevices.wiki' = 'Category:Medical Devices'
'Cat_PerformanceEvaluation.wiki' = 'Category:Performance Evaluation'
'Cat_PostMarket.wiki' = 'Category:Post-Market Surveillance'
'Cat_Traceability.wiki' = 'Category:Traceability'
'About_GxPlex.wiki' = 'About GxPlex'
'Contribute_to_GxPlex.wiki' = 'Contribute to GxPlex'
'Create_new_page.wiki' = 'Create new page'
'GxPlex_Search.wiki' = 'GxPlex Search'
'Help_Adding_a_topic.wiki' = 'Help:Adding a topic'
'Roadmap.wiki' = 'Development Roadmap'
'The_Project.wiki' = 'The Project'
'Themes_and_topics.wiki' = 'Themes and topics'
}
# ── .deploy.env laden ─────────────────────────────────────────────────────────
$envFile = Join-Path $PSScriptRoot '..' '.deploy.env' $envFile = Join-Path $PSScriptRoot '..' '.deploy.env'
if (-not (Test-Path $envFile)) { if (-not (Test-Path $envFile)) {
Write-Error @" Write-Error @"
@ -37,20 +67,24 @@ Get-Content $envFile | ForEach-Object {
} }
} }
$deployHost = $env:DEPLOY_HOST # Pflichtfelder prüfen
$deployUser = $env:DEPLOY_USER foreach ($var in @('DEPLOY_HOST', 'DEPLOY_USER', 'DEPLOY_MW_PATH', 'DEPLOY_PROJECT_PATH')) {
$mwPath = $env:DEPLOY_MW_PATH
$projectPath = $env:DEPLOY_PROJECT_PATH
$target = "${deployUser}@${deployHost}"
foreach ($var in @('DEPLOY_HOST','DEPLOY_USER','DEPLOY_MW_PATH')) {
if (-not [System.Environment]::GetEnvironmentVariable($var, 'Process')) { if (-not [System.Environment]::GetEnvironmentVariable($var, 'Process')) {
Write-Error "$var ist nicht in .deploy.env gesetzt." Write-Error "$var fehlt in .deploy.env."
exit 1 exit 1
} }
} }
# === WSL rsync oder scp === $deployHost = $env:DEPLOY_HOST
$deployUser = $env:DEPLOY_USER
$mwPath = $env:DEPLOY_MW_PATH
$projectPath = $env:DEPLOY_PROJECT_PATH
$serverPhp = if ($env:DEPLOY_PHP) { $env:DEPLOY_PHP } else { 'php' }
$mwUser = if ($env:DEPLOY_MW_USER) { $env:DEPLOY_MW_USER } else { 'Gxplex admin' }
$target = "${deployUser}@${deployHost}"
$repoRoot = Split-Path $PSScriptRoot
# ── Hilfsfunktionen ───────────────────────────────────────────────────────────
$hasWsl = $null -ne (Get-Command 'wsl' -ErrorAction SilentlyContinue) $hasWsl = $null -ne (Get-Command 'wsl' -ErrorAction SilentlyContinue)
function ConvertTo-WslPath([string]$winPath) { function ConvertTo-WslPath([string]$winPath) {
@ -61,32 +95,30 @@ function ConvertTo-WslPath([string]$winPath) {
} }
function Sync-Dir([string]$localDir, [string]$remoteDir, [bool]$delete = $false) { function Sync-Dir([string]$localDir, [string]$remoteDir, [bool]$delete = $false) {
Write-Host " $localDir" -ForegroundColor DarkGray Write-Host " $localDir${target}:${remoteDir}" -ForegroundColor DarkGray
Write-Host " -> ${target}:${remoteDir}" -ForegroundColor DarkGray
if ($hasWsl) { if ($hasWsl) {
$wslSrc = (ConvertTo-WslPath $localDir) + '/' $wslSrc = (ConvertTo-WslPath $localDir) + '/'
$rsyncArgs = @('-az', '--progress') $rsyncArgs = @('-az', '--progress')
if ($delete) { $rsyncArgs += '--delete' } if ($delete) { $rsyncArgs += '--delete' }
$rsyncArgs += $wslSrc $rsyncArgs += @($wslSrc, "${target}:${remoteDir}/")
$rsyncArgs += "${target}:${remoteDir}/"
wsl rsync @rsyncArgs wsl rsync @rsyncArgs
} else { } else {
# Fallback: scp (kein automatisches Loeschen von Remotedateien) Write-Warning "WSL nicht gefunden nutze scp."
Write-Warning "WSL nicht gefunden nutze scp. Geloeschte lokale Dateien bleiben auf dem Server."
ssh $target "mkdir -p '$remoteDir'" ssh $target "mkdir -p '$remoteDir'"
scp -r "${localDir}\." "${target}:${remoteDir}/" scp -r "${localDir}\*" "${target}:${remoteDir}/"
}
if ($LASTEXITCODE -ne 0) {
Write-Error "Uebertragung fehlgeschlagen (Exit $LASTEXITCODE)."
exit $LASTEXITCODE
} }
if ($LASTEXITCODE -ne 0) { Write-Error "Übertragung fehlgeschlagen (Exit $LASTEXITCODE)."; exit $LASTEXITCODE }
} }
$repoRoot = Split-Path $PSScriptRoot function Push-WikiPage([string]$remoteFile, [string]$title) {
# Titel für einfache Shell-Quoting escapen: einfache Anführungszeichen verdoppeln
$safeTitle = $title -replace "'", "'\"'\"'"
$cmd = "cat '$remoteFile' | $serverPhp $mwPath/maintenance/run.php edit --user '$mwUser' --summary 'Deploy' '$safeTitle' 2>&1"
$out = ssh $target $cmd
return $LASTEXITCODE -eq 0
}
# === Deploy-Modi === # ── Deploy-Modi ───────────────────────────────────────────────────────────────
switch ($Mode) { switch ($Mode) {
'skin' { 'skin' {
@ -95,27 +127,102 @@ switch ($Mode) {
Write-Host "Fertig." -ForegroundColor Green Write-Host "Fertig." -ForegroundColor Green
} }
'templates' { 'pages' {
if (-not $projectPath) { Write-Host "Deploye Wiki-Seiten ($($PAGE_MAP.Count) Seiten)..." -ForegroundColor Cyan
Write-Error "DEPLOY_PROJECT_PATH muss in .deploy.env gesetzt sein."
# 1. Template-Dateien auf Server synchronisieren
Write-Host "`n[1/2] Dateien übertragen..." -ForegroundColor DarkGray
Sync-Dir "$repoRoot\scripts\templates" "$projectPath/scripts/templates"
# 2. Jede Seite per Maintenance-Script einspielen
Write-Host "`n[2/2] Seiten pushen..." -ForegroundColor DarkGray
$ok = 0; $fail = 0; $skip = 0
foreach ($entry in $PAGE_MAP.GetEnumerator()) {
$localFile = Join-Path "$repoRoot\scripts\templates" $entry.Key
$remoteFile = "$projectPath/scripts/templates/$($entry.Key)"
$title = $entry.Value
if (-not (Test-Path $localFile)) {
Write-Host " SKIP $($entry.Key) (Datei nicht gefunden)" -ForegroundColor DarkYellow
$skip++
continue
}
Write-Host -NoNewline "$title ... "
if (Push-WikiPage $remoteFile $title) {
Write-Host "OK" -ForegroundColor Green
$ok++
} else {
Write-Host "FEHLER" -ForegroundColor Red
$fail++
}
}
Write-Host ""
$color = if ($fail -eq 0) { 'Green' } else { 'Yellow' }
Write-Host "Ergebnis: $ok OK | $fail Fehler | $skip übersprungen" -ForegroundColor $color
}
'db' {
Write-Host "Synchronisiere Datenbank lokal → Server..." -ForegroundColor Cyan
# Konfiguration aus .deploy.env
$localDbName = if ($env:LOCAL_DB_NAME) { $env:LOCAL_DB_NAME } else { 'gflex_test' }
$localDump = if ($env:LOCAL_MYSQLDUMP) { $env:LOCAL_MYSQLDUMP } else { 'D:\xampp\mysql\bin\mysqldump.exe' }
$serverDbName = if ($env:DEPLOY_DB_NAME) { $env:DEPLOY_DB_NAME } else { 'gplx' }
$serverDbUser = if ($env:DEPLOY_DB_USER) { $env:DEPLOY_DB_USER } else { 'gplx' }
$serverDbPass = $env:DEPLOY_DB_PASS
if (-not $serverDbPass) {
Write-Error "DEPLOY_DB_PASS fehlt in .deploy.env."
exit 1 exit 1
} }
Write-Host "Deploye Wiki-Templates..." -ForegroundColor Cyan
Sync-Dir "$repoRoot\scripts\templates" "$projectPath/scripts/templates" $dumpFile = Join-Path $env:TEMP "gplx_deploy_$(Get-Date -Format 'yyyyMMdd_HHmmss').sql"
Write-Host "Fertig." -ForegroundColor Green
# 1. Lokalen Dump erstellen
Write-Host "`n[1/3] Erstelle lokalen DB-Dump ($localDbName)..." -ForegroundColor DarkGray
& $localDump -u root $localDbName | Set-Content $dumpFile -Encoding UTF8
if ($LASTEXITCODE -ne 0) { Write-Error "Dump fehlgeschlagen."; exit 1 }
$sizeMb = [math]::Round((Get-Item $dumpFile).Length / 1MB, 1)
Write-Host " Dump erstellt: $dumpFile ($sizeMb MB)"
# 2. Dump auf Server übertragen
Write-Host "`n[2/3] Übertrage Dump auf Server..." -ForegroundColor DarkGray
scp $dumpFile "${target}:/tmp/gplx_deploy.sql"
if ($LASTEXITCODE -ne 0) { Write-Error "SCP fehlgeschlagen."; exit 1 }
# 3. Auf Server importieren (DB droppen + neu anlegen)
Write-Host "`n[3/3] Importiere auf Server (DB wird überschrieben)..." -ForegroundColor DarkGray
$importCmd = @"
mysql -u root <<'SQL'
DROP DATABASE IF EXISTS $serverDbName;
CREATE DATABASE $serverDbName CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
GRANT ALL PRIVILEGES ON $serverDbName.* TO '${serverDbUser}'@'localhost';
FLUSH PRIVILEGES;
SQL
mysql -u$serverDbUser -p$serverDbPass $serverDbName < /tmp/gplx_deploy.sql
cd $mwPath && $serverPhp maintenance/run.php update --quick
rm /tmp/gplx_deploy.sql
"@
ssh $target $importCmd
if ($LASTEXITCODE -ne 0) { Write-Error "Import fehlgeschlagen."; exit 1 }
# Lokale Dump-Datei aufräumen
Remove-Item $dumpFile -Force
Write-Host "`nFertig. Datenbank wurde auf dem Server überschrieben." -ForegroundColor Green
Write-Host "Hinweis: LocalSettings.php auf dem Server muss ggf. angepasst werden." -ForegroundColor DarkYellow
} }
'all' { 'all' {
Write-Host "Deploye Skin + Templates..." -ForegroundColor Cyan Write-Host "Deploye Skin + Wiki-Seiten..." -ForegroundColor Cyan
Sync-Dir "$repoRoot\mediawiki\skins\Cavendish" "$mwPath/skins/Cavendish" -delete $true & $PSCommandPath -Mode skin
if ($projectPath) { & $PSCommandPath -Mode pages
Sync-Dir "$repoRoot\scripts\templates" "$projectPath/scripts/templates"
}
Write-Host "Fertig." -ForegroundColor Green
} }
'initial' { 'initial' {
Write-Host "Erstinstallation uebertraege MediaWiki-Core..." -ForegroundColor Yellow Write-Host "Erstinstallation übertrage MediaWiki-Core..." -ForegroundColor Yellow
Write-Host "Das kann einige Minuten dauern." -ForegroundColor DarkGray Write-Host "Das kann einige Minuten dauern." -ForegroundColor DarkGray
$excludes = @( $excludes = @(
@ -131,23 +238,20 @@ switch ($Mode) {
$rsyncArgs = @('-az', '--progress') + $excludes + @($wslSrc, "${target}:${mwPath}/") $rsyncArgs = @('-az', '--progress') + $excludes + @($wslSrc, "${target}:${mwPath}/")
wsl rsync @rsyncArgs wsl rsync @rsyncArgs
} else { } else {
Write-Warning "Fuer die Erstinstallation wird WSL empfohlen (grosse Datenmenge)." Write-Error "WSL ist für die Erstinstallation erforderlich (große Datenmenge). Bitte WSL installieren."
Write-Warning "Alternative: WinSCP GUI verwenden oder WSL installieren."
exit 1 exit 1
} }
if ($LASTEXITCODE -ne 0) { Write-Error "Übertragung fehlgeschlagen."; exit 1 }
Write-Host "" Write-Host ""
Write-Host "Erstinstallation abgeschlossen." -ForegroundColor Green Write-Host "Erstinstallation abgeschlossen." -ForegroundColor Green
Write-Host "" Write-Host ""
Write-Host "Naechste Schritte auf dem Server:" -ForegroundColor Yellow Write-Host "Nächste Schritte auf dem Server:" -ForegroundColor Yellow
Write-Host " 1. LocalSettings.php anlegen:" Write-Host " 1. LocalSettings.php hochladen: scp mediawiki/LocalSettings.php ${target}:${mwPath}/"
Write-Host " Vorlage: mediawiki/LocalSettings.production.example.php" Write-Host " 2. Composer: ssh $target 'cd $mwPath && composer install --no-dev'"
Write-Host " 2. Composer-Abhaengigkeiten installieren:" Write-Host " 3. DB-Setup: php maintenance/run.php install ... (siehe Runbook)"
Write-Host " cd $mwPath && composer install --no-dev" Write-Host " 4. SMW-Setup: php extensions/SemanticMediaWiki/maintenance/setupStore.php --nochecks"
Write-Host " 3. Datenbank einrichten:" Write-Host " 5. Seiten pushen: .\deploy.ps1 -Mode pages"
Write-Host " php maintenance/run.php install ..."
Write-Host " oder: Dump importieren mit mysql -u user -p dbname < dump.sql"
Write-Host " 4. SMW initialisieren:"
Write-Host " php maintenance/run.php setupStore"
} }
} }

View File

@ -0,0 +1,25 @@
== About GxPlex ==
GxPlex is a structured document database for regulatory affairs in the medical device industry.
It helps teams manage, review, and approve regulatory documents in compliance with MDR and IVDR requirements.
=== Purpose ===
* Central repository for regulatory documents
* Structured metadata via [[Special:Properties|semantic properties]]
* Approval workflow for controlled document management
=== How to use GxPlex ===
* [[Create_new_page|Create a new page]] — open a pre-filled document form
* [[Contribute_to_GxPlex|How to contribute]] — step-by-step guide for authors
* [[Help_Adding_a_topic|How to add a topic]] — create a new category that appears on the topics overview
=== Development ===
* [[Development Roadmap]] — planned features and open tasks for future versions
=== Contact ===
For questions or access requests, contact the wiki administrator.

View File

@ -1,3 +1,4 @@
{{#set: Description=Clinical evaluation, clinical performance studies, PMCF, and PMPF documentation.}}
Pages in this category cover clinical evaluation, clinical performance, post-market clinical follow-up (PMCF), and post-market performance follow-up (PMPF). Pages in this category cover clinical evaluation, clinical performance, post-market clinical follow-up (PMCF), and post-market performance follow-up (PMPF).
== Pages in this category == == Pages in this category ==
@ -9,3 +10,5 @@ Pages in this category cover clinical evaluation, clinical performance, post-mar
| headers=show | headers=show
| limit=50 | limit=50
}} }}
[[Category:Themes and topics]]

View File

@ -0,0 +1,17 @@
{{#set: Description=Core EU regulatory frameworks: MDR (EU 2017/745) and IVDR (EU 2017/746).}}
Pages in this category relate to EU regulatory frameworks governing medical devices and in vitro diagnostic devices, including MDR (EU 2017/745) and IVDR (EU 2017/746).
== Documents ==
{{#ask: [[Category:EU Regulation]] [[Status::+]]
| ?Document name = Name
| ?Status = Status
| ?Area of validity = Region
| ?Version = Version
| format=table
| headers=show
| limit=50
| sort=Status
}}
[[Category:Themes and topics]]

View File

@ -1,3 +1,4 @@
{{#set: Description=Regulatory documents and references under IVDR (EU 2017/746).}}
Pages in this category cover regulatory documents and references in the In Vitro Diagnostics (IVD) scope. Pages in this category cover regulatory documents and references in the In Vitro Diagnostics (IVD) scope.
== Documents == == Documents ==
@ -20,3 +21,5 @@ Pages in this category cover regulatory documents and references in the In Vitro
* [[:Category:Labelling|Labelling]] * [[:Category:Labelling|Labelling]]
* [[:Category:Traceability|Traceability]] * [[:Category:Traceability|Traceability]]
* [[:Category:Performance Evaluation|Performance Evaluation]] * [[:Category:Performance Evaluation|Performance Evaluation]]
[[Category:Themes and topics]]

View File

@ -1,3 +1,4 @@
{{#set: Description=Labelling requirements, UDI carriers, and packaging obligations.}}
Pages in this category cover labelling requirements, UDI carriers, and packaging obligations. Pages in this category cover labelling requirements, UDI carriers, and packaging obligations.
== Pages in this category == == Pages in this category ==
@ -9,3 +10,5 @@ Pages in this category cover labelling requirements, UDI carriers, and packaging
| headers=show | headers=show
| limit=50 | limit=50
}} }}
[[Category:Themes and topics]]

View File

@ -1,3 +1,4 @@
{{#set: Description=Regulatory documents and references under MDR (EU 2017/745).}}
Pages in this category cover regulatory documents and references in the Medical Devices (MD) scope. Pages in this category cover regulatory documents and references in the Medical Devices (MD) scope.
== Documents == == Documents ==
@ -19,3 +20,5 @@ Pages in this category cover regulatory documents and references in the Medical
* [[:Category:Post-Market Surveillance|Post-Market Surveillance]] * [[:Category:Post-Market Surveillance|Post-Market Surveillance]]
* [[:Category:Labelling|Labelling]] * [[:Category:Labelling|Labelling]]
* [[:Category:Traceability|Traceability]] * [[:Category:Traceability|Traceability]]
[[Category:Themes and topics]]

View File

@ -0,0 +1,17 @@
{{#set: Description=Performance evaluation plans, reports, and PMPF documentation for IVD devices (IVDR).}}
Pages in this category cover performance evaluation documentation for in vitro diagnostic devices, including performance evaluation plans, reports, and post-market performance follow-up (PMPF) under IVDR.
== Documents ==
{{#ask: [[Category:Performance Evaluation]] [[Status::+]]
| ?Document name = Name
| ?Status = Status
| ?Area of validity = Region
| ?Version = Version
| format=table
| headers=show
| limit=50
| sort=Status
}}
[[Category:Themes and topics]]

View File

@ -1,3 +1,4 @@
{{#set: Description=Post-market surveillance plans, PMCF, PMPF, and periodic safety update reports (PSUR/PSSR).}}
Pages in this category cover post-market surveillance (PMS), post-market clinical follow-up (PMCF), post-market performance follow-up (PMPF), and periodic safety update reports (PSUR). Pages in this category cover post-market surveillance (PMS), post-market clinical follow-up (PMCF), post-market performance follow-up (PMPF), and periodic safety update reports (PSUR).
== Pages in this category == == Pages in this category ==
@ -9,3 +10,5 @@ Pages in this category cover post-market surveillance (PMS), post-market clinica
| headers=show | headers=show
| limit=50 | limit=50
}} }}
[[Category:Themes and topics]]

View File

@ -0,0 +1,17 @@
{{#set: Description=UDI systems and device identification obligations under MDR and IVDR.}}
Pages in this category cover traceability requirements including UDI (Unique Device Identification) systems and device identification obligations under MDR and IVDR.
== Documents ==
{{#ask: [[Category:Traceability]] [[Status::+]]
| ?Document name = Name
| ?Status = Status
| ?Area of validity = Region
| ?Version = Version
| format=table
| headers=show
| limit=50
| sort=Status
}}
[[Category:Themes and topics]]

View File

@ -0,0 +1,504 @@
/* site-wide JS — VoteNY toggle is handled in Vote.js */
/* GxPlex Search — chainable filter buttons with pagination */
( function () {
if ( mw.config.get( 'wgPageName' ) !== 'GxPlex_Search' ) {
return;
}
var PAGE_SIZE = 50;
var FILTER_GROUPS = [
{ key: 'type', label: 'Type' },
{ key: 'validityArea', label: 'Validity area' },
{ key: 'scopes', label: 'Scope(s)' },
{ key: 'status', label: 'Status' },
{ key: 'docType', label: 'Document type' },
{ key: 'language', label: 'Language' },
{ key: 'restricted', label: 'Restricted access' }
];
var DOC_QUERY = '[[Document:+]]'
+ '|?DocReference=reference'
+ '|?DocValidityArea=validityArea'
+ '|?DocScopes=scopes'
+ '|?DocName=docName'
+ '|?DocVersion=version'
+ '|?DocStatus=status'
+ '|?DocType=docType'
+ '|?DocLanguage=language'
+ '|?DocDescription=description'
+ '|?DocRestricted=restricted'
+ '|?DocSubmittedBy=submittedBy'
+ '|?DocContributors=contributors'
+ '|limit=500'
+ '|sort=DocName'
+ '|order=asc';
var REF_QUERY = '[[Reference:+]]'
+ '|?HasDocument=linkedDoc'
+ '|?HasDocument.DocReference=reference'
+ '|?HasDocument.DocValidityArea=validityArea'
+ '|?HasDocument.DocScopes=scopes'
+ '|?HasDocument.DocName=docName'
+ '|?HasDocument.DocVersion=version'
+ '|?HasDocument.DocStatus=status'
+ '|?HasDocument.DocType=docType'
+ '|?HasDocument.DocLanguage=language'
+ '|?HasDocument.DocDescription=description'
+ '|?HasDocument.DocRestricted=restricted'
+ '|?HasDocument.DocSubmittedBy=submittedBy'
+ '|?HasDocument.DocContributors=contributors'
+ '|?RefScope=refScope'
+ '|?RefLanguage=refLanguage'
+ '|?RefTagsEN=tagsEN'
+ '|limit=500';
var allResults = [];
var filteredResults = [];
var currentPage = 1;
var activeFilters = {};
/* ---- helpers ---- */
function esc( str ) {
return String( str || '' )
.replace( /&/g, '&amp;' )
.replace( /</g, '&lt;' )
.replace( />/g, '&gt;' )
.replace( /"/g, '&quot;' );
}
function getProp( printouts, key ) {
var arr = printouts[ key ];
if ( !arr || !arr.length ) { return ''; }
return arr.map( function ( v ) {
if ( typeof v === 'string' ) { return v; }
return v.item !== undefined ? v.item : ( v.fulltext || '' );
} ).filter( Boolean ).join( ', ' );
}
function splitValues( str ) {
return String( str || '' ).split( ',' ).map( function ( v ) { return v.trim(); } ).filter( Boolean );
}
/* ---- parse SMW responses ---- */
function parseResults( data, type ) {
var raw = ( data.query && data.query.results ) || {};
return Object.keys( raw ).map( function ( key ) {
var page = raw[ key ];
var p = page.printouts || {};
var docName = getProp( p, 'docName' );
return {
_type: type,
fulltext: page.fulltext,
url: page.fullurl,
displayName: type === 'Document'
? ( docName || page.fulltext.replace( /^Document:/, '' ) )
: page.fulltext.replace( /^Reference:/, '' ),
reference: getProp( p, 'reference' ),
validityArea: getProp( p, 'validityArea' ),
scopes: getProp( p, 'scopes' ),
docName: docName,
version: getProp( p, 'version' ),
status: getProp( p, 'status' ),
docType: getProp( p, 'docType' ),
language: getProp( p, 'language' ),
description: getProp( p, 'description' ),
restricted: getProp( p, 'restricted' ),
submittedBy: getProp( p, 'submittedBy' ),
contributors: getProp( p, 'contributors' ),
preview: ''
};
} );
}
/* ---- text extracts (250 chars, batches of 50) ---- */
function fetchExtracts( results, callback ) {
if ( !results.length ) { callback(); return; }
var byTitle = {};
results.forEach( function ( r ) { byTitle[ r.fulltext ] = r; } );
var titles = results.map( function ( r ) { return r.fulltext; } );
var batches = [];
for ( var i = 0; i < titles.length; i += 50 ) {
batches.push( titles.slice( i, i + 50 ) );
}
var done = 0;
var api = new mw.Api();
batches.forEach( function ( batch ) {
api.get( {
action: 'query', prop: 'extracts',
exchars: 250, exintro: true, explaintext: true,
titles: batch.join( '|' ), format: 'json'
} ).always( function ( data ) {
if ( data && data.query && data.query.pages ) {
Object.keys( data.query.pages ).forEach( function ( id ) {
var pg = data.query.pages[ id ];
if ( byTitle[ pg.title ] ) {
byTitle[ pg.title ].preview = ( pg.extract || '' )
.replace( /\s+/g, ' ' ).trim().slice( 0, 250 );
}
} );
}
if ( ++done === batches.length ) { callback(); }
} );
} );
}
/* ---- filter button data ---- */
function collectFieldValues( results ) {
var values = {};
FILTER_GROUPS.forEach( function ( g ) { values[ g.key ] = {}; } );
results.forEach( function ( r ) {
FILTER_GROUPS.forEach( function ( g ) {
var raw = g.key === 'type' ? r._type : r[ g.key ];
splitValues( raw ).forEach( function ( v ) { values[ g.key ][ v ] = true; } );
} );
} );
return values;
}
/* ---- render filter buttons ---- */
function renderFilterButtons( filterSection, values ) {
filterSection.innerHTML = '';
activeFilters = {};
var hasAnyValue = FILTER_GROUPS.some( function ( g ) {
return Object.keys( values[ g.key ] || {} ).length > 0;
} );
if ( !hasAnyValue ) {
var msg = document.createElement( 'p' );
msg.className = 'gplx-no-results';
msg.textContent = 'No documents or references indexed yet. Import data to enable filters.';
filterSection.appendChild( msg );
return;
}
FILTER_GROUPS.forEach( function ( g ) {
activeFilters[ g.key ] = {};
var vals = Object.keys( values[ g.key ] || {} ).sort();
if ( !vals.length ) { return; }
var group = document.createElement( 'div' );
group.className = 'gplx-filter-group';
var label = document.createElement( 'span' );
label.className = 'gplx-filter-label';
label.textContent = g.label;
var row = document.createElement( 'div' );
row.className = 'gplx-filter-btn-row';
vals.forEach( function ( v ) {
var btn = document.createElement( 'button' );
btn.type = 'button';
btn.className = 'gplx-filter-btn';
btn.textContent = v;
btn.setAttribute( 'aria-pressed', 'false' );
btn.addEventListener( 'click', function () {
if ( activeFilters[ g.key ][ v ] ) {
delete activeFilters[ g.key ][ v ];
btn.classList.remove( 'is-active' );
btn.setAttribute( 'aria-pressed', 'false' );
} else {
activeFilters[ g.key ][ v ] = true;
btn.classList.add( 'is-active' );
btn.setAttribute( 'aria-pressed', 'true' );
}
refresh();
} );
row.appendChild( btn );
} );
group.appendChild( label );
group.appendChild( row );
filterSection.appendChild( group );
} );
}
/* ---- filter logic: AND across groups, OR within group ---- */
function matchesFilters( result ) {
return FILTER_GROUPS.every( function ( g ) {
var active = Object.keys( activeFilters[ g.key ] || {} );
if ( !active.length ) { return true; }
var fieldVal = g.key === 'type' ? result._type : ( result[ g.key ] || '' );
var parts = splitValues( fieldVal );
return active.some( function ( av ) {
return parts.some( function ( p ) {
return p.toLowerCase() === av.toLowerCase();
} );
} );
} );
}
function applyFilters( results ) {
var textEl = document.getElementById( 'gplx-filter-text' );
var text = textEl ? textEl.value.trim().toLowerCase() : '';
return results.filter( function ( r ) {
if ( !matchesFilters( r ) ) { return false; }
if ( text ) {
var hay = [
r.displayName, r.reference, r.validityArea, r.scopes,
r.docName, r.version, r.status, r.docType, r.language,
r.description, r.restricted, r.submittedBy, r.contributors,
r.preview
].join( ' ' ).toLowerCase();
if ( hay.indexOf( text ) === -1 ) { return false; }
}
return true;
} );
}
/* ---- pagination ---- */
function getPageRange( current, total ) {
if ( total <= 7 ) {
var pages = [];
for ( var i = 1; i <= total; i++ ) { pages.push( i ); }
return pages;
}
var result = [ 1 ];
var left = current - 2;
var right = current + 2;
if ( left > 2 ) { result.push( '...' ); }
for ( var j = Math.max( 2, left ); j <= Math.min( total - 1, right ); j++ ) {
result.push( j );
}
if ( right < total - 1 ) { result.push( '...' ); }
result.push( total );
return result;
}
function renderPagination( total ) {
var el = document.getElementById( 'gplx-pagination' );
if ( !el ) { return; }
var totalPages = Math.ceil( total / PAGE_SIZE );
if ( totalPages <= 1 ) { el.innerHTML = ''; return; }
var frag = document.createDocumentFragment();
function pageBtn( label, page, isCurrent, isDisabled ) {
var btn = document.createElement( 'button' );
btn.type = 'button';
btn.className = 'gplx-page-btn' + ( isCurrent ? ' gplx-page-btn--current' : '' );
btn.textContent = label;
btn.disabled = isDisabled || isCurrent;
if ( isCurrent ) { btn.setAttribute( 'aria-current', 'page' ); }
if ( !isDisabled && !isCurrent ) {
btn.addEventListener( 'click', function () {
currentPage = page;
renderPage();
var s = document.getElementById( 'gplx-search-status' );
if ( s ) { s.scrollIntoView( { behavior: 'smooth', block: 'nearest' } ); }
} );
}
return btn;
}
frag.appendChild( pageBtn( ' Previous', currentPage - 1, false, currentPage === 1 ) );
getPageRange( currentPage, totalPages ).forEach( function ( p ) {
if ( p === '...' ) {
var span = document.createElement( 'span' );
span.className = 'gplx-page-ellipsis';
span.textContent = '…';
frag.appendChild( span );
} else {
frag.appendChild( pageBtn( String( p ), p, p === currentPage, false ) );
}
} );
frag.appendChild( pageBtn( 'Next ', currentPage + 1, false, currentPage === totalPages ) );
el.innerHTML = '';
el.appendChild( frag );
}
/* ---- render results table ---- */
function renderTable( pageResults ) {
var container = document.getElementById( 'gplx-search-results' );
if ( !pageResults.length ) {
container.innerHTML = '<p class="gplx-no-results">No results match the selected filters.</p>';
return;
}
var rows = pageResults.map( function ( r ) {
var typeBadge = r._type === 'Document'
? '<span class="gplx-badge-doc">Document</span>'
: '<span class="gplx-badge-ref">Reference</span>';
var restricted = r.restricted && r.restricted.toLowerCase() === 'yes'
? ' <span class="gplx-badge-restricted">Restricted</span>' : '';
var preview = r.preview
? '<div class="gplx-result-preview">' + esc( r.preview ) + '</div>' : '';
return '<tr>'
+ '<td>' + typeBadge + '</td>'
+ '<td><a href="' + esc( r.url ) + '">' + esc( r.displayName ) + '</a>'
+ restricted + preview + '</td>'
+ '<td>' + esc( r.validityArea ) + '</td>'
+ '<td>' + esc( r.status ) + '</td>'
+ '<td>' + esc( r.language ) + '</td>'
+ '<td>' + esc( r.docType ) + '</td>'
+ '</tr>';
} );
container.innerHTML = '<table class="wikitable sortable gplx-results-table">'
+ '<thead><tr>'
+ '<th>Type</th><th>Title</th>'
+ '<th>Validity area</th><th>Status</th><th>Language</th><th>Document type</th>'
+ '</tr></thead>'
+ '<tbody>' + rows.join( '' ) + '</tbody>'
+ '</table>';
}
function setStatus( msg, busy ) {
var s = document.getElementById( 'gplx-search-status' );
if ( !s ) { return; }
s.textContent = msg;
s.className = 'gplx-search-status' + ( busy ? ' gplx-search-busy' : '' );
}
function renderPage() {
var total = filteredResults.length;
var totalPages = Math.ceil( total / PAGE_SIZE );
currentPage = Math.max( 1, Math.min( currentPage, totalPages || 1 ) );
var start = ( currentPage - 1 ) * PAGE_SIZE;
var end = Math.min( start + PAGE_SIZE, total );
if ( total === 0 ) {
setStatus( '0 results', false );
} else {
setStatus(
'Showing ' + ( start + 1 ) + '' + end + ' of ' + total
+ ' result' + ( total !== 1 ? 's' : '' ),
false
);
}
renderTable( filteredResults.slice( start, end ) );
renderPagination( total );
}
function refresh() {
filteredResults = applyFilters( allResults );
currentPage = 1;
renderPage();
}
/* ---- build container UI ---- */
function buildUI( container ) {
var filterSection = document.createElement( 'div' );
filterSection.className = 'gplx-filter-buttons';
var clearBtn = document.createElement( 'button' );
clearBtn.type = 'button';
clearBtn.className = 'gplx-filter-clear';
clearBtn.textContent = 'Clear all filters';
clearBtn.addEventListener( 'click', function () {
FILTER_GROUPS.forEach( function ( g ) { activeFilters[ g.key ] = {}; } );
document.querySelectorAll( '.gplx-filter-btn' ).forEach( function ( b ) {
b.classList.remove( 'is-active' );
b.setAttribute( 'aria-pressed', 'false' );
} );
refresh();
} );
var searchLabel = document.createElement( 'label' );
searchLabel.className = 'gplx-filter-label';
searchLabel.htmlFor = 'gplx-filter-text';
searchLabel.textContent = 'Search in titles and text';
var textInput = document.createElement( 'input' );
textInput.type = 'search';
textInput.id = 'gplx-filter-text';
textInput.className = 'gplx-filter-input';
textInput.placeholder = 'Search…';
textInput.setAttribute( 'aria-label', 'Search in titles and text' );
var searchGroup = document.createElement( 'div' );
searchGroup.className = 'gplx-filter-group gplx-filter-group--grow';
searchGroup.style.marginBottom = '0.5rem';
searchGroup.appendChild( searchLabel );
searchGroup.appendChild( textInput );
var status = document.createElement( 'p' );
status.id = 'gplx-search-status';
status.className = 'gplx-search-status';
var results = document.createElement( 'div' );
results.id = 'gplx-search-results';
var pagination = document.createElement( 'nav' );
pagination.id = 'gplx-pagination';
pagination.className = 'gplx-pagination';
pagination.setAttribute( 'aria-label', 'Results pages' );
container.innerHTML = '';
container.appendChild( filterSection );
container.appendChild( clearBtn );
container.appendChild( searchGroup );
container.appendChild( status );
container.appendChild( results );
container.appendChild( pagination );
return { filterSection: filterSection, textInput: textInput };
}
/* ---- load data ---- */
function load( controls ) {
setStatus( 'Loading…', true );
var api = new mw.Api();
var docResults = [], refResults = [];
var docDone = false, refDone = false;
function onBothDone() {
allResults = docResults.concat( refResults );
fetchExtracts( allResults, function () {
var values = collectFieldValues( allResults );
renderFilterButtons( controls.filterSection, values );
if ( allResults.length ) {
refresh();
} else {
setStatus( '', false );
document.getElementById( 'gplx-search-results' ).innerHTML = '';
}
} );
}
api.get( { action: 'ask', query: DOC_QUERY, format: 'json' } )
.always( function ( data ) {
if ( data && data.query ) { docResults = parseResults( data, 'Document' ); }
docDone = true;
if ( refDone ) { onBothDone(); }
} );
api.get( { action: 'ask', query: REF_QUERY, format: 'json' } )
.always( function ( data ) {
if ( data && data.query ) { refResults = parseResults( data, 'Reference' ); }
refDone = true;
if ( docDone ) { onBothDone(); }
} );
}
/* ---- init ---- */
mw.loader.using( [ 'mediawiki.api' ], function () {
$( function () {
var container = document.getElementById( 'gplx-search-app' );
if ( !container ) { return; }
var controls = buildUI( container );
var debounce;
controls.textInput.addEventListener( 'input', function () {
clearTimeout( debounce );
debounce = setTimeout( refresh, 250 );
} );
load( controls );
} );
} );
}() );

View File

@ -0,0 +1,26 @@
== Contribute to GxPlex ==
Registered users can contribute documents and content to GxPlex.
=== How to contribute ===
# Log in with your user account.
# Use [[Create_new_page|Create new page]] to start a new document.
# Fill in the required metadata fields (scope, status, area of validity, etc.).
# Submit the page — it will enter the approval workflow and be reviewed before publication.
=== Roles ===
{| class="wikitable"
! Role !! Permissions
|-
| Author || Create and edit pages
|-
| Reviewer || Approve or reject submitted pages
|-
| Administrator || Full access including user management
|}
=== Questions? ===
Contact the wiki administrator or visit [[About_GxPlex|About GxPlex]].

View File

@ -0,0 +1,13 @@
== Create new page ==
Enter a title for your new document and click '''Create'''.
<inputbox>
type=create
width=40
placeholder=Document title…
buttonlabel=Create page
preload=Template:RegDocument
</inputbox>
New pages are pre-filled with the [[Template:RegDocument|RegDocument]] template to ensure all required metadata fields are present.

View File

@ -1,70 +1,118 @@
This page lists all regulatory documents in this wiki. Use the sections below to browse by status or scope. This page lists all regulatory documents in this wiki. Use [[GxPlex_Search|GxPlex Search]] for filter-based browsing, or [[Special:Ask|Special:Ask]] for custom queries.
== Current documents == == All documents ==
{{#ask: [[Status::Current]] {{#ask: [[Document:+]]
| ?Document name = Name | ?DocName = Document name
| ?Scope = Scope | ?DocScopes = Scope
| ?Area of validity = Region | ?DocValidityArea = Region
| ?Document type = Type | ?DocStatus = Status
| ?DocType = Type
| format=table
| headers=show
| limit=500
| sort=DocName
| order=asc
}}
== By status ==
=== Current ===
{{#ask: [[Document:+]] [[DocStatus::Current]]
| ?DocName = Document name
| ?DocScopes = Scope
| ?DocValidityArea = Region
| ?DocType = Type
| format=table
| headers=show
| limit=200
| sort=DocName
| order=asc
}}
=== Draft ===
{{#ask: [[Document:+]] [[DocStatus::Draft]]
| ?DocName = Document name
| ?DocScopes = Scope
| ?DocValidityArea = Region
| format=table | format=table
| headers=show | headers=show
| limit=100 | limit=100
| sort=Scope | sort=DocName
}} }}
== Grouped by scope == === Superseded ===
{{#ask: [[Document:+]] [[DocStatus::Superseded]]
| ?DocName = Document name
| ?DocScopes = Scope
| ?DocValidityArea = Region
| format=table
| headers=show
| limit=100
| sort=DocName
}}
== By scope ==
=== Human Medicines (HM) ===
{{#ask: [[Document:+]] [[DocScopes::~*HM*]]
| ?DocName = Document name
| ?DocStatus = Status
| ?DocValidityArea = Region
| format=table
| headers=show
| limit=100
| sort=DocName
}}
=== Medical Devices (MD) === === Medical Devices (MD) ===
{{#ask: [[Scope::~*MD*]] {{#ask: [[Document:+]] [[DocScopes::~*MD*]]
| ?Document name = Name | ?DocName = Document name
| ?Status = Status | ?DocStatus = Status
| ?Area of validity = Region | ?DocValidityArea = Region
| format=table | format=table
| headers=show | headers=show
| limit=50 | limit=100
| sort=DocName
}} }}
=== In Vitro Diagnostics (IVD) === === In Vitro Diagnostics (IVD) ===
{{#ask: [[Scope::~*IVD*]] {{#ask: [[Document:+]] [[DocScopes::~*IVD*]]
| ?Document name = Name | ?DocName = Document name
| ?Status = Status | ?DocStatus = Status
| ?Area of validity = Region | ?DocValidityArea = Region
| format=table | format=table
| headers=show | headers=show
| limit=50 | limit=100
| sort=DocName
}} }}
=== GMP / PV / NIS === === GMP / PV / NIS ===
{{#ask: [[Scope::~*GMP*]] OR [[Scope::~*PV*]] OR [[Scope::~*NIS*]] {{#ask: [[Document:+]] [[DocScopes::~*GMP*]] OR [[Document:+]] [[DocScopes::~*PV*]] OR [[Document:+]] [[DocScopes::~*NIS*]]
| ?Document name = Name | ?DocName = Document name
| ?Scope = Scope | ?DocScopes = Scope
| ?Status = Status | ?DocStatus = Status
| format=table | format=table
| headers=show | headers=show
| limit=50 | limit=100
| sort=DocName
}} }}
== Draft and not yet promulgated == == References ==
{{#ask: [[Status::Draft]] OR [[Status::Final not promulgated]] {{#ask: [[Reference:+]]
| ?Document name = Name | ?HasDocument = Document
| ?Scope = Scope | ?RefScope = Scope
| ?Area of validity = Region | ?RefLanguage = Language
| format=table | format=table
| headers=show | headers=show
| limit=50 | limit=200
}} | sort=HasDocument
== Superseded ==
{{#ask: [[Status::Superseded]]
| ?Document name = Name
| ?Scope = Scope
| format=table
| headers=show
| limit=50
}} }}

View File

@ -0,0 +1,20 @@
<div style="background:#f0f4f8; border:1px solid #c8ccd1; border-left:4px solid #3366cc; padding:0.75em 1em; margin-bottom:1em; border-radius:0 4px 4px 0; font-size:0.9em;">
'''Advertisement editing notes'''
The content of this page is displayed below the toolbox in the left sidebar. Standard wikitext and links are supported.
; Disable the ad slot
: Enter only a hyphen (<code>-</code>) or leave the page blank.
; Adding an image
: 1. Upload the image via [[Special:Upload]].
: 2. Insert it with:
:: <code><nowiki>[[File:Filename.jpg|center|link=https://example.com|alt=Short description]]</nowiki></code>
: The <code>link=</code> parameter makes the image clickable. <code>alt=</code> is required for accessibility (WCAG).
; Recommended image dimensions
: The sidebar is approximately '''200 px''' wide. Recommended image width: '''180200 px''' (or 360400 px for HiDPI displays). Wider images are scaled down automatically to fit — there is no hard pixel limit. Keep the height below 150 px; landscape or square formats work best in the sidebar.
; Caption
: An optional short caption appears below the image in grey text. Add it as a plain paragraph after the image syntax.
</div>

View File

@ -0,0 +1,16 @@
== Search in GxPlex ==
=== Full-text search ===
Searches across all page content, titles, and annotations.
<inputbox>
type=fulltext
width=50
searchbuttonlabel=Search
placeholder=Search all pages…
</inputbox>
=== Filter documents and references ===
<div id="gplx-search-app">Loading…</div>

View File

@ -0,0 +1,59 @@
This page explains how to add a new topic category to GxPlex so it appears automatically on [[Themes_and_topics|Themes and topics]].
== What is a topic? ==
A topic is a [[Special:Categories|category page]] that groups related regulatory documents. Every topic must belong to the parent category "Themes and topics" and carry a short description so it appears in the overview table.
== Adding a topic via the browser ==
=== Step 1 Open the new category page ===
Navigate to <code>http://&lt;wiki-url&gt;/index.php/Category:Your_Topic_Name</code> in your browser. The page will show "There is currently no text in this page." Click '''Create''' to start editing.
=== Step 2 Add the required content ===
Paste the following into the editor and adjust the highlighted parts:
<pre>
{{#set: Description=A one-sentence description of what this topic covers.}}
Pages in this category cover …
== Documents ==
{{#ask: [[Category:Your Topic Name]] [[Status::+]]
| ?Document name = Name
| ?Status = Status
| ?Area of validity = Region
| ?Version = Version
| format=table
| headers=show
| limit=50
| sort=Status
}}
[[Category:Themes and topics]]
</pre>
{| class="wikitable"
! Part !! What to change
|-
| <code>Description=…</code> || One sentence describing the topic — shown in the [[Themes_and_topics|Themes and topics]] table.
|-
| <code>Pages in this category cover …</code> || A short introductory sentence visible on the category page itself.
|-
| <code><nowiki>[[Category:Your Topic Name]]</nowiki></code> in the ask query || Replace with the exact name of your new category.
|-
| <code><nowiki>[[Category:Themes and topics]]</nowiki></code> at the bottom || '''Do not remove.''' This line makes the topic appear in the overview table.
|}
=== Step 3 Save ===
Click '''Save page'''. The new topic will appear on [[Themes_and_topics|Themes and topics]] immediately — no further steps needed.
== Removing a topic ==
Edit the category page and remove the line <code><nowiki>[[Category:Themes and topics]]</nowiki></code>, then save. The topic disappears from the overview on the next page load.
== Need help? ==
Contact the wiki administrator or see [[About_GxPlex|About GxPlex]].

View File

@ -1,8 +1,8 @@
This wiki is a structured reference database for regulatory documents in the medical devices, in vitro diagnostics, and pharmaceutical sectors. Each document is stored with standardized metadata (scope, validity status, region, official source) and linked to individual pages for its key articles, annexes, and provisions, organized by topic. This wiki is a structured reference database for regulatory documents in the medical devices, in vitro diagnostics, and pharmaceutical sectors. Each document is stored with standardized metadata (scope, validity status, region, official source) and linked to individual reference pages for key articles, annexes, and provisions.
The database currently covers regulations, directives, and guidelines in the MD, IVD, GMP, PV, and NIS scopes. Cross-references between equivalent provisions in different regulations are maintained explicitly, for example between MDR and IVDR requirements on post-market surveillance or UDI. The database currently covers regulations, directives, and guidelines in the MD, IVD, HM, GMP, PV, and NIS scopes. Cross-references between equivalent provisions in different regulations are maintained explicitly.
Structured queries are available on the [[Document_Index|Document Index]] and on each category page. Use [[Special:Ask|Special:Ask]] for custom queries across any combination of scope, status, region, or topic. Structured queries are available on the [[Document_Index|Document Index]]. Use [[GxPlex_Search|GxPlex Search]] for filter-based browsing, or [[Special:Ask|Special:Ask]] for custom SMW queries.
All registered users can contribute. New pages require reviewer approval before becoming publicly visible. All registered users can contribute. New pages require reviewer approval before becoming publicly visible.
@ -11,34 +11,41 @@ All registered users can contribute. New pages require reviewer approval before
{| style="width:100%; border-collapse:collapse;" {| style="width:100%; border-collapse:collapse;"
|- |-
| style="width:33%; vertical-align:top; padding:0 1em 1em 0;" | | style="width:33%; vertical-align:top; padding:0 1em 1em 0;" |
=== By document === === Browse ===
* [[Document_Index|All documents]] * [[Document_Index|Document index]]
* [[GxPlex_Search|Search & filter]]
* [[Special:Ask|Custom SMW query]]
* [[Special:RecentChanges|Recent changes]] * [[Special:RecentChanges|Recent changes]]
* [[Special:Ask|Custom query]]
| style="width:33%; vertical-align:top; padding:0 1em 1em 0;" | | style="width:33%; vertical-align:top; padding:0 1em 1em 0;" |
=== By scope === === By category ===
* [[:Category:Medical Devices|Medical Devices (MD)]] * [[:Category:Medical Devices|Medical Devices]]
* [[:Category:In Vitro Diagnostics|In Vitro Diagnostics (IVD)]] * [[:Category:In Vitro Diagnostics|In Vitro Diagnostics]]
| style="width:33%; vertical-align:top; padding:0 1em 1em 0;" |
=== By topic ===
* [[:Category:Clinical|Clinical]] * [[:Category:Clinical|Clinical]]
* [[:Category:Post-Market Surveillance|Post-Market Surveillance]] * [[:Category:Post-Market Surveillance|Post-Market Surveillance]]
* [[:Category:Labelling|Labelling]] * [[:Category:Labelling|Labelling]]
* [[:Category:Traceability|Traceability]] * [[:Category:Traceability|Traceability]]
| style="width:33%; vertical-align:top; padding:0 1em 1em 0;" |
=== Contribute ===
* [[Contribute_to_GxPlex|How to contribute]]
* [[Help:Adding_a_topic|Add a topic category]]
* [[Create_new_page|Create a new page]]
* [[GxPlex_Search|Find existing pages]]
|} |}
== Current documents == == Recent documents ==
{{#ask: [[Status::Current]] {{#ask: [[Document:+]]
| ?Document name = Name | ?DocName = Document name
| ?Scope = Scope | ?DocScopes = Scope
| ?Area of validity = Region | ?DocValidityArea = Region
| ?DocStatus = Status
| format=table | format=table
| headers=show | headers=show
| limit=10 | limit=10
| sort=Scope | sort=DocName
| order=asc
}} }}
[[Document_Index|View all documents →]] [[Document_Index|View all documents →]]

View File

@ -0,0 +1,82 @@
== Development Roadmap ==
This page collects planned features, open tasks, and ideas for future development of GxPlex.
Items are grouped by theme. Priority is indicated in the Status column.
=== Rating and feedback ===
{| class="wikitable"
! Feature !! Status !! Notes
|-
| Loading indicator during vote submission
| Planned
| After clicking a star, the UI gives no visual feedback until the API response arrives. A spinner or disabled state on the stars during the request would improve perceived responsiveness. Relevant file: <code>extensions/VoteNY/resources/js/Vote.js</code> — disable stars on click, re-enable in <code>.done()</code>.
|-
| Link to top-rated pages
| Planned
| VoteNY ships with [[Special:TopRatings]] — a ranked list of the highest-rated pages. Link it from [[GxPlex_Search|GxPlex Search]] and the sidebar.
|-
| Show vote count on document index
| Idea
| The <code>{{NUMBEROFVOTESPAGE:Page name}}</code> magic word from VoteNY can display the number of votes per page. Could be added as a column to [[Document_Index|Document Index]] or the category pages.
|}
=== Content and structure ===
{| class="wikitable"
! Feature !! Status !! Notes
|-
| Document part as filterable property
| Deferred
| Adding <code>document_part</code> (chapter, article, section) as an SMW property to [[Template:RegDocument]] was explicitly deferred. Would require updating all existing pages after the template change. Reopen when the document count makes this worthwhile.
|-
| Additional content templates
| Planned
| Beyond <code>{{RegDocument}}</code>, dedicated templates for recurring document types (e.g. PSUR, CEP, PMCF report) would reduce setup time per page and improve metadata consistency.
|-
| Structured change history
| Idea
| A property or subpage convention for recording revision history of a regulatory document (who updated what, and when) separate from the wiki edit history.
|}
=== Search and navigation ===
{| class="wikitable"
! Feature !! Status !! Notes
|-
| Dynamic filter UI for GxPlex Search
| Planned
| The current [[GxPlex_Search|GxPlex Search]] uses a sortable table. Installing the [https://www.mediawiki.org/wiki/Extension:PageForms PageForms extension] would enable dropdown filters that query SMW properties dynamically without a full page reload.
|-
| Topic property on RegDocument
| Idea
| Adding a <code>Topic</code> SMW property would allow filtering documents by topic in <code>Special:Ask</code> without navigating to category pages. Currently topics are inferred from page categories only.
|}
=== Communication ===
{| class="wikitable"
! Feature !! Status !! Notes
|-
| Email notifications for approval workflow
| Planned
| Authors currently have no automatic notification when their page is approved or rejected. Requires SMTP configuration (<code>$wgSMTP</code> in <code>LocalSettings.php</code>) and a hook on ApprovedRevs' approval/rejection events.
|-
| Discussion threads on document pages
| Planned
| The [[Special:Version|DiscussionTools extension]] is already installed but not surfaced in the UI. Enabling it would allow threaded comments on each document page without a separate talk page workflow.
|}
=== Infrastructure ===
{| class="wikitable"
! Feature !! Status !! Notes
|-
| Sponsor or partner slot
| Idea
| A designated area in the sidebar or footer for a sponsor link or logo. Requires a hook in <code>LocalSettings.php</code> and a configurable wikitext snippet so non-technical admins can update the content without a deployment.
|-
| Automated accessibility regression test
| Idea
| The Pa11y and Lighthouse checks (<code>npm run a11y:all</code>) currently run manually. Adding them to a pre-commit hook or a scheduled task would catch regressions earlier.
|}

View File

@ -1,9 +1,16 @@
* Document Database * About the Database
** Document_Index|All documents ** mainpage|Main Page
** Special:Ask|Custom query ** About_GxPlex|About GxPlex
** Special:Categories|Browse categories ** The_Project|The Project
** Contribute_to_GxPlex|Contribute to GxPlex
* Navigation
** GxPlex_Search|Search in GxPlex
* Structure
** Document_Index|All documents
** Special:Categories|Keywords / Tags
** Themes_and_topics|Themes and topics
* TOOLBOX
* navigation
** mainpage|mainpage-description
** Special:RecentChanges|recentchanges
** Special:Random|randompage

View File

@ -0,0 +1,96 @@
<noinclude>
Display template for document metadata. Does not set any SMW properties — use <code>{{#set:}}</code> directly on the Document page.
== Parameters ==
{| class="wikitable"
! Parameter !! Description
|-
| <code>reference</code> || Composite reference identifier (e.g. AUT AMG Arzneimittelgesetz 04-Mar-2023)
|-
| <code>validity_area</code> || Country / region code (e.g. AUT, USA, EU)
|-
| <code>scopes</code> || Scope code(s) (e.g. HM, G)
|-
| <code>doc_name</code> || Full document name (regulation, law, guideline, …)
|-
| <code>version</code> || Version or revision date
|-
| <code>status</code> || Current, Draft, Superseded, …
|-
| <code>doc_type</code> || Legal act, Regulation, Guideline, …
|-
| <code>language</code> || ISO 639 language code(s) (e.g. EN, DE)
|-
| <code>description</code> || Short description of the document
|-
| <code>official_link</code> || URL to the official source (omit the surrounding brackets)
|-
| <code>restricted</code> || Yes / No
|-
| <code>submitted_by</code> || Name of the person who submitted this entry
|-
| <code>contributors</code> || Additional contributors (optional)
|}
== Usage ==
<pre>
{{DocumentBox
|reference=
|validity_area=
|scopes=
|doc_name=
|version=
|status=
|doc_type=
|language=
|description=
|official_link=
|restricted=No
|submitted_by=
|contributors=
}}
</pre>
</noinclude><includeonly>{| class="wikitable gplx-infobox"
! colspan="2" class="gplx-infobox-header" | Document information
|-
! scope="row" | Reference
| {{{reference|}}}
|-
! scope="row" | Validity area
| {{{validity_area|}}}
|-
! scope="row" | Scope(s)
| {{{scopes|}}}
|-
! scope="row" | Document name
| {{{doc_name|}}}
|-
! scope="row" | Version / Revision
| {{{version|}}}
|-
! scope="row" | Status
| {{{status|}}}
|-
! scope="row" | Document type
| {{{doc_type|}}}
|-
! scope="row" | Language(s)
| {{{language|}}}
|-
! scope="row" | Description
| {{{description|}}}
|-
! scope="row" | Official source
| {{#if:{{{official_link|}}}|[{{{official_link|}}} Official link]|—}}
|-
! scope="row" | Restricted access
| {{{restricted|No}}}
|-
! scope="row" | Submitted by
| {{{submitted_by|}}}
|-
! scope="row" | Contributors
| {{{contributors|}}}
|}</includeonly>

View File

@ -0,0 +1,57 @@
<noinclude>
Sidebar template for reference-specific metadata on Reference: pages. Does not set any SMW properties — use <code>{{#set:}}</code> directly on the Reference page.
== Parameters ==
{| class="wikitable"
! Parameter !! Description
|-
| <code>scope</code> || Applicable scope of this reference (e.g. HM, G, IVD)
|-
| <code>doc_part</code> || Article, section, annex, or chapter (e.g. Art. 12, Annex I)
|-
| <code>language</code> || Language of this reference (ISO 639, e.g. EN, DE)
|-
| <code>tags_en</code> || English tags / keywords (comma-separated)
|-
| <code>tags_original</code> || Tags in original document language
|-
| <code>tags_fr</code> || French tags (optional)
|}
== Usage ==
<pre>
{{ReferenceBox
|scope=
|doc_part=
|language=
|tags_en=
|tags_original=
|tags_fr=
}}
</pre>
</noinclude><includeonly>{| class="wikitable gplx-infobox"
! colspan="2" class="gplx-infobox-header" | Reference details
|-
! scope="row" | Scope
| {{{scope|}}}
|-
! scope="row" | Document part
| {{{doc_part|}}}
|-
! scope="row" | Language
| {{{language|}}}
|-
! scope="row" | Tags (EN)
| {{{tags_en|}}}
{{#if:{{{tags_original|}}}|
|-
! scope="row" | Tags (original)
| {{{tags_original|}}}
}}{{#if:{{{tags_fr|}}}|
|-
! scope="row" | Tags (FR)
| {{{tags_fr|}}}
}}
|}</includeonly>

View File

@ -0,0 +1,17 @@
== The Project ==
GxPlex was started to address the need for a transparent, traceable document management system tailored to medical device regulation.
=== Background ===
Regulatory documentation under MDR (EU 2017/745) and IVDR (EU 2017/746) requires structured, versioned, and approved records. GxPlex provides a wiki-based platform optimised for this workflow.
=== Goals ===
* Traceable document lifecycle from draft to approved
* Full-text and property-based search across all documents
* Role-based access for authors, reviewers, and administrators
=== Status ===
The project is currently in active development.

View File

@ -0,0 +1,11 @@
{{#ask: [[Category:Themes and topics]]
| ?Description
| format=table
| headers=plain
| mainlabel=Topic
| limit=50
| sort=title
}}
----
[[Help:Adding a topic|How to add or remove a topic →]]