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:
parent
9ca73776bb
commit
355da16a5b
|
|
@ -3,11 +3,28 @@
|
|||
# .deploy.env wird NICHT ins Git-Repository übernommen.
|
||||
|
||||
# SSH-Verbindung
|
||||
DEPLOY_HOST=deine-subdomain.bitpalast.de
|
||||
DEPLOY_USER=dein-ssh-user
|
||||
DEPLOY_HOST=deine-server-ip-oder-domain
|
||||
DEPLOY_USER=root
|
||||
|
||||
# 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/)
|
||||
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 |
|
|
@ -1,27 +1,57 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Deployt GPLX MediaWiki auf den Bitpalast-Server.
|
||||
Deployt GPLX MediaWiki auf den IONOS-Server.
|
||||
|
||||
.PARAMETER Mode
|
||||
skin - Cavendish-Skin synchronisieren (Standard, schnell)
|
||||
templates - Wiki-Seiten-Vorlagen synchronisieren
|
||||
all - Skin + Templates
|
||||
initial - Erstinstallation: gesamten MediaWiki-Core übertragen
|
||||
skin – Cavendish-Skin synchronisieren (schnell)
|
||||
pages – Alle Wiki-Seiten aus scripts/templates/ auf Server pushen
|
||||
db – Lokalen DB-Dump erstellen und auf Server importieren
|
||||
all – Skin + Pages
|
||||
initial – Erstinstallation: gesamten MediaWiki-Core übertragen
|
||||
|
||||
.EXAMPLE
|
||||
.\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
|
||||
#>
|
||||
param(
|
||||
[ValidateSet('skin', 'templates', 'all', 'initial')]
|
||||
[ValidateSet('skin', 'pages', 'db', 'all', 'initial')]
|
||||
[string]$Mode = 'skin'
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$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'
|
||||
if (-not (Test-Path $envFile)) {
|
||||
Write-Error @"
|
||||
|
|
@ -37,20 +67,24 @@ Get-Content $envFile | ForEach-Object {
|
|||
}
|
||||
}
|
||||
|
||||
$deployHost = $env:DEPLOY_HOST
|
||||
$deployUser = $env:DEPLOY_USER
|
||||
$mwPath = $env:DEPLOY_MW_PATH
|
||||
$projectPath = $env:DEPLOY_PROJECT_PATH
|
||||
$target = "${deployUser}@${deployHost}"
|
||||
|
||||
foreach ($var in @('DEPLOY_HOST','DEPLOY_USER','DEPLOY_MW_PATH')) {
|
||||
# Pflichtfelder prüfen
|
||||
foreach ($var in @('DEPLOY_HOST', 'DEPLOY_USER', 'DEPLOY_MW_PATH', 'DEPLOY_PROJECT_PATH')) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
# === 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)
|
||||
|
||||
function ConvertTo-WslPath([string]$winPath) {
|
||||
|
|
@ -61,32 +95,30 @@ function ConvertTo-WslPath([string]$winPath) {
|
|||
}
|
||||
|
||||
function Sync-Dir([string]$localDir, [string]$remoteDir, [bool]$delete = $false) {
|
||||
Write-Host " $localDir" -ForegroundColor DarkGray
|
||||
Write-Host " -> ${target}:${remoteDir}" -ForegroundColor DarkGray
|
||||
|
||||
Write-Host " $localDir → ${target}:${remoteDir}" -ForegroundColor DarkGray
|
||||
if ($hasWsl) {
|
||||
$wslSrc = (ConvertTo-WslPath $localDir) + '/'
|
||||
$rsyncArgs = @('-az', '--progress')
|
||||
if ($delete) { $rsyncArgs += '--delete' }
|
||||
$rsyncArgs += $wslSrc
|
||||
$rsyncArgs += "${target}:${remoteDir}/"
|
||||
$rsyncArgs += @($wslSrc, "${target}:${remoteDir}/")
|
||||
wsl rsync @rsyncArgs
|
||||
} else {
|
||||
# Fallback: scp (kein automatisches Loeschen von Remotedateien)
|
||||
Write-Warning "WSL nicht gefunden – nutze scp. Geloeschte lokale Dateien bleiben auf dem Server."
|
||||
Write-Warning "WSL nicht gefunden – nutze scp."
|
||||
ssh $target "mkdir -p '$remoteDir'"
|
||||
scp -r "${localDir}\." "${target}:${remoteDir}/"
|
||||
scp -r "${localDir}\*" "${target}:${remoteDir}/"
|
||||
}
|
||||
if ($LASTEXITCODE -ne 0) { Write-Error "Übertragung fehlgeschlagen (Exit $LASTEXITCODE)."; exit $LASTEXITCODE }
|
||||
}
|
||||
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Error "Uebertragung fehlgeschlagen (Exit $LASTEXITCODE)."
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
$repoRoot = Split-Path $PSScriptRoot
|
||||
|
||||
# === Deploy-Modi ===
|
||||
# ── Deploy-Modi ───────────────────────────────────────────────────────────────
|
||||
switch ($Mode) {
|
||||
|
||||
'skin' {
|
||||
|
|
@ -95,27 +127,102 @@ switch ($Mode) {
|
|||
Write-Host "Fertig." -ForegroundColor Green
|
||||
}
|
||||
|
||||
'templates' {
|
||||
if (-not $projectPath) {
|
||||
Write-Error "DEPLOY_PROJECT_PATH muss in .deploy.env gesetzt sein."
|
||||
'pages' {
|
||||
Write-Host "Deploye Wiki-Seiten ($($PAGE_MAP.Count) Seiten)..." -ForegroundColor Cyan
|
||||
|
||||
# 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
|
||||
}
|
||||
Write-Host "Deploye Wiki-Templates..." -ForegroundColor Cyan
|
||||
Sync-Dir "$repoRoot\scripts\templates" "$projectPath/scripts/templates"
|
||||
Write-Host "Fertig." -ForegroundColor Green
|
||||
|
||||
$dumpFile = Join-Path $env:TEMP "gplx_deploy_$(Get-Date -Format 'yyyyMMdd_HHmmss').sql"
|
||||
|
||||
# 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' {
|
||||
Write-Host "Deploye Skin + Templates..." -ForegroundColor Cyan
|
||||
Sync-Dir "$repoRoot\mediawiki\skins\Cavendish" "$mwPath/skins/Cavendish" -delete $true
|
||||
if ($projectPath) {
|
||||
Sync-Dir "$repoRoot\scripts\templates" "$projectPath/scripts/templates"
|
||||
}
|
||||
Write-Host "Fertig." -ForegroundColor Green
|
||||
Write-Host "Deploye Skin + Wiki-Seiten..." -ForegroundColor Cyan
|
||||
& $PSCommandPath -Mode skin
|
||||
& $PSCommandPath -Mode pages
|
||||
}
|
||||
|
||||
'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
|
||||
|
||||
$excludes = @(
|
||||
|
|
@ -131,23 +238,20 @@ switch ($Mode) {
|
|||
$rsyncArgs = @('-az', '--progress') + $excludes + @($wslSrc, "${target}:${mwPath}/")
|
||||
wsl rsync @rsyncArgs
|
||||
} else {
|
||||
Write-Warning "Fuer die Erstinstallation wird WSL empfohlen (grosse Datenmenge)."
|
||||
Write-Warning "Alternative: WinSCP GUI verwenden oder WSL installieren."
|
||||
Write-Error "WSL ist für die Erstinstallation erforderlich (große Datenmenge). Bitte WSL installieren."
|
||||
exit 1
|
||||
}
|
||||
|
||||
if ($LASTEXITCODE -ne 0) { Write-Error "Übertragung fehlgeschlagen."; exit 1 }
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Erstinstallation abgeschlossen." -ForegroundColor Green
|
||||
Write-Host ""
|
||||
Write-Host "Naechste Schritte auf dem Server:" -ForegroundColor Yellow
|
||||
Write-Host " 1. LocalSettings.php anlegen:"
|
||||
Write-Host " Vorlage: mediawiki/LocalSettings.production.example.php"
|
||||
Write-Host " 2. Composer-Abhaengigkeiten installieren:"
|
||||
Write-Host " cd $mwPath && composer install --no-dev"
|
||||
Write-Host " 3. Datenbank einrichten:"
|
||||
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"
|
||||
Write-Host "Nächste Schritte auf dem Server:" -ForegroundColor Yellow
|
||||
Write-Host " 1. LocalSettings.php hochladen: scp mediawiki/LocalSettings.php ${target}:${mwPath}/"
|
||||
Write-Host " 2. Composer: ssh $target 'cd $mwPath && composer install --no-dev'"
|
||||
Write-Host " 3. DB-Setup: php maintenance/run.php install ... (siehe Runbook)"
|
||||
Write-Host " 4. SMW-Setup: php extensions/SemanticMediaWiki/maintenance/setupStore.php --nochecks"
|
||||
Write-Host " 5. Seiten pushen: .\deploy.ps1 -Mode pages"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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 ==
|
||||
|
|
@ -9,3 +10,5 @@ Pages in this category cover clinical evaluation, clinical performance, post-mar
|
|||
| headers=show
|
||||
| limit=50
|
||||
}}
|
||||
|
||||
[[Category:Themes and topics]]
|
||||
|
|
|
|||
|
|
@ -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]]
|
||||
|
|
@ -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.
|
||||
|
||||
== Documents ==
|
||||
|
|
@ -20,3 +21,5 @@ Pages in this category cover regulatory documents and references in the In Vitro
|
|||
* [[:Category:Labelling|Labelling]]
|
||||
* [[:Category:Traceability|Traceability]]
|
||||
* [[:Category:Performance Evaluation|Performance Evaluation]]
|
||||
|
||||
[[Category:Themes and topics]]
|
||||
|
|
|
|||
|
|
@ -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 ==
|
||||
|
|
@ -9,3 +10,5 @@ Pages in this category cover labelling requirements, UDI carriers, and packaging
|
|||
| headers=show
|
||||
| limit=50
|
||||
}}
|
||||
|
||||
[[Category:Themes and topics]]
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
== 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:Labelling|Labelling]]
|
||||
* [[:Category:Traceability|Traceability]]
|
||||
|
||||
[[Category:Themes and topics]]
|
||||
|
|
|
|||
|
|
@ -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]]
|
||||
|
|
@ -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 ==
|
||||
|
|
@ -9,3 +10,5 @@ Pages in this category cover post-market surveillance (PMS), post-market clinica
|
|||
| headers=show
|
||||
| limit=50
|
||||
}}
|
||||
|
||||
[[Category:Themes and topics]]
|
||||
|
|
|
|||
|
|
@ -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]]
|
||||
|
|
@ -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, '&' )
|
||||
.replace( /</g, '<' )
|
||||
.replace( />/g, '>' )
|
||||
.replace( /"/g, '"' );
|
||||
}
|
||||
|
||||
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 );
|
||||
} );
|
||||
} );
|
||||
}() );
|
||||
|
|
@ -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]].
|
||||
|
|
@ -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.
|
||||
|
|
@ -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]]
|
||||
| ?Document name = Name
|
||||
| ?Scope = Scope
|
||||
| ?Area of validity = Region
|
||||
| ?Document type = Type
|
||||
{{#ask: [[Document:+]]
|
||||
| ?DocName = Document name
|
||||
| ?DocScopes = Scope
|
||||
| ?DocValidityArea = Region
|
||||
| ?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
|
||||
| headers=show
|
||||
| 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) ===
|
||||
|
||||
{{#ask: [[Scope::~*MD*]]
|
||||
| ?Document name = Name
|
||||
| ?Status = Status
|
||||
| ?Area of validity = Region
|
||||
{{#ask: [[Document:+]] [[DocScopes::~*MD*]]
|
||||
| ?DocName = Document name
|
||||
| ?DocStatus = Status
|
||||
| ?DocValidityArea = Region
|
||||
| format=table
|
||||
| headers=show
|
||||
| limit=50
|
||||
| limit=100
|
||||
| sort=DocName
|
||||
}}
|
||||
|
||||
=== In Vitro Diagnostics (IVD) ===
|
||||
|
||||
{{#ask: [[Scope::~*IVD*]]
|
||||
| ?Document name = Name
|
||||
| ?Status = Status
|
||||
| ?Area of validity = Region
|
||||
{{#ask: [[Document:+]] [[DocScopes::~*IVD*]]
|
||||
| ?DocName = Document name
|
||||
| ?DocStatus = Status
|
||||
| ?DocValidityArea = Region
|
||||
| format=table
|
||||
| headers=show
|
||||
| limit=50
|
||||
| limit=100
|
||||
| sort=DocName
|
||||
}}
|
||||
|
||||
=== GMP / PV / NIS ===
|
||||
|
||||
{{#ask: [[Scope::~*GMP*]] OR [[Scope::~*PV*]] OR [[Scope::~*NIS*]]
|
||||
| ?Document name = Name
|
||||
| ?Scope = Scope
|
||||
| ?Status = Status
|
||||
{{#ask: [[Document:+]] [[DocScopes::~*GMP*]] OR [[Document:+]] [[DocScopes::~*PV*]] OR [[Document:+]] [[DocScopes::~*NIS*]]
|
||||
| ?DocName = Document name
|
||||
| ?DocScopes = Scope
|
||||
| ?DocStatus = Status
|
||||
| format=table
|
||||
| headers=show
|
||||
| limit=50
|
||||
| limit=100
|
||||
| sort=DocName
|
||||
}}
|
||||
|
||||
== Draft and not yet promulgated ==
|
||||
== References ==
|
||||
|
||||
{{#ask: [[Status::Draft]] OR [[Status::Final not promulgated]]
|
||||
| ?Document name = Name
|
||||
| ?Scope = Scope
|
||||
| ?Area of validity = Region
|
||||
{{#ask: [[Reference:+]]
|
||||
| ?HasDocument = Document
|
||||
| ?RefScope = Scope
|
||||
| ?RefLanguage = Language
|
||||
| format=table
|
||||
| headers=show
|
||||
| limit=50
|
||||
}}
|
||||
|
||||
== Superseded ==
|
||||
|
||||
{{#ask: [[Status::Superseded]]
|
||||
| ?Document name = Name
|
||||
| ?Scope = Scope
|
||||
| format=table
|
||||
| headers=show
|
||||
| limit=50
|
||||
| limit=200
|
||||
| sort=HasDocument
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -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: '''180–200 px''' (or 360–400 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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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://<wiki-url>/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]].
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
@ -11,34 +11,41 @@ All registered users can contribute. New pages require reviewer approval before
|
|||
{| style="width:100%; border-collapse:collapse;"
|
||||
|-
|
||||
| style="width:33%; vertical-align:top; padding:0 1em 1em 0;" |
|
||||
=== By document ===
|
||||
* [[Document_Index|All documents]]
|
||||
=== Browse ===
|
||||
* [[Document_Index|Document index]]
|
||||
* [[GxPlex_Search|Search & filter]]
|
||||
* [[Special:Ask|Custom SMW query]]
|
||||
* [[Special:RecentChanges|Recent changes]]
|
||||
* [[Special:Ask|Custom query]]
|
||||
|
||||
| style="width:33%; vertical-align:top; padding:0 1em 1em 0;" |
|
||||
=== By scope ===
|
||||
* [[:Category:Medical Devices|Medical Devices (MD)]]
|
||||
* [[:Category:In Vitro Diagnostics|In Vitro Diagnostics (IVD)]]
|
||||
|
||||
| style="width:33%; vertical-align:top; padding:0 1em 1em 0;" |
|
||||
=== By topic ===
|
||||
=== By category ===
|
||||
* [[:Category:Medical Devices|Medical Devices]]
|
||||
* [[:Category:In Vitro Diagnostics|In Vitro Diagnostics]]
|
||||
* [[:Category:Clinical|Clinical]]
|
||||
* [[:Category:Post-Market Surveillance|Post-Market Surveillance]]
|
||||
* [[:Category:Labelling|Labelling]]
|
||||
* [[: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]]
|
||||
| ?Document name = Name
|
||||
| ?Scope = Scope
|
||||
| ?Area of validity = Region
|
||||
{{#ask: [[Document:+]]
|
||||
| ?DocName = Document name
|
||||
| ?DocScopes = Scope
|
||||
| ?DocValidityArea = Region
|
||||
| ?DocStatus = Status
|
||||
| format=table
|
||||
| headers=show
|
||||
| limit=10
|
||||
| sort=Scope
|
||||
| sort=DocName
|
||||
| order=asc
|
||||
}}
|
||||
|
||||
[[Document_Index|View all documents →]]
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|}
|
||||
|
|
@ -1,9 +1,16 @@
|
|||
* Document Database
|
||||
** Document_Index|All documents
|
||||
** Special:Ask|Custom query
|
||||
** Special:Categories|Browse categories
|
||||
* About the Database
|
||||
** mainpage|Main Page
|
||||
** About_GxPlex|About GxPlex
|
||||
** 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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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.
|
||||
|
|
@ -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 →]]
|
||||
Loading…
Reference in New Issue