Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace or create a copy of learning worlds when uploading #500

Merged
merged 7 commits into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- You can now import learning worlds from .zip archives.
- New list of previously uploaded learning worlds that appears after logging in to the LMS.
- Previously uploaded worlds can now be deleted from the LMS and the AdLerBackend.
- Before a learning world is uploaded, the system now checks whether a learning world with the same name already exists on the backend. If there is a duplicate, the author can decide whether to replace the learning world or create a copy.
- Authors can now assign an enrolment key for their learning world.
- Learning outcomes can now be created for learning spaces using an input form.
- A new button in the header bar shows an overview of all learning outcomes in the selected world.
Expand Down
39 changes: 39 additions & 0 deletions IntegrationTest/Dialogues/ReplaceCopyLmsWorldDialogIt.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Threading.Tasks;
using Bunit;
using MudBlazor;
using NUnit.Framework;
using Presentation.Components.Dialogues;
using Shared;

namespace IntegrationTest.Dialogues;

[TestFixture]
public class ReplaceCopyLmsWorldDialogIt : MudDialogTestFixture<ReplaceCopyLmsWorldDialog>
{
[Test]
public async Task CopyButtonPressed_CallsDialogAndReturnsResult()
{
var dialog = await OpenDialogAndGetDialogReferenceAsync();


var buttons = DialogProvider.FindComponents<MudButton>();
buttons[1].Find("button").Click();

var result = await dialog.Result;
Assert.That(result.Data, Is.EqualTo(ReplaceCopyLmsWorldDialogResult.Copy));
Assert.That(result.Canceled, Is.False);
}

[Test]
public async Task CancelButtonPressed_CallsDialogAndReturnsResult()
{
var dialog = await OpenDialogAndGetDialogReferenceAsync();

var buttons = DialogProvider.FindComponents<MudButton>();
buttons[2].Find("button").Click();

var result = await dialog.Result;
Assert.That(result.Data, Is.Null);
Assert.That(result.Canceled, Is.True);
}
}
59 changes: 59 additions & 0 deletions Presentation/Components/Dialogues/ReplaceCopyLmsWorldDialog.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
@using Microsoft.Extensions.Localization
@using System.Diagnostics.CodeAnalysis
@using Shared
<MudDialog>
<DialogContent>
@((MarkupString)Localizer["ReplaceCopyLmsWorldDialog.DialogContent", LmsWorldName].ToString())
</DialogContent>
<DialogActions>
<MudButton Color="Color.Info" OnClick="Replace">@Localizer["ReplaceCopyLmsWorldDialog.ReplaceButtonText"]</MudButton>
<MudButton Color="Color.Warning" OnClick="Copy">@Localizer["ReplaceCopyLmsWorldDialog.CopyButtonText"]</MudButton>
<MudButton Color="Color.Dark" OnClick="Cancel">@Localizer["ReplaceCopyLmsWorldDialog.CancelButtonText"]</MudButton>
</DialogActions>
</MudDialog>

@code {
[CascadingParameter] public MudDialogInstance? MudDialog { get; set; }

[Inject, AllowNull] //can never be null, DI will throw exception on unresolved types - n.stich
IDialogService DialogService { get; set; }

[Inject, AllowNull] //can never be null, DI will throw exception on unresolved types - n.stich
private IStringLocalizer<ReplaceCopyLmsWorldDialog> Localizer { get; set; }

[Parameter] public string LmsWorldName { get; set; } = string.Empty;

private async void Replace()
{
var options = new DialogOptions
{
CloseButton = true,
CloseOnEscapeKey = true,
DisableBackdropClick = true,
};
var parameters = new DialogParameters
{
{ "DialogText", Localizer["ReplaceCopyLmsWorldDialog.ConfirmationDialogText"].ToString() },
{ "SubmitButtonText", Localizer["ReplaceCopyLmsWorldDialog.ConfirmationDialogSubmitButtonText"].ToString() }
};
var dialog = await DialogService.ShowAsync<GenericCancellationConfirmationDialog>(Localizer["ReplaceCopyLmsWorldDialog.ConfirmationDialogTitle"].ToString(), parameters,
options);
var result = await dialog.Result;

if (result.Data is bool && (bool)result.Data)
{
MudDialog!.Close(DialogResult.Ok(ReplaceCopyLmsWorldDialogResult.Replace));
}
}

private void Copy()
{
MudDialog!.Close(DialogResult.Ok(ReplaceCopyLmsWorldDialogResult.Copy));
}

private void Cancel()
{
MudDialog!.Close(DialogResult.Cancel());
}

}
18 changes: 18 additions & 0 deletions Presentation/Presentation.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@
<Compile Remove="PresentationLogic\EntityMapping\LearningElementMapper\LearningElementMapper.cs" />
<Compile Remove="PresentationLogic\EntityMapping\LearningElementMapper\H5PActivationElementMapper.cs" />
<Compile Remove="PresentationLogic\LearningContent\LearningContentViewModel.cs" />
<Compile Update="Resources\Components\Dialogues\OverwriteLmsWorldDialog.de.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>OverwriteLmsWorldDialog.de.resx</DependentUpon>
</Compile>
<Compile Update="Resources\Components\Dialogues\OverwriteLmsWorldDialog.en.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>OverwriteLmsWorldDialog.en.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources\Components\Culture\CultureSelector.en.resx">
Expand Down Expand Up @@ -195,6 +205,14 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>ConditionToggleSwitch.en.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Resources\Components\Dialogues\ReplaceCopyLmsWorldDialog.de.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>OverwriteLmsWorldDialog.de.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Resources\Components\Dialogues\ReplaceCopyLmsWorldDialog.en.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>OverwriteLmsWorldDialog.en.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Update="Resources\Components\LearningOutcomes\CreateEditManualLearningOutcome.de.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>CreateEditManualLearningOutcome.de.Designer.cs</LastGenOutput>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>

<root>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root"
xmlns="">
<xsd:element name="root" msdata:IsDataSet="true">

</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<data name="ReplaceCopyLmsWorldDialog.ReplaceButtonText" xml:space="preserve">
<value>Lernwelt ersetzen</value>
</data>
<data name="ReplaceCopyLmsWorldDialog.CopyButtonText" xml:space="preserve">
<value>Kopie von Lernwelt erstellen</value>
</data>
<data name="ReplaceCopyLmsWorldDialog.CancelButtonText" xml:space="preserve">
<value>Abbrechen</value>
</data>
<data name="ReplaceCopyLmsWorldDialog.DialogContent" xml:space="preserve">
<value>Auf dem LMS Moodle existiert bereits eine Lernwelt mit dem Namen &lt;b&gt;{0}&lt;/b&gt;. Möchten Sie diese Lernwelt ersetzen oder eine Kopie erstellen?</value>
</data>
<data name="ReplaceCopyLmsWorldDialog.ConfirmationDialogText" xml:space="preserve">
<value>Sind Sie sicher, dass Sie die bestehende Lernwelt ersetzen möchten? Alle Daten der bisherigen Lernwelt, einschließlich des Nutzerfortschritts, werden damit unwiderruflich gelöscht.</value>
</data>
<data name="ReplaceCopyLmsWorldDialog.ConfirmationDialogTitle" xml:space="preserve">
<value>Sind Sie sicher?</value>
</data>
<data name="ReplaceCopyLmsWorldDialog.ConfirmationDialogSubmitButtonText" xml:space="preserve">
<value>Ja</value>
</data>
</root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>

<root>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root"
xmlns="">
<xsd:element name="root" msdata:IsDataSet="true">

</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089
</value>
</resheader>
<data name="ReplaceCopyLmsWorldDialog.ReplaceButtonText" xml:space="preserve">
<value>Replace learning world</value>
</data>
<data name="ReplaceCopyLmsWorldDialog.CancelButtonText" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="ReplaceCopyLmsWorldDialog.CopyButtonText" xml:space="preserve">
<value>Create Copy of learning world</value>
</data>
<data name="ReplaceCopyLmsWorldDialog.DialogContent" xml:space="preserve">
<value>A learning world with the name &lt;b&gt;{0}&lt;/b&gt;. already exists on the LMS Moodle. Would you like to replace it or create a copy?</value>
</data>
<data name="ReplaceCopyLmsWorldDialog.ConfirmationDialogText" xml:space="preserve">
<value>Are you sure you want to replace the existing learning world? All data from the previous learning world, including user progress, will be irrevocably deleted.</value>
</data>
<data name="ReplaceCopyLmsWorldDialog.ConfirmationDialogTitle" xml:space="preserve">
<value>Are you sure?</value>
</data>
<data name="ReplaceCopyLmsWorldDialog.ConfirmationDialogSubmitButtonText" xml:space="preserve">
<value>Yes</value>
</data>
</root>
3 changes: 3 additions & 0 deletions Presentation/Resources/View/HeaderBar.de.resx
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@
<data name="HeaderBar.Help.Tutorial" xml:space="preserve">
<value>Tutorial</value>
</data>
<data name="ConfirmReplaceOrCreateCopyDialog.Title" xml:space="preserve">
<value>Lernwelt existiert bereits</value>
</data>
<data name="LearningOutcomes.Overview" xml:space="preserve">
<value>Lernziele dieser Lernwelt</value>
</data>
Expand Down
3 changes: 3 additions & 0 deletions Presentation/Resources/View/HeaderBar.en.resx
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@
<data name="HeaderBar.Help.Tutorial" xml:space="preserve">
<value>Tutorial</value>
</data>
<data name="ConfirmReplaceOrCreateCopyDialog.Title" xml:space="preserve">
<value>Learning world already exists</value>
</data>
<data name="LearningOutcomes.Overview" xml:space="preserve">
<value>Learning outcomes of this learning world</value>
</data>
Expand Down
59 changes: 50 additions & 9 deletions Presentation/View/HeaderBar.razor
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
@using Presentation.PresentationLogic.LearningWorld
@using Presentation.PresentationLogic.Mediator
@using Presentation.PresentationLogic.SelectedViewModels
@using Shared
@using Shared.Exceptions
@using Presentation.PresentationLogic.API
@using Presentation.PresentationLogic.AuthoringToolWorkspace
Expand All @@ -25,7 +26,7 @@

<header class=" grid grid-cols-3 grid-rows-1 w-full bg-buttonbgblue items-center">
<div class="ml-1 flex justify-start items-center gap-2">
<MudIconButton Class="p-1 text-adlerdarkblue" Icon="@Icons.Material.Filled.Home" Size="Size.Large" Title=@Localizer["LearningWorld.Home.Hover"] @onclick="OnClickMyLearningWorldsOverview"></MudIconButton>
<MudIconButton Class="p-1 text-adlerdarkblue" Icon="@Icons.Material.Filled.Home" Size="Size.Large" Title=@Localizer["LearningWorld.Home.Hover"] @onclick="OnClickMyLearningWorldsOverview"></MudIconButton>
<MudDivider Vertical="true" FlexItem="true"/>
<div class="relative">
<MudIconButton Class="p-1 text-adlerdarkblue" Icon="@Icons.Material.Filled.Save" Title=@Localizer["LearningWorld.Save.Hover"] OnClick="TrySave">
Expand Down Expand Up @@ -117,9 +118,8 @@
}

<CultureSelector/>

<MudIconButton Class="p-1 text-adlerdarkblue" OnClick="@ShowLearningOutcomesOverview" Disabled="@(SelectedViewModelsProvider.LearningWorld == null)" Icon="@learningOutcomeIcon" Title="@Localizer["LearningOutcomes.Overview"]"></MudIconButton>

<MudMenu Dense="true" AnchorOrigin="Origin.BottomCenter" Icon="@icon">
<MudMenuItem Disabled="true">@Localizer["HeaderBar.Help.UserManual"]</MudMenuItem>
<MudMenuItem Disabled="true">@Localizer["HeaderBar.Help.Tutorial"]</MudMenuItem>
Expand Down Expand Up @@ -250,12 +250,7 @@
Localizer["Dialog.UploadLearningWorld.DialogText", world.Name].ToString()
},
};
var options = new DialogOptions
{
CloseButton = true,
CloseOnEscapeKey = true,
DisableBackdropClick = true,
};
var options = CreateUnskippableDialogOptions();
var dialog =
await DialogService.ShowAsync<GenericCancellationConfirmationDialog>(Localizer["DialogService.UploadLearningWorld.Dialog"].ToString(), parameters,
options);
Expand All @@ -264,6 +259,13 @@
//if not cancelled, upload LearningWorld
if (result.Canceled) return;

var existingLmsWorlds = await PresentationLogic.GetLmsWorldList();
if (existingLmsWorlds.Any(lmsWorld => lmsWorld.WorldName == world.Name))
{
var confirmReplaceOrCreateCopyAsync = await ConfirmReplaceOrCreateCopyAsync(world.Name, existingLmsWorlds);
if (!confirmReplaceOrCreateCopyAsync) return;
}

//present progress dialog
var cancellationTokenSource = new CancellationTokenSource();
var progress = new Progress<int>();
Expand Down Expand Up @@ -310,6 +312,45 @@
StateHasChanged();
}

private async Task<bool> ConfirmReplaceOrCreateCopyAsync(string worldName, List<LmsWorldViewModel> lmsWorlds)
{
var options = CreateUnskippableDialogOptions();
var parameters = new DialogParameters
{
{ "LmsWorldName", worldName }
};
var replaceCopyLmsWorldDialog = await DialogService.ShowAsync<ReplaceCopyLmsWorldDialog>(@Localizer["ConfirmReplaceOrCreateCopyDialog.Title"], parameters, options);
var result = await replaceCopyLmsWorldDialog.Result;

if (result.Canceled)
{
return false;
}

switch ((ReplaceCopyLmsWorldDialogResult)result.Data)
{
case ReplaceCopyLmsWorldDialogResult.Replace:
await PresentationLogic.DeleteLmsWorld(lmsWorlds.First(lmsWorld => lmsWorld.WorldName == worldName));
break;
case ReplaceCopyLmsWorldDialogResult.Copy:
break;
}

return true;
}

private static DialogOptions CreateUnskippableDialogOptions()
{
var options = new DialogOptions
{
CloseButton = true,
CloseOnEscapeKey = true,
DisableBackdropClick = true,
};
return options;
}


private void ShowUploadSuccessfulDialog(UploadResponseViewModel response)
{
var options = new DialogOptions
Expand Down
Loading
Loading