Add magazine issues admin page.

This commit is contained in:
2020-08-08 17:53:00 +01:00
parent 697bd2149b
commit f24810e808
14 changed files with 1082 additions and 1 deletions

View File

@@ -2,7 +2,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Version>4.0.0.1809</Version>
<Version>4.0.0.1810</Version>
<Company>Canary Islands Computer Museum</Company>
<Copyright>Copyright © 2003-2020 Natalia Portillo</Copyright>
<Product>Canary Islands Computer Museum Website</Product>

View File

@@ -0,0 +1,191 @@
@{
/******************************************************************************
// MARECHAI: Master repository of computing history artifacts information
// ----------------------------------------------------------------------------
//
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2003-2020 Natalia Portillo
*******************************************************************************/
}
@page "/admin/magazine_issues/details/{Id:long}"
@page "/admin/magazine_issues/edit/{Id:long}"
@page "/admin/magazine_issues/create"
@using Marechai.Database
@using Marechai.Database.Models
@inherits OwningComponentBase<MagazineIssuesService>
@inject IStringLocalizer<MagazineIssuesService> L
@inject MagazinesService MagazinesService
@inject NavigationManager NavigationManager
@inject IWebHostEnvironment Host
@inject IJSRuntime JSRuntime
@inject Microsoft.AspNetCore.Identity.UserManager<ApplicationUser> UserManager
@inject AuthenticationStateProvider AuthenticationStateProvider
@attribute [Authorize(Roles = "UberAdmin, Admin")]
<h3>@L["Magazine issue details"]</h3>
<hr />
@if (!_loaded)
{
<p align="center">@L["Loading..."]</p>
return;
}
<div>
<Field>
<FieldLabel>@L["Magazine"]</FieldLabel>
<Select Disabled="!_editing" TValue="long" @bind-SelectedValue="@_model.MagazineId">
@foreach (var magazine in _magazines)
{
<SelectItem TValue="long" Value="@magazine.Id">@magazine.Title</SelectItem>
}
</Select>
</Field>
<Field>
<FieldLabel>@L["Caption using latin script"]</FieldLabel>
<Validation Validator="@ValidateCaption">
<TextEdit ReadOnly="!_editing" @bind-Text="@_model.Caption">
<Feedback>
<ValidationError>@L["Please enter a valid caption."]</ValidationError>
</Feedback>
</TextEdit>
</Validation>
</Field>
@if (_editing || _model.NativeCaption != null)
{
<Field>
<FieldLabel>@L["Native caption, that is, caption using native script (cyrillic, chinese, etc)"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownNativeCaption">@L["Unknown (native caption)"]</Check>
}
@if (!_editing ||
!_unknownNativeCaption)
{
<Validation Validator="@ValidateNativeCaption">
<TextEdit Disabled="!_editing" @bind-Text="@_model.NativeCaption">
<Feedback>
<ValidationError>@L["Please enter a valid native caption."]</ValidationError>
</Feedback>
</TextEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.Published != null)
{
<Field>
<FieldLabel>@L["Published"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownPublished">@L["Unknown (publication date)"]</Check>
}
@if (!_editing || !_unknownPublished)
{
@L["If the date of exact publication is unknown, set as first day of first month the publication applies to. E.g. July/August 1998 -> 1st July 1998"]
<Validation Validator="@ValidatePublished">
<DateEdit TValue="DateTime?" ReadOnly="!_editing" @bind-Date="@_model.Published" >
<Feedback>
<ValidationError>@L["Please enter a valid publication date."]</ValidationError>
</Feedback>
</DateEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.ProductCode != null)
{
<Field>
<FieldLabel>@L["Product code"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownProductCode">@L["Unknown (Product code)"]</Check>
}
@if (!_editing ||
!_unknownProductCode)
{
<Validation Validator="@ValidateProductCode">
<TextEdit ReadOnly="!_editing" @bind-Text="@_model.ProductCode">
<Feedback>
<ValidationError>@L["Please enter a valid product code."]</ValidationError>
</Feedback>
</TextEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.Pages.HasValue)
{
<Field>
<FieldLabel>@L["Pages"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownPages">@L["Unknown (pages)"]</Check>
}
@if (!_editing ||
!_unknownPages)
{
<Validation Validator="@ValidatePages">
<NumericEdit Disabled="!_editing" TValue="short?" Decimals="0" @bind-Value="@_model.Pages">
<Feedback>
<ValidationError>@L["Please enter a valid number of pages."]</ValidationError>
</Feedback>
</NumericEdit>
</Validation>
}
</Field>
}
@if (_editing || _model.IssueNumber.HasValue)
{
<Field>
<FieldLabel>@L["Issue number"]</FieldLabel>
@if (_editing)
{
<Check TValue="bool" @bind-Checked="@_unknownIssueNumber">@L["Unknown (issue number)"]</Check>
}
@if (!_editing ||
!_unknownIssueNumber)
{
<Validation Validator="@ValidateIssueNumber">
<NumericEdit Disabled="!_editing" TValue="uint?" Decimals="0" @bind-Value="@_model.IssueNumber">
<Feedback>
<ValidationError>@L["Please enter a valid issue number."]</ValidationError>
</Feedback>
</NumericEdit>
</Validation>
}
</Field>
}
</div>
<div>
@if (!_editing)
{
<Button Color="Color.Primary" Clicked="@OnEditClicked">@L["Edit"]</Button>
}
else
{
<Button Color="Color.Success" Clicked="@OnSaveClicked">@L["Save"]</Button>
<Button Color="Color.Danger" Clicked="@OnCancelClicked">@L["Cancel"]</Button>
}
<a href="/admin/magazine_issues" class="btn btn-secondary">@L["Back to list"]</a>
</div>

View File

@@ -0,0 +1,172 @@
/******************************************************************************
// MARECHAI: Master repository of computing history artifacts information
// ----------------------------------------------------------------------------
//
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2003-2020 Natalia Portillo
*******************************************************************************/
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Blazorise;
using Marechai.Shared;
using Marechai.ViewModels;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
namespace Marechai.Pages.Admin.Details
{
public partial class MagazineIssue
{
AuthenticationState _authState;
bool _creating;
bool _editing;
bool _loaded;
List<MagazineViewModel> _magazines;
MagazineIssueViewModel _model;
bool _unknownIssueNumber;
bool _unknownNativeCaption;
bool _unknownPages;
bool _unknownProductCode;
bool _unknownPublished;
[Parameter]
public long Id { get; set; }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(_loaded)
return;
_loaded = true;
_creating = NavigationManager.ToBaseRelativePath(NavigationManager.Uri).ToLowerInvariant().
StartsWith("admin/magazine_issues/create", StringComparison.InvariantCulture);
if(Id <= 0 &&
!_creating)
return;
_magazines = await MagazinesService.GetTitlesAsync();
_model = _creating ? new MagazineIssueViewModel() : await Service.GetAsync(Id);
_authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
_editing = _creating || NavigationManager.ToBaseRelativePath(NavigationManager.Uri).ToLowerInvariant().
StartsWith("admin/magazine_issues/edit/",
StringComparison.InvariantCulture);
if(_editing)
SetCheckboxes();
StateHasChanged();
}
void SetCheckboxes()
{
_unknownProductCode = string.IsNullOrWhiteSpace(_model.ProductCode);
_unknownIssueNumber = !_model.IssueNumber.HasValue;
_unknownNativeCaption = string.IsNullOrWhiteSpace(_model.NativeCaption);
_unknownPublished = !_model.Published.HasValue;
_unknownPages = !_model.Pages.HasValue;
}
void OnEditClicked()
{
_editing = true;
SetCheckboxes();
StateHasChanged();
}
async void OnCancelClicked()
{
_editing = false;
if(_creating)
{
NavigationManager.ToBaseRelativePath("admin/magazine_issues");
return;
}
_model = await Service.GetAsync(Id);
SetCheckboxes();
StateHasChanged();
}
async void OnSaveClicked()
{
if(_unknownNativeCaption)
_model.NativeCaption = null;
else if(string.IsNullOrWhiteSpace(_model.NativeCaption))
return;
if(_unknownPages)
_model.Pages = null;
else if(_model.Pages < 1)
return;
if(_unknownIssueNumber)
_model.IssueNumber = null;
else if(_model.IssueNumber < 1)
return;
if(_unknownPublished)
_model.Published = null;
else if(_model.Published?.Date >= DateTime.UtcNow.Date)
return;
// TODO: Recognize JAN-13, EAN-13, UPC, etc
if(_unknownProductCode)
_model.ProductCode = null;
else if(string.IsNullOrWhiteSpace(_model.ProductCode))
return;
if(string.IsNullOrWhiteSpace(_model.Caption))
return;
if(_creating)
Id = await Service.CreateAsync(_model, (await UserManager.GetUserAsync(_authState.User)).Id);
else
await Service.UpdateAsync(_model, (await UserManager.GetUserAsync(_authState.User)).Id);
_editing = false;
_creating = false;
_model = await Service.GetAsync(Id);
SetCheckboxes();
StateHasChanged();
}
void ValidateCaption(ValidatorEventArgs e) =>
Validators.ValidateString(e, L["Caption must be smaller than 256 characters."], 256);
void ValidatePublished(ValidatorEventArgs e) => Validators.ValidateDate(e);
void ValidateNativeCaption(ValidatorEventArgs e) =>
Validators.ValidateString(e, L["Native caption must be smaller than 256 characters."], 256);
void ValidatePages(ValidatorEventArgs e) => Validators.ValidateShort(e, 1);
void ValidateIssueNumber(ValidatorEventArgs e) => Validators.ValidateInteger(e, 1);
void ValidateProductCode(ValidatorEventArgs e) =>
Validators.ValidateString(e, L["Product code must be smaller than 18 characters."], 18);
}
}

View File

@@ -98,6 +98,9 @@
<li>
<a href="/admin/magazines">@L["Magazines"]</a>
</li>
<li>
<a href="/admin/magazine_issues">@L["Magazine issues"]</a>
</li>
</ul>
</div>
<div class="content">

View File

@@ -0,0 +1,98 @@
@{
/******************************************************************************
// MARECHAI: Master repository of computing history artifacts information
// ----------------------------------------------------------------------------
//
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2003-2020 Natalia Portillo
*******************************************************************************/
}
@page "/admin/magazine_issues"
@using Marechai.Database.Models
@inherits OwningComponentBase<MagazineIssuesService>
@inject IStringLocalizer<MagazineIssuesService> L
@inject Microsoft.AspNetCore.Identity.UserManager<ApplicationUser> UserManager
@inject AuthenticationStateProvider AuthenticationStateProvider
@attribute [Authorize(Roles = "UberAdmin, Admin")]
<h3>@L["Magazine issues"]</h3>
@if (_magazineIssues is null)
{
<p>@L["Loading..."]</p>
return;
}
<p>
<a class="btn btn-primary" href="/admin/magazine_issues/create">@L["Create new"]</a>
</p>
<table class="table table-striped">
<thead>
<tr>
<th>
@L["Magazine title"]
</th>
<th>
@L["Published"]
</th>
<th>
@L["Caption"]
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in _magazineIssues)
{
<tr>
<td>
@item.MagazineTitle
</td>
<td>
@($"{item.Published:d}")
</td>
<td>
@item.Caption
</td>
<td>
<a class="btn btn-primary" href="/admin/magazine_issues/details/@item.Id">@L["Details"]</a>
<a class="btn btn-secondary" href="/admin/magazine_issues/edit/@item.Id">@L["Edit"]</a>
<Button Color="Color.Danger" Clicked="() => {ShowModal(item.Id);}">@L["Delete"]</Button>
</td>
</tr>
}
</tbody>
</table>
<Modal @ref="_frmDelete" IsCentered="true" Closing="@ModalClosing">
<ModalBackdrop/>
<ModalContent Centered="true">
<ModalHeader>
<ModalTitle>@L["Delete magazine issue"]</ModalTitle>
<CloseButton Clicked="@HideModal"/>
</ModalHeader>
<ModalBody>
<Text>@string.Format(@L["Are you sure you want to delete the magazine issue published on {0} for magazine {1} with caption {2}?"], _currentMagazineIssue?.Published,_currentMagazineIssue?.MagazineTitle,_currentMagazineIssue?.Caption)</Text>
</ModalBody>
<ModalFooter>
<Button Color="Color.Primary" Clicked="@HideModal" Disabled="@_deleteInProgress">@L["Cancel"]</Button>
<Button Color="Color.Danger" Clicked="@ConfirmDelete" Disabled="@_deleteInProgress">@L["Delete"]</Button>
</ModalFooter>
</ModalContent>
</Modal>

View File

@@ -0,0 +1,88 @@
/******************************************************************************
// MARECHAI: Master repository of computing history artifacts information
// ----------------------------------------------------------------------------
//
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2003-2020 Natalia Portillo
*******************************************************************************/
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Blazorise;
using Marechai.ViewModels;
using Microsoft.AspNetCore.Components.Authorization;
namespace Marechai.Pages.Admin
{
public partial class MagazineIssues
{
MagazineIssueViewModel _currentMagazineIssue;
bool _deleteInProgress;
Modal _frmDelete;
bool _loaded;
List<MagazineIssueViewModel> _magazineIssues;
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(_loaded)
return;
_magazineIssues = await Service.GetAsync();
_loaded = true;
StateHasChanged();
}
void ShowModal(long itemId)
{
_currentMagazineIssue = _magazineIssues.FirstOrDefault(n => n.Id == itemId);
_frmDelete.Show();
}
void HideModal() => _frmDelete.Hide();
async void ConfirmDelete()
{
if(_currentMagazineIssue is null)
return;
_deleteInProgress = true;
_magazineIssues = null;
AuthenticationState authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
// Yield thread to let UI to update
await Task.Yield();
await Service.DeleteAsync(_currentMagazineIssue.Id, (await UserManager.GetUserAsync(authState.User)).Id);
_magazineIssues = await Service.GetAsync();
_deleteInProgress = false;
_frmDelete.Hide();
// Yield thread to let UI to update
await Task.Yield();
// Tell we finished loading
StateHasChanged();
}
void ModalClosing(ModalClosingEventArgs obj) => _currentMagazineIssue = null;
}
}

View File

@@ -35,4 +35,7 @@
<data name="Documents" xml:space="preserve">
<value>Documents</value>
</data>
<data name="Magazine issues" xml:space="preserve">
<value>Magazine issues</value>
</data>
</root>

View File

@@ -234,4 +234,7 @@
<data name="Documents" xml:space="preserve">
<value>Documentos</value>
</data>
<data name="Magazine issues" xml:space="preserve">
<value>Números de revistas</value>
</data>
</root>

View File

@@ -0,0 +1,122 @@
<root>
<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="Magazine issues" xml:space="preserve">
<value>Magazine issues</value>
</data>
<data name="Loading..." xml:space="preserve">
<value>Loading...</value>
</data>
<data name="Create new" xml:space="preserve">
<value>Create new</value>
</data>
<data name="Magazine title" xml:space="preserve">
<value>Magazine title</value>
</data>
<data name="Published" xml:space="preserve">
<value>Published</value>
</data>
<data name="Caption" xml:space="preserve">
<value>Caption</value>
</data>
<data name="Details" xml:space="preserve">
<value>Details</value>
</data>
<data name="Edit" xml:space="preserve">
<value>Edit</value>
</data>
<data name="Delete" xml:space="preserve">
<value>Delete</value>
</data>
<data name="Delete magazine issue" xml:space="preserve">
<value>Delete magazine issue</value>
</data>
<data name="Are you sure you want to delete the magazine issue published on {0} for magazine {1} with caption {2}?" xml:space="preserve">
<value>Are you sure you want to delete the magazine issue published on {0} for magazine {1} with caption {2}?</value>
</data>
<data name="Cancel" xml:space="preserve">
<value>Cancel</value>
</data>
<data name="Magazine issue details" xml:space="preserve">
<value>Magazine issue details</value>
</data>
<data name="Magazine" xml:space="preserve">
<value>Magazine</value>
</data>
<data name="Caption using latin script" xml:space="preserve">
<value>Caption using latin script</value>
</data>
<data name="Please enter a valid caption." xml:space="preserve">
<value>Please enter a valid caption.</value>
</data>
<data name="Native caption, that is, caption using native script (cyrillic, chinese, etc)" xml:space="preserve">
<value>Native caption, that is, caption using native script (cyrillic, chinese, etc)</value>
</data>
<data name="Unknown (native caption)" xml:space="preserve">
<value>Unknown</value>
</data>
<data name="Please enter a valid native caption." xml:space="preserve">
<value>Please enter a valid native caption.</value>
</data>
<data name="Unknown (publication date)" xml:space="preserve">
<value>Unknown</value>
</data>
<data name="If the date of exact publication is unknown, set as first day of first month the publication applies to. E.g. July/August 1998 -&gt; 1st July 1998" xml:space="preserve">
<value>If the date of exact publication is unknown, set as first day of first month the publication applies to. E.g. July/August 1998 -&gt; 1st July 1998</value>
</data>
<data name="Please enter a valid publication date." xml:space="preserve">
<value>Please enter a valid publication date.</value>
</data>
<data name="Product code" xml:space="preserve">
<value>Product code</value>
</data>
<data name="Unknown (Product code)" xml:space="preserve">
<value>Unknown</value>
</data>
<data name="Please enter a valid product code." xml:space="preserve">
<value>Please enter a valid product code.</value>
</data>
<data name="Pages" xml:space="preserve">
<value>Pages</value>
</data>
<data name="Unknown (pages)" xml:space="preserve">
<value>Unknown</value>
</data>
<data name="Please enter a valid number of pages." xml:space="preserve">
<value>Please enter a valid number of pages.</value>
</data>
<data name="Issue number" xml:space="preserve">
<value>Issue number</value>
</data>
<data name="Unknown (issue number)" xml:space="preserve">
<value>Unknown</value>
</data>
<data name="Please enter a valid issue number." xml:space="preserve">
<value>Please enter a valid issue number.</value>
</data>
<data name="Save" xml:space="preserve">
<value>Save</value>
</data>
<data name="Back to list" xml:space="preserve">
<value>Back to list</value>
</data>
<data name="Caption must be smaller than 256 characters." xml:space="preserve">
<value>Caption must be smaller than 256 characters.</value>
</data>
<data name="Product code must be smaller than 18 characters." xml:space="preserve">
<value>Product code must be smaller than 18 characters.</value>
</data>
<data name="Native caption must be smaller than 256 characters." xml:space="preserve">
<value>Native caption must be smaller than 256 characters.</value>
</data>
</root>

View File

@@ -0,0 +1,229 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- ReSharper disable MarkupTextTypo -->
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Magazine issues" xml:space="preserve">
<value>Números de revista</value>
</data>
<data name="Loading..." xml:space="preserve">
<value>Cargando...</value>
</data>
<data name="Create new" xml:space="preserve">
<value>Crear nuevo</value>
</data>
<data name="Magazine title" xml:space="preserve">
<value>Título de la revista</value>
</data>
<data name="Published" xml:space="preserve">
<value>Publicación</value>
</data>
<data name="Caption" xml:space="preserve">
<value>Portada</value>
</data>
<data name="Details" xml:space="preserve">
<value>Detalles</value>
</data>
<data name="Edit" xml:space="preserve">
<value>Editar</value>
</data>
<data name="Delete" xml:space="preserve">
<value>Eliminar</value>
</data>
<data name="Delete magazine issue" xml:space="preserve">
<value>Eliminar número de revista</value>
</data>
<data name="Are you sure you want to delete the magazine issue published on {0} for magazine {1} with caption {2}?" xml:space="preserve">
<value>¿Estás seguro de eliminar el número de la revista {1} publicado el {0} con la portada {2}?</value>
</data>
<data name="Cancel" xml:space="preserve">
<value>Cancelar</value>
</data>
<data name="Magazine issue details" xml:space="preserve">
<value>Detalles de número de revista</value>
</data>
<data name="Magazine" xml:space="preserve">
<value>Revista</value>
</data>
<data name="Caption using latin script" xml:space="preserve">
<value>Portada usando escritura latina</value>
</data>
<data name="Please enter a valid caption." xml:space="preserve">
<value>Por favor introduce un texto de portada válido.</value>
</data>
<data name="Native caption, that is, caption using native script (cyrillic, chinese, etc)" xml:space="preserve">
<value>Portada nativa, es decir, el text de portada usando escritura nativa (cirílico, chino, etc)</value>
</data>
<data name="Unknown (native caption)" xml:space="preserve">
<value>Desconocido</value>
</data>
<data name="Please enter a valid native caption." xml:space="preserve">
<value>Por favor introduce un texto de portada nativo válido.</value>
</data>
<data name="Unknown (publication date)" xml:space="preserve">
<value>Desconocida</value>
</data>
<data name="If the date of exact publication is unknown, set as first day of first month the publication applies to. E.g. July/August 1998 -&gt; 1st July 1998" xml:space="preserve">
<value>Si la fecha exacta de la publicación es desconocida, poner como el primer día del mes al que se aplica la publicación. Ej.: Julio/Agosto 1998 -&gt; 1 de Agosto de 1998</value>
</data>
<data name="Please enter a valid publication date." xml:space="preserve">
<value>Por favor introduce una fecha de publicación válida.</value>
</data>
<data name="Product code" xml:space="preserve">
<value>Código de producto</value>
</data>
<data name="Unknown (Product code)" xml:space="preserve">
<value>Desconocido</value>
</data>
<data name="Please enter a valid product code." xml:space="preserve">
<value>Por favor introduce un código de producto válido.</value>
</data>
<data name="Pages" xml:space="preserve">
<value>Páginas</value>
</data>
<data name="Unknown (pages)" xml:space="preserve">
<value>Desconocido</value>
</data>
<data name="Please enter a valid number of pages." xml:space="preserve">
<value>Por favor introduce un número de páginas válido.</value>
</data>
<data name="Issue number" xml:space="preserve">
<value>Número de publicación</value>
</data>
<data name="Unknown (issue number)" xml:space="preserve">
<value>Desconocido</value>
</data>
<data name="Please enter a valid issue number." xml:space="preserve">
<value>Por favor introduce un número de publicación válido.</value>
</data>
<data name="Save" xml:space="preserve">
<value>Guardar</value>
</data>
<data name="Back to list" xml:space="preserve">
<value>Volver a la lista</value>
</data>
<data name="Caption must be smaller than 256 characters." xml:space="preserve">
<value>El texto de la portada debe contener menos de 256 caracteres.</value>
</data>
<data name="Native caption must be smaller than 256 characters." xml:space="preserve">
<value>La portada nativa debe contener menos de 256 caracteres.</value>
</data>
<data name="Product code must be smaller than 18 characters." xml:space="preserve">
<value>El código de producto debe contener menos de 18 caracteres.</value>
</data>
</root>

View File

@@ -0,0 +1,121 @@
/******************************************************************************
// MARECHAI: Master repository of computing history artifacts information
// ----------------------------------------------------------------------------
//
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2003-2020 Natalia Portillo
*******************************************************************************/
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Marechai.Database.Models;
using Marechai.ViewModels;
using Microsoft.EntityFrameworkCore;
namespace Marechai.Services
{
public class MagazineIssuesService
{
readonly MarechaiContext _context;
public MagazineIssuesService(MarechaiContext context) => _context = context;
public async Task<List<MagazineIssueViewModel>> GetAsync() => await _context.
MagazineIssues.
OrderBy(b => b.Magazine.Title).
ThenBy(b => b.Published).
ThenBy(b => b.Caption).
Select(b => new MagazineIssueViewModel
{
Id = b.Id,
MagazineId = b.MagazineId,
MagazineTitle = b.Magazine.Title,
Caption = b.Caption,
NativeCaption = b.NativeCaption,
Published = b.Published,
ProductCode = b.ProductCode,
Pages = b.Pages,
IssueNumber = b.IssueNumber
}).ToListAsync();
public async Task<MagazineIssueViewModel> GetAsync(long id) =>
await _context.MagazineIssues.Where(b => b.Id == id).Select(b => new MagazineIssueViewModel
{
Id = b.Id,
MagazineId = b.MagazineId,
MagazineTitle = b.Magazine.Title,
Caption = b.Caption,
NativeCaption = b.NativeCaption,
Published = b.Published,
ProductCode = b.ProductCode,
Pages = b.Pages,
IssueNumber = b.IssueNumber
}).FirstOrDefaultAsync();
public async Task UpdateAsync(MagazineIssueViewModel viewModel, string userId)
{
MagazineIssue model = await _context.MagazineIssues.FindAsync(viewModel.Id);
if(model is null)
return;
model.MagazineId = viewModel.MagazineId;
model.Caption = viewModel.Caption;
model.NativeCaption = viewModel.NativeCaption;
model.Published = viewModel.Published;
model.ProductCode = viewModel.ProductCode;
model.Pages = viewModel.Pages;
model.IssueNumber = viewModel.IssueNumber;
await _context.SaveChangesWithUserAsync(userId);
}
public async Task<long> CreateAsync(MagazineIssueViewModel viewModel, string userId)
{
var model = new MagazineIssue
{
MagazineId = viewModel.MagazineId,
Caption = viewModel.Caption,
NativeCaption = viewModel.NativeCaption,
Published = viewModel.Published,
ProductCode = viewModel.ProductCode,
Pages = viewModel.Pages,
IssueNumber = viewModel.IssueNumber
};
await _context.MagazineIssues.AddAsync(model);
await _context.SaveChangesWithUserAsync(userId);
return model.Id;
}
public async Task DeleteAsync(long id, string userId)
{
MagazineIssue item = await _context.MagazineIssues.FindAsync(id);
if(item is null)
return;
_context.MagazineIssues.Remove(item);
await _context.SaveChangesWithUserAsync(userId);
}
}
}

View File

@@ -54,6 +54,15 @@ namespace Marechai.Services
Country = b.Country.Name
}).ToListAsync();
public async Task<List<MagazineViewModel>> GetTitlesAsync() => await _context.
Magazines.OrderBy(b => b.Title).
ThenBy(b => b.FirstPublication).
Select(b => new MagazineViewModel
{
Id = b.Id,
Title = $"{b.Title} ({b.Country.Name}"
}).ToListAsync();
public async Task<MagazineViewModel> GetAsync(long id) => await _context.Magazines.Where(b => b.Id == id).
Select(b => new MagazineViewModel
{

View File

@@ -75,6 +75,7 @@ namespace Marechai.Services
services.AddScoped<DumpsService>();
services.AddScoped<MediaService>();
services.AddScoped<MagazinesService>();
services.AddScoped<MagazineIssuesService>();
}
}
}

View File

@@ -0,0 +1,41 @@
/******************************************************************************
// MARECHAI: Master repository of computing history artifacts information
// ----------------------------------------------------------------------------
//
// Author(s) : Natalia Portillo <claunia@claunia.com>
//
// --[ License ] --------------------------------------------------------------
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// ----------------------------------------------------------------------------
// Copyright © 2003-2020 Natalia Portillo
*******************************************************************************/
using System;
namespace Marechai.ViewModels
{
public class MagazineIssueViewModel : BaseViewModel<long>
{
public long MagazineId { get; set; }
public string MagazineTitle { get; set; }
public string Caption { get; set; }
public string NativeCaption { get; set; }
public DateTime? Published { get; set; }
public string ProductCode { get; set; }
public short? Pages { get; set; }
public uint? IssueNumber { get; set; }
}
}