users cannot delete themselves anymore

This commit is contained in:
Steffen Jost 2019-03-22 11:39:59 +01:00
parent 2ddb637b6e
commit 21ba1f2afa
14 changed files with 197 additions and 168 deletions

View File

@ -1,4 +1,5 @@
Es s
Übersicht über PageActions und Workflow im Vergleich zum alten UniWorX:
-----
Course Actions im alten UniWorX:
- Studenten
@ -8,6 +9,8 @@ Course Actions im alten UniWorX:
- E-Mails
- Online-Evaluation
-----
CourseActions in Uni2Work:
-1 Übungsblätter (öfter)
[ Auch aus SheetList übernommen un hier ebenfalls angzeigt:

View File

@ -6,6 +6,7 @@ BtnDeregister: Abmelden
BtnHijack: Sitzung übernehmen
Aborted: Abgebrochen
Remarks: Hinweise
Registered: Angemeldet
RegisteredHeader: Anmeldung
RegisteredSince date@Text: Angemeldet seit #{date}

3
routes
View File

@ -37,6 +37,7 @@
/ HomeR GET !free
/users UsersR GET -- no tags, i.e. admins only
/users/#CryptoUUIDUser AdminUserR GET POST !development
/users/#CryptoUUIDUser/delete AdminUserDeleteR POST !development
/users/#CryptoUUIDUser/hijack AdminHijackUserR POST !adminANDno-escalation
/admin AdminR GET
/admin/features AdminFeaturesR GET POST
@ -52,7 +53,7 @@
/help HelpR GET POST !free
/user ProfileR GET POST !free
/user/profile ProfileDataR GET POST !free
/user/profile ProfileDataR GET !free
/user/authpreds AuthPredsR GET POST !free
/term TermShowR GET !free

View File

@ -833,6 +833,7 @@ getCUserR _tid _ssh _csh uCId = do
-- - User is a lecturer for course
uid <- decrypt uCId
User{..} <- runDB $ get404 uid
-- USE src/utils/Form.formResult
defaultLayout -- TODO
[whamlet|
@ -844,5 +845,6 @@ getCHiWisR = error "CHiWisR: Not implemented"
getCNotesR, postCNotesR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
-- NOTE: The route getNotesR is abused for correctorORlecturer access rights!
getCNotesR = error "CNotesR: Not implemented"
-- PROBLEM: Correctors usually don't know Participants by name (anonymous), maybe notes are not shared?
getCNotesR = error "CNotesR: Not implemented"
postCNotesR = error "CNotesR: Not implemented"

View File

@ -121,134 +121,47 @@ postProfileR = do
setTitle . toHtml $ "Profil " <> userIdent
$(widgetFile "formPageI18n")
postProfileDataR :: Handler Html
postProfileDataR = do
((btnResult,_), _) <- runFormPost buttonForm
case btnResult of
(FormSuccess BtnDelete) -> do
(uid, User{..}) <- requireAuthPair
clearCreds False -- Logout-User
((deletedSubmissions,groupSubmissions),deletedSubmissionGroups) <- runDB $ deleteUser uid
-- addMessageIHamlet
$(addMessageFile Success "templates/deletedUser.hamlet") -- USE THIS ONE
-- addMessageI Success $ MsgDeleteUser deletedSubmissions
-- when (groupSubmissions > 0) $ addMessageI Info $ MsgDeleteUserGroupSubmissions groupSubmissions
defaultLayout
$(widgetFile "deletedUser")
-- (FormSuccess BtnAbort ) -> do
-- addMessageI Info MsgAborted
-- redirect ProfileDataR
_other -> getProfileDataR
deleteUser :: UserId -> DB ((Int,Int),Int64) -- TODO: Restrict deletions for lecturers, tutors and students in course that won't allow deregistration
deleteUser duid = do
-- E.deleteCount for submissions is not cascading, hence we first select and then delete manually
-- We delete all files tied to submissions where the user is the lone submissionUser
-- Do not deleteCascade submissions where duid is the corrector:
updateWhere [SubmissionRatingBy ==. Just duid] [SubmissionRatingBy =. Nothing]
groupSubmissions <- selectSubmissionsWhere (\numBuddies -> numBuddies E.>. E.val (0::Int64))
singleSubmissions <- selectSubmissionsWhere (\numBuddies -> numBuddies E.==. E.val (0::Int64))
deleteCascade duid
forM_ singleSubmissions $ \(E.Value submissionId) -> do
deleteFileIds <- map E.unValue <$> getSubmissionFiles submissionId
deleteCascade submissionId
deleteCascadeWhere [FileId <-. deleteFileIds] -- TODO: throws exception for de-duplicated files
deletedSubmissionGroups <- deleteSingleSubmissionGroups
return ((length singleSubmissions, length groupSubmissions),deletedSubmissionGroups)
where
selectSubmissionsWhere :: (E.SqlExpr (E.Value Int64) -> E.SqlExpr (E.Value Bool)) -> DB [E.Value (Key Submission)]
selectSubmissionsWhere whereBuddies = E.select $ E.from $ \(submission `E.InnerJoin` suser) -> do
E.on $ submission E.^. SubmissionId E.==. suser E.^. SubmissionUserSubmission
let numBuddies = E.sub_select $ E.from $ \subUsers -> do
E.where_ $ subUsers E.^. SubmissionUserSubmission E.==. submission E.^. SubmissionId
E.&&. subUsers E.^. SubmissionUserUser E.!=. E.val duid
return E.countRows
E.where_ $ suser E.^. SubmissionUserUser E.==. E.val duid
E.&&. whereBuddies numBuddies
return $ submission E.^. SubmissionId
getSubmissionFiles :: SubmissionId -> DB [E.Value (Key File)]
getSubmissionFiles subId = E.select $ E.from $ \file -> do
E.where_ $ E.exists $ E.from $ \submissionFile ->
E.where_ $ submissionFile E.^. SubmissionFileSubmission E.==. E.val subId
E.&&. submissionFile E.^. SubmissionFileFile E.==. file E.^. FileId
return $ file E.^. FileId
deleteSingleSubmissionGroups = E.deleteCount $ E.from $ \submissionGroup -> do
E.where_ $ E.exists $ E.from $ \subGroupUser ->
E.where_ $ subGroupUser E.^. SubmissionGroupUserSubmissionGroup E.==. submissionGroup E.^. SubmissionGroupId
E.&&. subGroupUser E.^. SubmissionGroupUserUser E.==. E.val duid
E.where_ $ E.notExists $ E.from $ \subGroupUser ->
E.where_ $ subGroupUser E.^. SubmissionGroupUserSubmissionGroup E.==. submissionGroup E.^. SubmissionGroupId
E.&&. subGroupUser E.^. SubmissionGroupUserUser E.!=. E.val duid
getProfileDataR :: Handler Html
getProfileDataR = do
(uid, User{..}) <- requireAuthPair
userEnt <- requireAuth
dataWidget <- runDB $ makeProfileData userEnt
defaultLayout $ do
dataWidget
$(widgetFile "dsgvDisclaimer")
makeProfileData :: (Entity User) -> DB Widget
makeProfileData (Entity uid User{..}) = do
-- MsgRenderer mr <- getMsgRenderer
(admin_rights,lecturer_rights,lecture_corrector,studies) <- runDB $ (,,,) <$>
E.select
( E.from $ \(adright `E.InnerJoin` school) -> do
E.where_ $ adright E.^. UserAdminUser E.==. E.val uid
E.on $ adright E.^. UserAdminSchool E.==. school E.^. SchoolId
return (school E.^. SchoolShorthand)
)
<*>
E.select
( E.from $ \(lecright `E.InnerJoin` school) -> do
E.where_ $ lecright E.^. UserLecturerUser E.==. E.val uid
E.on $ lecright E.^. UserLecturerSchool E.==. school E.^. SchoolId
return (school E.^. SchoolShorthand)
)
<*>
E.select
( E.distinct $ E.from $ \(sheet `E.InnerJoin` corrector `E.InnerJoin` course) -> do
E.on $ sheet E.^. SheetCourse E.==. course E.^. CourseId
E.on $ sheet E.^. SheetId E.==. corrector E.^. SheetCorrectorSheet
E.where_ $ corrector E.^. SheetCorrectorUser E.==. E.val uid
return (course E.^. CourseTerm, course E.^. CourseSchool, course E.^. CourseShorthand)
)
<*>
E.select
( E.from $ \(studydegree `E.InnerJoin` studyfeat `E.InnerJoin` studyterms) -> do
E.where_ $ studyfeat E.^. StudyFeaturesUser E.==. E.val uid
E.on $ studyfeat E.^. StudyFeaturesField E.==. studyterms E.^. StudyTermsId
E.on $ studyfeat E.^. StudyFeaturesDegree E.==. studydegree E.^. StudyDegreeId
return (studyfeat, studydegree, studyterms)
)
( (hasRows, ownedCoursesTable)
, enrolledCoursesTable
, submissionTable
, submissionGroupTable
, correctionsTable
) <- runDB $ (,,,,)
<$> mkOwnedCoursesTable uid -- Tabelle mit eigenen Kursen
<*> mkEnrolledCoursesTable uid -- Tabelle mit allen Teilnehmer: Kurs (link), Datum
<*> mkSubmissionTable uid -- Tabelle mit allen Abgaben und Abgabe-Gruppen
<*> mkSubmissionGroupTable uid -- Tabelle mit allen Abgabegruppen
<*> mkCorrectionsTable uid -- Tabelle mit allen Korrektor-Aufgaben
admin_rights <- E.select $ E.from $ \(adright `E.InnerJoin` school) -> do
E.where_ $ adright E.^. UserAdminUser E.==. E.val uid
E.on $ adright E.^. UserAdminSchool E.==. school E.^. SchoolId
return (school E.^. SchoolShorthand)
lecturer_rights <- E.select $ E.from $ \(lecright `E.InnerJoin` school) -> do
E.where_ $ lecright E.^. UserLecturerUser E.==. E.val uid
E.on $ lecright E.^. UserLecturerSchool E.==. school E.^. SchoolId
return (school E.^. SchoolShorthand)
lecture_corrector <- E.select $ E.distinct $ E.from $ \(sheet `E.InnerJoin` corrector `E.InnerJoin` course) -> do
E.on $ sheet E.^. SheetCourse E.==. course E.^. CourseId
E.on $ sheet E.^. SheetId E.==. corrector E.^. SheetCorrectorSheet
E.where_ $ corrector E.^. SheetCorrectorUser E.==. E.val uid
return (course E.^. CourseTerm, course E.^. CourseSchool, course E.^. CourseShorthand)
studies <- E.select $ E.from $ \(studydegree `E.InnerJoin` studyfeat `E.InnerJoin` studyterms) -> do
E.where_ $ studyfeat E.^. StudyFeaturesUser E.==. E.val uid
E.on $ studyfeat E.^. StudyFeaturesField E.==. studyterms E.^. StudyTermsId
E.on $ studyfeat E.^. StudyFeaturesDegree E.==. studydegree E.^. StudyDegreeId
return (studyfeat, studydegree, studyterms)
--Tables
(hasRows, ownedCoursesTable) <- mkOwnedCoursesTable uid -- Tabelle mit eigenen Kursen
enrolledCoursesTable <- mkEnrolledCoursesTable uid -- Tabelle mit allen Teilnehmer: Kurs (link), Datum
submissionTable <- mkSubmissionTable uid -- Tabelle mit allen Abgaben und Abgabe-Gruppen
submissionGroupTable <- mkSubmissionGroupTable uid -- Tabelle mit allen Abgabegruppen
correctionsTable <- mkCorrectionsTable uid -- Tabelle mit allen Korrektor-Aufgaben
let examTable = [whamlet|Klausuren werden momentan leider noch nicht unterstützt.|]
let ownTutorialTable = [whamlet|Übungsgruppen werden momentan leider noch nicht unterstützt.|]
let tutorialTable = [whamlet|Übungsgruppen werden momentan leider noch nicht unterstützt.|]
lastLogin <- traverse (formatTime SelFormatDateTime) userLastAuthentication
-- Delete Button
(btnWdgt, btnEnctype) <- generateFormPost (buttonForm :: Form ButtonDelete)
defaultLayout $ do
let delWdgt = $(widgetFile "widgets/data-delete/data-delete")
$(widgetFile "profileData")
$(widgetFile "dsgvDisclaimer")
return $(widgetFile "profileData")

View File

@ -16,6 +16,8 @@ import qualified Data.Map as Map
import qualified Database.Esqueleto as E
import qualified Database.Esqueleto.Utils as E
import Handler.Profile (makeProfileData)
hijackUserForm :: CryptoUUIDUser -> Form ()
hijackUserForm cID csrf = do
@ -157,7 +159,7 @@ postAdminUserR uuid = do
uid <- decrypt uuid
let fromSchoolList = Set.fromList . map (userAdminSchool . entityVal)
let unValueRights (school, E.Value isAdmin, E.Value isLecturer) = (school,isAdmin,isLecturer)
(User{..}, fromSchoolList -> adminSchools, fmap unValueRights -> userRights) <- runDB $ (,,)
(user@User{..}, fromSchoolList -> adminSchools, fmap unValueRights -> userRights) <- runDB $ (,,)
<$> get404 uid
<*> selectList [UserAdminUser ==. adminId] []
<*> E.select ( E.from $ \school -> do
@ -172,7 +174,7 @@ postAdminUserR uuid = do
)
-- above data is needed for both form generation and result evaluation
let userRightsForm :: Form [(SchoolId, Bool, Bool)]
userRightsForm csrf = do
userRightsForm = identifyForm FIDuserRights $ \csrf -> do
boxRights <- forM userRights $ \(school@(Entity sid _), isAdmin, isLecturer) ->
if Set.member sid adminSchools
then do
@ -205,5 +207,79 @@ postAdminUserR uuid = do
formResult result userRightsAction
let heading =
[whamlet|_{MsgAccessRightsFor} ^{nameWidget userDisplayName userSurname}|]
siteLayout heading
-- Delete Button needed in data-delete
(btnWdgt, btnEnctype) <- generateFormPost (buttonForm :: Form ButtonDelete)
userDataWidget <- runDB $ makeProfileData $ Entity uid user
siteLayout heading $ do
let deleteWidget = $(widgetFile "widgets/data-delete/data-delete")
$(widgetFile "adminUser")
postAdminUserDeleteR :: CryptoUUIDUser -> Handler Html
postAdminUserDeleteR uuid = do
uid <- decrypt uuid
((btnResult,_), _) <- runFormPost (buttonForm :: Form ButtonDelete)
case btnResult of
(FormSuccess BtnDelete) -> do
User{..} <- runDB $ get404 uid
-- clearCreds False -- Logout-User
((deletedSubmissions,groupSubmissions),deletedSubmissionGroups) <- runDB $ deleteUser uid
-- addMessageIHamlet
$(addMessageFile Success "templates/deletedUser.hamlet") -- USE THIS ONE
-- addMessageI Success $ MsgDeleteUser deletedSubmissions
-- when (groupSubmissions > 0) $ addMessageI Info $ MsgDeleteUserGroupSubmissions groupSubmissions
defaultLayout
$(widgetFile "deletedUser")
-- (FormSuccess BtnAbort ) -> do
-- addMessageI Info MsgAborted
-- redirect ProfileDataR
_other -> getAdminUserR uuid
deleteUser :: UserId -> DB ((Int,Int),Int64) -- TODO: Restrict deletions for lecturers, tutors and students in course that won't allow deregistration
deleteUser duid = do
-- E.deleteCount for submissions is not cascading, hence we first select and then delete manually
-- We delete all files tied to submissions where the user is the lone submissionUser
-- Do not deleteCascade submissions where duid is the corrector:
updateWhere [SubmissionRatingBy ==. Just duid] [SubmissionRatingBy =. Nothing]
groupSubmissions <- selectSubmissionsWhere (\numBuddies -> numBuddies E.>. E.val (0::Int64))
singleSubmissions <- selectSubmissionsWhere (\numBuddies -> numBuddies E.==. E.val (0::Int64))
deleteCascade duid
forM_ singleSubmissions $ \(E.Value submissionId) -> do
deleteFileIds <- map E.unValue <$> getSubmissionFiles submissionId
deleteCascade submissionId
deleteCascadeWhere [FileId <-. deleteFileIds] -- TODO: throws exception for de-duplicated files
deletedSubmissionGroups <- deleteSingleSubmissionGroups
return ((length singleSubmissions, length groupSubmissions),deletedSubmissionGroups)
where
selectSubmissionsWhere :: (E.SqlExpr (E.Value Int64) -> E.SqlExpr (E.Value Bool)) -> DB [E.Value (Key Submission)]
selectSubmissionsWhere whereBuddies = E.select $ E.from $ \(submission `E.InnerJoin` suser) -> do
E.on $ submission E.^. SubmissionId E.==. suser E.^. SubmissionUserSubmission
let numBuddies = E.sub_select $ E.from $ \subUsers -> do
E.where_ $ subUsers E.^. SubmissionUserSubmission E.==. submission E.^. SubmissionId
E.&&. subUsers E.^. SubmissionUserUser E.!=. E.val duid
return E.countRows
E.where_ $ suser E.^. SubmissionUserUser E.==. E.val duid
E.&&. whereBuddies numBuddies
return $ submission E.^. SubmissionId
getSubmissionFiles :: SubmissionId -> DB [E.Value (Key File)]
getSubmissionFiles subId = E.select $ E.from $ \file -> do
E.where_ $ E.exists $ E.from $ \submissionFile ->
E.where_ $ submissionFile E.^. SubmissionFileSubmission E.==. E.val subId
E.&&. submissionFile E.^. SubmissionFileFile E.==. file E.^. FileId
return $ file E.^. FileId
deleteSingleSubmissionGroups = E.deleteCount $ E.from $ \submissionGroup -> do
E.where_ $ E.exists $ E.from $ \subGroupUser ->
E.where_ $ subGroupUser E.^. SubmissionGroupUserSubmissionGroup E.==. submissionGroup E.^. SubmissionGroupId
E.&&. subGroupUser E.^. SubmissionGroupUserUser E.==. E.val duid
E.where_ $ E.notExists $ E.from $ \subGroupUser ->
E.where_ $ subGroupUser E.^. SubmissionGroupUserSubmissionGroup E.==. submissionGroup E.^. SubmissionGroupId
E.&&. subGroupUser E.^. SubmissionGroupUserUser E.!=. E.val duid

View File

@ -44,11 +44,16 @@ simpleLink :: Widget -> Route UniWorX -> Widget
simpleLink lbl url = [whamlet|<a href=@{url}>^{lbl}|]
-- | toWidget-Version of @nameHtml@, for convenience
nameWidget :: Text -> Text -> Widget
nameWidget :: Text -- ^ userDisplayName
-> Text -- ^ userSurname
-> Widget
nameWidget displayName surname = toWidget $ nameHtml displayName surname
-- | toWidget-Version of @nameEmailHtml@, for convenience
nameEmailWidget :: CI Text -> Text -> Text -> Widget
nameEmailWidget :: CI Text -- ^ userEmail
-> Text -- ^ userDisplayName
-> Text -- ^ userSurname
-> Widget
nameEmailWidget email displayName surname = toWidget $ nameEmailHtml email displayName surname
-- | Show user's displayName, highlighting the surname if possible.

View File

@ -125,14 +125,16 @@ linkButton lbl cls url = do
-- buttonForm :: (Button UniWorX a, Finite a) => Markup -> MForm (HandlerT UniWorX IO) (FormResult a, Widget)
buttonForm :: (Button UniWorX a, Finite a) => Form a
buttonForm csrf = do
(res, ($ []) -> fViews) <- aFormToForm . disambiguateButtons $ combinedButtonFieldF ""
return (res, [whamlet|
$newline never
#{csrf}
$forall bView <- fViews
^{fvInput bView}
|])
buttonForm = identifyForm FIDbuttonForm buttonFormAux -- TODO: distinguish diffent buttons despite @disambiguateButtons@
where
buttonFormAux csrf = do
(res, ($ []) -> fViews) <- aFormToForm . disambiguateButtons $ combinedButtonFieldF ""
return (res, [whamlet|
$newline never
#{csrf}
$forall bView <- fViews
^{fvInput bView}
|])
------------

View File

@ -4,7 +4,11 @@ import Data.Either (isLeft)
import Import.NoFoundation
modal :: WidgetT site IO () -> Either (SomeRoute site) (WidgetT site IO ()) -> WidgetT site IO ()
-- | Create a link to a modal
modal :: WidgetT site IO () -- ^ Widget that represents the link
-> Either (SomeRoute site) (WidgetT site IO ()) -- ^ Modal contant: either dynamic link or static widget
-> WidgetT site IO () -- ^ result widget
modal modalTrigger modalContent = do
let modalDynamic = isLeft modalContent
modalId <- newIdent

View File

@ -217,6 +217,8 @@ data FormIdentifier
| FIDDBTable
| FIDDelete
| FIDCourseRegister
| FIDuserRights
| FIDbuttonForm
deriving (Eq, Ord, Read, Show)
instance PathPiece FormIdentifier where
@ -238,7 +240,7 @@ identifyForm' resLens identVal form fragment = do
|]
-- Check if we got its value back.
hasIdent <- (== Just identVal) <$> lookupGlobalPostParamForm PostFormIdentifier
hasIdent <- (== Just identVal) <$> lookupGlobalPostParamForm PostFormIdentifier
-- Run the form proper (with our hidden <input>). If the
-- data is missing, then do not provide any params to the
@ -250,7 +252,7 @@ identifyForm' resLens identVal form fragment = do
identifyForm :: (Monad m, PathPiece ident, Eq ident) => ident -> (Html -> MForm m (FormResult a, widget)) -> (Html -> MForm m (FormResult a, widget))
identifyForm = identifyForm' id
{- Hinweise zur Erinnerung:
- identForm primär, wenn es mehr als ein Formular pro Handler gibt

View File

@ -1,6 +1,14 @@
<p>
$# Does not use link-email.hamlet, but should
^{mailtoHtml userEmail}
<form method=post action=@{AdminUserR uuid} enctype=#{formEnctype}>
^{formWidget}
^{submitButtonView}
<section>
$# Does not use link-email.hamlet, but should
^{mailtoHtml userEmail}
<form method=post action=@{AdminUserR uuid} enctype=#{formEnctype}>
^{formWidget}
^{submitButtonView}
<section>
^{userDataWidget}
<h3>
^{modal "Benutzer löschen" (Right deleteWidget)}
Achtung, dieser Link löscht momentan noch den kompletten Benutzer
unwiderruflich aus der Live-Datenbank mit
<code>DELETE CASCADE uid
\ Klausurdaten müssen jedoch langfristig gespeichert werden!

View File

@ -1,6 +1,10 @@
<div .container>
<h1>
_{MsgUserAccountDeleted userDisplayName}
<div .container>
#{nameEmailHtml userEmail userDisplayName userSurname}
<div .container>
#{mailtoHtml userEmail}
<div .container>
#{display deletedSubmissions} Abgaben wurden unwiederruflich gelöscht.
$if groupSubmissions > 0
@ -12,5 +16,3 @@
$if deletedSubmissionGroups > 0
<div .container>
#{display deletedSubmissionGroups} benannte Abgabengruppen wurden gelöscht, da diese dadurch leer wurden.
<div .container>
Good Bye!

View File

@ -115,20 +115,28 @@
<a href=@{CorrectionsR}>Auflistung aller tatsächlich zugewiesenen Korrekturen
.
<h2>
^{modal "Alle Benutzerbezogenen Daten löschen" (Right delWdgt)}
<p>
<h4>Hinweise:
<section>
<h2>_{MsgRemarks}
<ul>
<li>
Sichern Sie Ihre Daten! Während des Testbetriebs von Uni2work
könnten Daten unabsichtlich unwidderuflich gelöscht werden.
Sichern Sie bitte Ihre Daten! Die Uni2work Datenbank wird täglich gesichert;
dennoch können wir Probleme während des Testbetriebs noch nicht gänzlich ausschließen.
<li>
Nicht aufgeführt sind Zeitstempel mit Benutzerinformationen, z.B. bei der Editierung und Korrektur von Übungen, Übungsgruppenleiterschaft, Raumbuchungen, etc.
<li>
Benutzerdaten bleiben so lange gespeichert, bis ein Institutsadministrator über die Exmatrikulation informiert wurde. Dann wird der Account gelöscht.
Abgaben/Bonuspunkte werden unwiderruflich gelöscht.
Klausurnoten verbleiben aus statistischen Gründen anonymisiert im System.
Sie können die
<a href=@{HelpR}>
Löschung Ihre Daten über eine Supportanfrage beantragen
. Ihre Daten werden dann nach Ablauf einer Frist gelöscht.
Daten, welche keiner gesetzlichen Aufbewahrungsfrist unterliegen
(z.B. Klausurnoten) verbleiben im System bis zur Ablauf der Aufbewahrungsfrist.
<p>
Benutzerdaten bleiben prinzipiell so lange gespeichert,
bis ein Institutsadministrator über die Exmatrikulation informiert wurde.
Dann wird der Account mit einer angemessenen zeitverzögerung gelöscht.
Anonymisierte Klausurnoten verbleiben aus statistischen Gründen dauerhaft im System.
<li>
Bei gemeinsamen Gruppenabgaben wird nur die Zuordnung zu diesem Benutzer gelöscht.
Die Abgabe selbst wird erst gelöscht, wenn alle Benutzer einer Abgabe deren Löschung veranlasst haben.

View File

@ -1,16 +1,22 @@
<form .form-inline method=post action=@{ProfileDataR} enctype=#{btnEnctype}>
<form .form-inline method=post action=@{AdminUserDeleteR uuid} enctype=#{btnEnctype}>
<h2>
Sind Sie sich absolut sicher, alle Ihre in Uni2work gespeicherten Daten zu löschen?
Sind Sie sich absolut sicher
Benutzer ^{nameEmailWidget userEmail userDisplayName userSurname} zu löschen?
<div .container>
Während der Testphase von Uni2work können Sie hiermit
Ihren Account bei Uni2work vollständig löschen.
Mit Ihrem Campus-Account können Sie sich aber danach
jederzeit erneut einloggen, wodurch wieder ein leerer Account erstellt wird.
Während der Testphase von Uni2work
werden Benutzer hiermit vollständig aus der Live-Datenbank mit
<code>DELETE CASCADE uid
gelöscht.
Klausurdaten müssen jedoch unbedingt 5 Jahre bis nach Exmatrikulation
aufbewahrt werden!
<div .container>
Benutzer können sich mit Ihrem Campus-Account
natürlich jederzeit erneut einloggen, wodurch wieder ein leerer Account erstellt wird.
<div .container>
Hochgeladene Hausaufgaben-Dateien werden unabhhängig vom Urherber nur dann gelöscht,
wenn die Dateien ausschließlich Ihnen zugeordnet sind.
wenn die Dateien ausschließlich diesem Benutzer zugeordnet waren.
Dateien aus Gruppenabgaben werden also erst dann gelöscht,
wenn alle Gruppenmitglieder Ihren Account gelöscht haben.
wenn die Accounts alle Gruppenmitglieder gelöscht wurden.
<div .container>
<em>Achtung:
Auch abgegebene Hausübungen werden gelöscht!
@ -19,9 +25,5 @@
(Verbuchte Noten sollten dadurch nicht betroffen sein, aber in einem etwaigen
Streitfall konnen die per Uni2work verwalteten Hausaufgaben dann
auch nicht mehr rekonstruiert/berücksichtigt werden.)
<div .container>
<em>Nach der Testphase von Uni2work wird das Löschen eines Accounts etwas
eingeschränkt werden, da z.B. Klausurnoten 5 Jahre bis nach Exmatrikulation
aufbewahrt werden müssen.
<div .container>
^{btnWdgt}