diff --git a/VocaDbWeb.Core/Code/GravatarUserIconFactory.cs b/VocaDbWeb.Core/Code/GravatarUserIconFactory.cs index 72b994fd6e..83c254052f 100644 --- a/VocaDbWeb.Core/Code/GravatarUserIconFactory.cs +++ b/VocaDbWeb.Core/Code/GravatarUserIconFactory.cs @@ -1,11 +1,11 @@ #nullable disable -using Microsoft.Web.Helpers; using VocaDb.Model.DataContracts; using VocaDb.Model.DataContracts.Users; using VocaDb.Model.Domain.Images; using VocaDb.Model.Domain.Users; using VocaDb.Model.Helpers; +using VocaDb.Web.Helpers; namespace VocaDb.Web.Code { diff --git a/VocaDbWeb.Core/Code/VocaDbPage.cs b/VocaDbWeb.Core/Code/VocaDbPage.cs index 5abd43ba85..f21571d2de 100644 --- a/VocaDbWeb.Core/Code/VocaDbPage.cs +++ b/VocaDbWeb.Core/Code/VocaDbPage.cs @@ -73,6 +73,12 @@ public IHtmlContent ToJS(object obj) public VocaUrlMapper UrlMapper => new VocaUrlMapper(); public IUserPermissionContext UserContext => Context.RequestServices.GetRequiredService(); + + // Code from: https://github.com/aspnet/AspNetWebStack/blob/749384689e027a2fcd29eb79a9137b94cea611a8/src/System.Web.WebPages/WebPageRenderingBase.cs#L178 + public string Culture => Thread.CurrentThread.CurrentCulture.Name; + + // Code from: https://github.com/aspnet/AspNetWebStack/blob/749384689e027a2fcd29eb79a9137b94cea611a8/src/System.Web.WebPages/WebPageRenderingBase.cs#L192 + public string UICulture => Thread.CurrentThread.CurrentUICulture.Name; } public abstract class VocaDbPage : RazorPage @@ -118,6 +124,9 @@ public IHtmlContent ToJS(object obj) public IUserPermissionContext UserContext => Context.RequestServices.GetRequiredService(); + // Code from: https://github.com/aspnet/AspNetWebStack/blob/749384689e027a2fcd29eb79a9137b94cea611a8/src/System.Web.WebPages/WebPageRenderingBase.cs#L178 + public string Culture => Thread.CurrentThread.CurrentCulture.Name; + // Code from: https://github.com/aspnet/AspNetWebStack/blob/749384689e027a2fcd29eb79a9137b94cea611a8/src/System.Web.WebPages/WebPageRenderingBase.cs#L192 public string UICulture => Thread.CurrentThread.CurrentUICulture.Name; } diff --git a/VocaDbWeb.Core/Helpers/ArchivedEntryHtmlExtensions.cs b/VocaDbWeb.Core/Helpers/ArchivedEntryHtmlExtensions.cs new file mode 100644 index 0000000000..e27ccdc993 --- /dev/null +++ b/VocaDbWeb.Core/Helpers/ArchivedEntryHtmlExtensions.cs @@ -0,0 +1,96 @@ +#nullable disable + +using System; +using System.Collections.Generic; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc.Rendering; +using VocaDb.Model.DataContracts; +using VocaDb.Model.DataContracts.Versioning; +using VocaDb.Model.Domain; +using VocaDb.Model.Domain.Globalization; +using VocaDb.Web.Models.Shared.Partials.ArchivedEntry; + +namespace VocaDb.Web.Helpers +{ + public static class ArchivedEntryHtmlExtensions + { + public static IHtmlContent DataRow(this IHtmlHelper htmlHelper, string name, T primary, T compared, Func valGetter, bool preserveLineBreaks = false) + where T : class + { + + var val1 = valGetter(primary); + var val2 = (compared != null ? valGetter(compared) : null); + + return htmlHelper.Partial("Partials/ArchivedEntry/_DataRow", new DataRowViewModel(name, val1, val2, preserveLineBreaks)); + + } + + public static IHtmlContent DataRow(this IHtmlHelper htmlHelper, string name, ComparedVersionsContract comparedVersions, Func valGetter, bool preserveLineBreaks = false) + where T : class + { + + var val1 = valGetter(comparedVersions.FirstData); + var val2 = (comparedVersions.SecondData != null ? valGetter(comparedVersions.SecondData) : null); + + return htmlHelper.Partial("Partials/ArchivedEntry/_DataRow", new DataRowViewModel(name, val1, val2, preserveLineBreaks)); + + } + + public static IHtmlContent DataRowList(this IHtmlHelper htmlHelper, string name, T primary, T compared, Func> valGetter) + where T : class + { + + var val1 = valGetter(primary); + var val2 = (compared != null ? valGetter(compared) : null); + + return htmlHelper.Partial("Partials/ArchivedEntry/_DataRowList", new DataRowListViewModel(name, val1, val2)); + + } + + public static IHtmlContent DataRowList(this IHtmlHelper htmlHelper, string name, ComparedVersionsContract comparedVersions, Func> valGetter) + where T : class + { + + var val1 = valGetter(comparedVersions.FirstData); + var val2 = (comparedVersions.SecondData != null ? valGetter(comparedVersions.SecondData) : null); + + return htmlHelper.Partial("Partials/ArchivedEntry/_DataRowList", new DataRowListViewModel(name, val1, val2)); + + } + + public static string FormatReleaseDate(OptionalDateTimeContract contract) + { + return OptionalDateTime.ToDateTime(contract.Year, contract.Month, contract.Day).ToShortDateString(); + } + + public static IHtmlContent ObjectRefList(this IHtmlHelper htmlHelper, string name, ComparedVersionsContract comparedVersions, + Func> valGetter) where T : class + { + + return DataRowList(htmlHelper, name, comparedVersions, d => DataFormatUtils.GenerateHtml(valGetter(d), objRef => htmlHelper.Partial("Partials/ArchivedEntry/_ObjectRefInfo", new ObjectRefInfoViewModel(objRef)))); + + } + + public static IHtmlContent PictureRow(this IHtmlHelper htmlHelper, string name, ComparedVersionsContract comparedVersions, Func urlGetter) + where T : class + { + + var val1 = urlGetter(comparedVersions.FirstId); + var val2 = (comparedVersions.SecondId != 0 ? urlGetter(comparedVersions.SecondId) : null); + + return htmlHelper.Partial("Partials/ArchivedEntry/_PictureRow", new PictureRowViewModel(name, val1, val2)); + + } + + public static IHtmlContent TranslatedNameRow(this IHtmlHelper htmlHelper, ComparedVersionsContract comparedVersions, Func valGetter) + where T : class + { + + var val1 = valGetter(comparedVersions.FirstData); + var val2 = comparedVersions.SecondData != null ? valGetter(comparedVersions.SecondData) : null; + + return htmlHelper.Partial("Partials/ArchivedEntry/_TranslatedNameRow", new TranslatedNameRowViewModel(val1, val2)); + + } + } +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Helpers/Gravatar.cs b/VocaDbWeb.Core/Helpers/Gravatar.cs new file mode 100644 index 0000000000..99be4299ad --- /dev/null +++ b/VocaDbWeb.Core/Helpers/Gravatar.cs @@ -0,0 +1,47 @@ +// Code from: https://github.com/mono/aspnetwebstack/blob/6248bfd24c31356e75a31c1b1030d4d96f669a6a/src/Microsoft.Web.Helpers/Gravatar.cs + +using System; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Web; +using Microsoft.AspNetCore.Html; +using Microsoft.Web.Helpers; + +namespace VocaDb.Web.Helpers +{ + public static class Gravatar + { + // review - extract conversion of anonymous object to html attributes string into separate helper + public static HtmlString GetHtml(string email, int imageSize = 80, string? defaultImage = null, GravatarRating rating = GravatarRating.Default, string? imageExtension = null, object? attributes = null) + { + var altSpecified = false; + var url = GetUrl(email, imageSize, defaultImage, rating, imageExtension); + var html = new StringBuilder(string.Format(CultureInfo.InvariantCulture, " p.Name)) + { + if (!p.Name.Equals("src", StringComparison.OrdinalIgnoreCase)) + { + var value = p.GetValue(attributes, null); + if (value != null) + { + var encodedValue = HttpUtility.HtmlAttributeEncode(value.ToString()); + html.Append(string.Format(CultureInfo.InvariantCulture, "{0}=\"{1}\" ", p.Name, encodedValue)); + } + if (p.Name.Equals("alt", StringComparison.OrdinalIgnoreCase)) + altSpecified = true; + } + } + } + if (!altSpecified) + html.Append("alt=\"gravatar\" "); + html.Append("/>"); + return new HtmlString(html.ToString()); + } + + public static string GetUrl(string email, int imageSize = 80, string? defaultImage = null, GravatarRating rating = GravatarRating.Default, string? imageExtension = null) + => Microsoft.Web.Helpers.Gravatar.GetUrl(email, imageSize, defaultImage, rating, imageExtension); + } +} diff --git a/VocaDbWeb.Core/Helpers/HtmlPrefixScopeExtensions.cs b/VocaDbWeb.Core/Helpers/HtmlPrefixScopeExtensions.cs new file mode 100644 index 0000000000..5d3e442fcb --- /dev/null +++ b/VocaDbWeb.Core/Helpers/HtmlPrefixScopeExtensions.cs @@ -0,0 +1,102 @@ +// Code from: https://github.com/saad749/BeginCollectionItemCore/blob/6614c505fde60fa72430c9da18999b8ceb7918f7/BeginCollectionItemCore/HtmlPrefixScopeExtensions.cs + +using System; +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using System.IO; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Primitives; +using Microsoft.AspNetCore.Mvc.Rendering; + +namespace VocaDb.Web.Helpers +{ + public static class HtmlPrefixScopeExtensions + { + private const string IdsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_"; + + public static IDisposable BeginCollectionItem(this IHtmlHelper html, string collectionName) + { + return BeginCollectionItem(html, collectionName, html.ViewContext.Writer); + } + + public static IDisposable BeginCollectionItem(this IHtmlHelper html, string collectionName, TextWriter writer) + { + /* + * added Nested collection support for newly added collection items + * as per this http://stackoverflow.com/questions/33916004/nested-list-of-lists-with-begincollectionitem + * and this http://www.joe-stevens.com/2011/06/06/editing-and-binding-nested-lists-with-asp-net-mvc-2/ + */ + if (html.ViewData["ContainerPrefix"] != null) + collectionName = string.Concat(html.ViewData["ContainerPrefix"], ".", collectionName); + + var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName); + var itemIndex = idsToReuse.Count > 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString(); + + string htmlFieldPrefix = $"{collectionName}[{itemIndex}]"; + html.ViewData["ContainerPrefix"] = htmlFieldPrefix; + + /* + * html.Name(); has been removed + * because of incorrect naming of collection items + * e.g. + * let collectionName = "Collection" + * the first item's name was Collection[0].Collection[] + * instead of Collection[] + */ + string indexInputName = $"{collectionName}.index"; + + // autocomplete="off" is needed to work around a very annoying Chrome behaviour + // whereby it reuses old values after the user clicks "Back", which causes the + // xyz.index and xyz[...] values to get out of sync. + writer.WriteLine($@""); + + + return BeginHtmlFieldPrefixScope(html, htmlFieldPrefix); + } + + public static IDisposable BeginHtmlFieldPrefixScope(this IHtmlHelper html, string htmlFieldPrefix) + { + return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix); + } + + private static Queue GetIdsToReuse(HttpContext httpContext, string collectionName) + { + // We need to use the same sequence of IDs following a server-side validation failure, + // otherwise the framework won't render the validation error messages next to each item. + var key = IdsToReuseKey + collectionName; + var queue = (Queue)httpContext.Items[key]; + if (queue == null) + { + httpContext.Items[key] = queue = new Queue(); + + if (httpContext.Request.Method == "POST" && httpContext.Request.HasFormContentType) + { + StringValues previouslyUsedIds = httpContext.Request.Form[collectionName + ".index"]; + if (!string.IsNullOrEmpty(previouslyUsedIds)) + foreach (var previouslyUsedId in previouslyUsedIds) + queue.Enqueue(previouslyUsedId); + } + } + return queue; + } + + internal class HtmlFieldPrefixScope : IDisposable + { + internal readonly TemplateInfo TemplateInfo; + internal readonly string PreviousHtmlFieldPrefix; + + public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix) + { + TemplateInfo = templateInfo; + + PreviousHtmlFieldPrefix = TemplateInfo.HtmlFieldPrefix; + TemplateInfo.HtmlFieldPrefix = htmlFieldPrefix; + } + + public void Dispose() + { + TemplateInfo.HtmlFieldPrefix = PreviousHtmlFieldPrefix; + } + } + } +} diff --git a/VocaDbWeb.Core/Views/ActivityEntry/FollowedArtistActivity.cshtml b/VocaDbWeb.Core/Views/ActivityEntry/FollowedArtistActivity.cshtml new file mode 100644 index 0000000000..6390868606 --- /dev/null +++ b/VocaDbWeb.Core/Views/ActivityEntry/FollowedArtistActivity.cshtml @@ -0,0 +1,22 @@ +@using VocaDb.Web.Models.Shared.Partials.Activityfeed +@model VocaDb.Model.DataContracts.Activityfeed.ActivityEntryForApiContract[] + +@{ + PageProperties.Title = "New activity by followed artists"; +} + + + +@foreach (var entry in Model) { + @Html.Partial("Partials/Activityfeed/_ActivityEntry", new ActivityEntryViewModel(entry)) +} diff --git a/VocaDbWeb.Core/Views/ActivityEntry/Index.cshtml b/VocaDbWeb.Core/Views/ActivityEntry/Index.cshtml new file mode 100644 index 0000000000..ac2c845d7c --- /dev/null +++ b/VocaDbWeb.Core/Views/ActivityEntry/Index.cshtml @@ -0,0 +1,55 @@ +@using VocaDb.Web.Models.Shared.Partials.Activityfeed +@using VocaDb.Web.Helpers +@using Res = VocaDb.Web.Resources.Views.ActivityEntry.IndexStrings +@inherits VocaDb.Web.Code.VocaDbPage +@inject LaravelMixHelper LaravelMixHelper +@inject Login Login + +@{ + PageProperties.Title = Res.RecentActivity; +} + + + + + +
+ @Html.Partial("Partials/Activityfeed/_ActivityEntryKnockout", new ActivityEntryKnockoutViewModel("$parent.getEntryTypeName", "$parent.getActivityFeedEventName", "$parents[1].getChangedFieldNames", true)) +
+ +
+

+ @Res.ViewMore +

+ +@section BodyScripts { + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Admin/ActiveEdits.cshtml b/VocaDbWeb.Core/Views/Admin/ActiveEdits.cshtml new file mode 100644 index 0000000000..5c18c37837 --- /dev/null +++ b/VocaDbWeb.Core/Views/Admin/ActiveEdits.cshtml @@ -0,0 +1,28 @@ +@using VocaDb.Web.Helpers +@model Tuple[] + +@{ + PageProperties.Title = "Active editors"; + ViewBag.Parents = new[] { + Html.ActionLink("Manage", "Index"), + }; +} + + + + + + + + + + + @foreach (var item in Model) { + + + + + + } + +
EntryEditorTime
@item.Item1.Name@item.Item2.Name@item.Item3
diff --git a/VocaDbWeb.Core/Views/Admin/AuditLogEntries.cshtml b/VocaDbWeb.Core/Views/Admin/AuditLogEntries.cshtml new file mode 100644 index 0000000000..20ffb0bd14 --- /dev/null +++ b/VocaDbWeb.Core/Views/Admin/AuditLogEntries.cshtml @@ -0,0 +1,15 @@ +@using VocaDb.Web.Models.Shared.Partials.User +@model VocaDb.Model.DataContracts.Security.AuditLogEntryContract[] + +@foreach (var entry in Model) { + + @entry.Time.ToUniversalTime() + + @Html.Partial("Partials/User/_UserLinkOrName", new UserLinkOrNameViewModel(entry.User, entry.AgentName)) + + + @Html.Raw(entry.Action) + + +} + diff --git a/VocaDbWeb.Core/Views/Admin/Index.cshtml b/VocaDbWeb.Core/Views/Admin/Index.cshtml new file mode 100644 index 0000000000..aac1ee7893 --- /dev/null +++ b/VocaDbWeb.Core/Views/Admin/Index.cshtml @@ -0,0 +1,98 @@ +@using VocaDb.Model.Domain.Security +@inherits VocaDb.Web.Code.VocaDbPage + +@{ + PageProperties.Title = "Site management"; +} + +@functions { + + bool HasPermission(PermissionToken token) { + return UserContext.HasPermission(token); + } + +} + +

Common tasks

+ +

@Html.ActionLink("View users list", "Index", "User")

+ +

@Html.ActionLink("View recent comments", "Index", "Comment")

+ +@if (UserContext.HasPermission(PermissionToken.ViewAuditLog)) { +

@Html.ActionLink("View audit log", "ViewAuditLog")

+} + +@if (HasPermission(PermissionToken.ManageTagMappings)) { +

@Html.ActionLink("Manage tag mappings", "ManageTagMappings")

+

@Html.ActionLink("Manage entry type to tag mappings", "ManageEntryTagMappings")

+} + +@if (UserContext.HasPermission(PermissionToken.ManageEntryReports)) { +

@Html.ActionLink("Manage entry reports", "ViewEntryReports")

+} + +@if (UserContext.HasPermission(PermissionToken.BulkDeletePVs)) { +

@Html.ActionLink("Delete PVs by author", "PVsByAuthor")

+} + +@if (UserContext.HasPermission(PermissionToken.MoveToTrash)) { +

@Html.ActionLink("Manage deleted albums", "Deleted", "Album")

+} + +@if (UserContext.HasPermission(PermissionToken.ManageIPRules)) { +

@Html.ActionLink("Manage IP rules", "ManageIPRules")

+} +
+ +@if (UserContext.HasPermission(PermissionToken.Admin)) { +

Database maintenance tasks

+

+ @Html.ActionLink("View active editors", "ActiveEdits") +

+

+ @Html.ActionLink("Create XML dump", "CreateXmlDump") +

+

+ @Html.ActionLink("Refresh NHibernate 2nd level cache", "RefreshDbCache") +

+

+ @Html.ActionLink("Refresh .NET memory cache", "ClearCaches") +

+ +@* +

+ @Html.ActionLink("Cleanup old log entries", "CleanupOldLogEntries") +

+

+ @Html.ActionLink("Missing thumbs", "CreateMissingThumbs") +

+

+ @Html.ActionLink("Generate image thumbnails", "GeneratePictureThumbs") +

+

+ @Html.ActionLink("Update entry sort names", "UpdateAdditionalNames") +

+

+ @Html.ActionLink("Update NicoIDs", "UpdateNicoIds") +

+

+ @Html.ActionLink("Update all artist strings", "UpdateArtistStrings") +

+

+ @Html.ActionLink("Update album ratings", "UpdateAlbumRatingTotals") +

+

+ @Html.ActionLink("Update link categories", "UpdateLinkCategories") +

+

+ @Html.ActionLink("Update PV Icons", "UpdatePVIcons") +

+

+ @Html.ActionLink("Update favorited song counts", "UpdateSongFavoritedTimes") +

+

+ @Html.ActionLink("Update tag vote counts", "UpdateTagVoteCounts") +

+*@ +} diff --git a/VocaDbWeb.Core/Views/Admin/ManageEntryTagMappings.cshtml b/VocaDbWeb.Core/Views/Admin/ManageEntryTagMappings.cshtml new file mode 100644 index 0000000000..ae3a1c3d6e --- /dev/null +++ b/VocaDbWeb.Core/Views/Admin/ManageEntryTagMappings.cshtml @@ -0,0 +1,99 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Knockout +@inherits VocaDb.Web.Code.VocaDbPage +@inject LaravelMixHelper LaravelMixHelper + +@{ + PageProperties.Title = "Manage entry type to tag mappings"; + ViewBag.Parents = new[] { + Html.ActionLink("Manage", "Index"), + }; + var siteName = BrandableStrings.Layout.SiteName; +} + +
+ Only one tag can be mapped to entry type / sub type combination. +
+ +
+

New mapping

+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ @Html.Partial("Partials/Knockout/_BasicEntryLinkLockingAutoComplete", new BasicEntryLinkLockingAutoCompleteViewModel("tagAutoComplete", "newTargetTag")) +
+
+
+
+ +
+
+
+ +
+ +
+ +

Mappings

+ +
+
+ + @Html.Partial("Partials/Shared/_SaveBtn") + + + + + + + + + + + + + + + + +
Entry typeTag
+ {{entryType.entryType}} - {{entryType.subType}} + + {{tag.name}} + + +
+
+ + @Html.Partial("Partials/Shared/_SaveBtn") + +
+ +@section BodyScripts { + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Admin/ManageIPRules.cshtml b/VocaDbWeb.Core/Views/Admin/ManageIPRules.cshtml new file mode 100644 index 0000000000..9c35b707e9 --- /dev/null +++ b/VocaDbWeb.Core/Views/Admin/ManageIPRules.cshtml @@ -0,0 +1,78 @@ +@using VocaDb.Web.Helpers +@model VocaDb.Model.Domain.Security.IPRule[] + +@{ + PageProperties.Title = "Manage blocked IPs"; + ViewBag.Parents = new[] { + Html.ActionLink("Manage", "Index"), + }; +} + +
+ + @Html.Partial("Partials/Shared/_SaveBtn") + + + +
+ +
+ + + + + + + + + + + + + + + + + + + +
AddressNotesCreated
{{address}} + + {{ created | formatDate: 'L LT' }}@ViewRes.SharedStrings.Remove
+ + @Html.Partial("Partials/Shared/_SaveBtn") + +
+ +
+

Automatically banned IPs

+
    +
  • + @{ Html.RenderPartial("KnockoutPartials/_IPManage"); } +
  • +
+
+ +@section BodyScripts { + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Admin/ManageTagMappings.cshtml b/VocaDbWeb.Core/Views/Admin/ManageTagMappings.cshtml new file mode 100644 index 0000000000..4360bb879c --- /dev/null +++ b/VocaDbWeb.Core/Views/Admin/ManageTagMappings.cshtml @@ -0,0 +1,109 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Knockout +@inherits VocaDb.Web.Code.VocaDbPage +@inject LaravelMixHelper LaravelMixHelper + +@{ + PageProperties.Title = "Manage NicoNicoDouga tag mappings"; + ViewBag.Parents = new[] { + Html.ActionLink("Manage", "Index"), + }; + var siteName = BrandableStrings.Layout.SiteName; +} + +
+ These mappings will be used to automatically add tags from external video streaming service. Currently only NicoNicoDouga is supported. + Tags will be mapped from the source system (NND) to target tag on @siteName. + Multiple tags from the source system may be mapped to a single tag on @siteName, but one source tag can be mapped to only one target tag (additional mappings are ignored). +
+ +
+

New mapping

+
+ +
+ +
+
+
+ +
+ @Html.Partial("Partials/Knockout/_BasicEntryLinkLockingAutoComplete", new BasicEntryLinkLockingAutoCompleteViewModel("tagAutoComplete", "newTargetTag")) +
+
+
+
+ +
+
+
+ +
+ +
+ +

Mappings

+ +
+ + + +
+ +
+
+ + @Html.Partial("Partials/Shared/_SaveBtn") + +
+ @Html.Partial("Partials/Knockout/_ServerSidePaging") +
+ + + + + + + + + + + + + + + + +
Source (NND)Target (@siteName)
+ {{sourceTag}} + + {{tag.name}} + + +
+
+ +
+ @Html.Partial("Partials/Knockout/_ServerSidePaging") +
+ + @Html.Partial("Partials/Shared/_SaveBtn") + +
+ +@section BodyScripts { + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Admin/PVsByAuthor.cshtml b/VocaDbWeb.Core/Views/Admin/PVsByAuthor.cshtml new file mode 100644 index 0000000000..6a30ea066c --- /dev/null +++ b/VocaDbWeb.Core/Views/Admin/PVsByAuthor.cshtml @@ -0,0 +1,57 @@ +@using VocaDb.Model.Domain.Security +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Song +@model VocaDb.Web.Models.Admin.PVsByAuthor +@inject Login Login + +@{ + PageProperties.Title = "PVs by author"; + ViewBag.Parents = new[] { + Html.ActionLink("Manage", "Index"), + }; +} + +@using (Html.BeginForm("PVsByAuthor", "Admin", FormMethod.Post, new { @class = "form form-inline" })) { + + @Html.TextBoxFor(m => m.Author, new { id = "author" }) + + + +} + +@if (Model.PVs.Any()) { +

@Model.PVs.Length PVs found

+ + + @foreach (var pv in Model.PVs) { + + + + + + } +
@pv.Name + @pv.Name
+ @pv.Service (@pv.PVType): @pv.PVId +
@Html.Partial("Partials/Song/_SongLink", new SongLinkViewModel(pv.Song))
+ + if (Login.Manager.HasPermission(PermissionToken.BulkDeletePVs)) { +
+ Delete PVs by this author + } + +} + +@section BodyScripts { + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Admin/Partials/_SFSCheckResponse.cshtml b/VocaDbWeb.Core/Views/Admin/Partials/_SFSCheckResponse.cshtml new file mode 100644 index 0000000000..d2c6a7e45d --- /dev/null +++ b/VocaDbWeb.Core/Views/Admin/Partials/_SFSCheckResponse.cshtml @@ -0,0 +1,81 @@ +@using VocaDb.Model.Service.Security.StopForumSpam +@using VocaDb.Web.Code.Security +@model SFSResponseContract + + + + + + + + + + + + + + + @if (Model.Appears) { + + + + + + + + + + + + + } +
+ IP: + + @Model.IP +
+ Conclusion: + + @if (Model.Conclusion == SFSCheckResultType.Malicious) { + + Malicious + + } else if (Model.Conclusion == SFSCheckResultType.Uncertain) { + + Suspicious + + } else { + + Likely harmless + + } +
+ Match: + + @if (Model.Appears) { + + Yes + + } else { + + No + + } +
+ Frequency: + + @Model.Frequency +
+ Confidence: + + @Model.Confidence % +
+ Last seen: + + @Model.LastSeen.ToShortDateString() +
+
+ + View on StopForumSpam + + diff --git a/VocaDbWeb.Core/Views/Admin/ViewEntryReports.cshtml b/VocaDbWeb.Core/Views/Admin/ViewEntryReports.cshtml new file mode 100644 index 0000000000..84899cf831 --- /dev/null +++ b/VocaDbWeb.Core/Views/Admin/ViewEntryReports.cshtml @@ -0,0 +1,89 @@ +@using VocaDb.Model.DataContracts +@using VocaDb.Model.DataContracts.Users +@using VocaDb.Model.Domain +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.User +@model EntryReportContract[] + +@{ + PageProperties.Title = "View entry reports"; + ReportStatus status = ViewBag.ReportStatus; +} + +@{ + void UserIconLinkOrName(UserForApiContract user, string name, int size = 20) { + if (user != null) { + @Html.Partial("Partials/User/_UserIconLink_UserForApiContract", new UserIconLink_UserForApiContractViewModel(user, size)) + } else { + @name + } + } +} + + + +
+ This list contains entries that have been reported for errors. + The list is shared between all trusted users and moderators, and anyone can take action based on these reported issues. + If you have time, please check that the reports are valid, and either notify the user who created the entry in the first place, + or correct the errors yourself. After the issue has been resolved you can delete the report, but not before. +
+ + + + + + + + + + @if (status == ReportStatus.Open) { + + } + @if (status == ReportStatus.Closed) { + + + } + + + + @foreach (var r in Model) { + + + + + + + @if (status == ReportStatus.Open) { + + } + @if (status == ReportStatus.Closed) { + + + } + + } + +
TimeAuthorEntryTypeNotesActionsClosed byClosed at
@r.Created@{ UserIconLinkOrName(r.User, r.Hostname); } + @r.Entry.Name + + @if (r.Version != null) { + var versionUrl = Url.Action("ViewVersion", r.Entry.EntryType.ToString(), new { id = r.Version.Id }); + @:(Version @r.Version.Version) + } + @r.ReportTypeName +
@r.Notes
+
+ Close + + @Html.Partial("Partials/User/_UserIconLink_UserForApiContract", new UserIconLink_UserForApiContractViewModel(r.ClosedBy)) + + @r.ClosedAt +
diff --git a/VocaDbWeb.Core/Views/Admin/ViewSysLog.cshtml b/VocaDbWeb.Core/Views/Admin/ViewSysLog.cshtml new file mode 100644 index 0000000000..7780fdb00c --- /dev/null +++ b/VocaDbWeb.Core/Views/Admin/ViewSysLog.cshtml @@ -0,0 +1,12 @@ +@model VocaDb.Web.Models.Admin.ViewSysLog + +@{ + PageProperties.Title = "View system log"; + ViewBag.Parents = new[] { + Html.ActionLink("Manage", "Index"), + }; +} + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Album/Deleted.cshtml b/VocaDbWeb.Core/Views/Album/Deleted.cshtml new file mode 100644 index 0000000000..3fd8a8d696 --- /dev/null +++ b/VocaDbWeb.Core/Views/Album/Deleted.cshtml @@ -0,0 +1,26 @@ +@inherits VocaDb.Web.Code.VocaDbPage + +@{ + PageProperties.Title = "Deleted albums"; + ViewBag.Parents = new[] { Html.ActionLink(ViewRes.SharedStrings.Albums, "Index") }; +} + + + +@{ Html.RenderPartial("Partials/_AlbumSearchList"); } + +@section BodyScripts { + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Album/Details.cshtml b/VocaDbWeb.Core/Views/Album/Details.cshtml new file mode 100644 index 0000000000..6a4db2222f --- /dev/null +++ b/VocaDbWeb.Core/Views/Album/Details.cshtml @@ -0,0 +1,592 @@ +@using VocaDb.Model.Domain +@using VocaDb.Model.Domain.Security +@using VocaDb.Model.Domain.Songs +@using VocaDb.Model.Domain.Albums +@using VocaDb.Model.Domain.Images +@using VocaDb.Model.Helpers +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models +@using VocaDb.Web.Models.Shared +@using VocaDb.Web.Models.Shared.Partials.Album +@using VocaDb.Web.Models.Shared.Partials.Artist +@using VocaDb.Web.Models.Shared.Partials.Comment +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@using VocaDb.Web.Models.Shared.Partials.Html +@using VocaDb.Web.Models.Shared.Partials.PV +@using VocaDb.Web.Models.Shared.Partials.Shared +@using VocaDb.Web.Models.Shared.Partials.Song +@using VocaDb.Web.Models.Shared.Partials.User +@using R = ViewRes.Album +@using Res = ViewRes.Album.DetailsStrings +@using EntryRes = ViewRes.EntryDetailsStrings +@model AlbumDetails +@inject LaravelMixHelper LaravelMixHelper +@inject Login Login + +@{ + ViewBag.Parents = new[] { Html.ActionLink(ViewRes.SharedStrings.Albums, "Index") }; + var url = PageProperties.CanonicalUrl; + + PageProperties.Robots = Model.Deleted ? PagePropertiesData.Robots_Noindex_Follow : string.Empty; +} + +@section Head { + @Html.Partial("Partials/Html/_OpenGraphMetaTags", new OpenGraphMetaTagsViewModel(PageProperties)) + + +} + +@section Toolbar { + + @ViewRes.Album.DetailsStrings.AddToCollection + @ViewRes.Album.DetailsStrings.UpdateCollection + + @Html.ActionLink(ViewRes.SharedStrings.Edit, "Edit", new { id = Model.Id }, new { id = "editAlbumLink", @class = (Model.CanEdit ? "" : "disabled") }) + + @Html.ActionLink(ViewRes.EntryDetailsStrings.ViewModifications, "Versions", new { id = Model.Id }, new { id = "viewVersions" }) + + + @Html.ActionLink(ViewRes.Album.DetailsStrings.DownloadTrackList, "DownloadTags", new { id = Model.Id }, new { id = "downloadTags" }) + @R.DetailsStrings.Customize + + + @ViewRes.EntryDetailsStrings.ReportAnError + + @Html.Partial("Partials/Shared/_EntryStatusMessage", new EntryStatusMessageViewModel(Model.Status)) +} + +@if (Model.Deleted) +{ + @Html.Partial("Partials/EntryDetails/_DeletedBanner", new DeletedBannerViewModel(Model.MergedTo)) +} + +@if (Model.Draft && !Model.Deleted) +{ + @Html.Partial("Partials/Shared/_DraftMessage", new DraftMessageViewModel("glalbums")) +} + +
+ + + +
+ +
+ +
+ + + @if (Model.RatingCount > 0) + { +
+ + @Html.Partial("Partials/Shared/_StarsMetaSpan", new StarsMetaSpanViewModel(Model.RatingAverage, 5))
+ (@Model.RatingCount @Res.Ratings) +
+ } +
+ + + + + + + + @if (Model.Vocalists.Any()) + { + + + + + } + + @if (Model.Subject != null && Model.Subject.Any()) + { + + + + + } + + @if (Model.Producers.Any()) + { + + + + + } + + @if (Model.Illustrators != null && Model.Illustrators.Any()) + { + + + + + } + + @if (Model.Bands.Any()) + { + + + + + } + + @if (Model.Circles.Any()) + { + + + + + } + + @if (Model.Labels.Any()) + { + + + + + } + + @if (Model.OtherArtists.Any()) + { + + + + + } + + @if (!Model.Description.IsEmpty) + { + + + + + } + + + + + + + + + + + + @if (Model.ReleaseDate != null && !Model.ReleaseDate.IsEmpty) + { + + + + + } + + @if (!string.IsNullOrEmpty(Model.CatNum)) + { + + + + + } + + @if (Model.ReleaseEvent != null) + { + + + + + } + + @if (Model.PVs.Any()) + { + + + + + } + + @Html.Partial("Partials/EntryDetails/_ExternalLinksRows", new ExternalLinksRowsViewModel(Model.WebLinks)) + + + + + + + + + + +
@ViewRes.SharedStrings.Name + @Model.Name
+ @Model.AdditionalNames +
@Res.Vocalists + @Html.Partial("Partials/Artist/_ArtistList", new ArtistListViewModel(Model.Vocalists, showRoles: ShowRolesMode.IfNotVocalist, showType: true)) +
@EntryRes.Subject + @Html.Partial("Partials/Artist/_ArtistList", new ArtistListViewModel(Model.Subject)) +
@Res.Producers + @Html.Partial("Partials/Artist/_ArtistList", new ArtistListViewModel(Model.Producers, showRoles: Model.ShowProducerRoles ? ShowRolesMode.IfNotDefault : ShowRolesMode.Never)) +
@Res.Illustrators + @Html.Partial("Partials/Artist/_ArtistList", new ArtistListViewModel(Model.Illustrators)) +
@Res.Band + @Html.Partial("Partials/Artist/_ArtistList", new ArtistListViewModel(Model.Bands)) +
@Res.Circle + @Html.Partial("Partials/Artist/_ArtistList", new ArtistListViewModel(Model.Circles)) +
@Res.Labels + @Html.Partial("Partials/Artist/_ArtistList", new ArtistListViewModel(Model.Labels)) +
@Res.OtherArtists + @Html.Partial("Partials/Artist/_ArtistList", new ArtistListViewModel(Model.OtherArtists, ShowRolesMode.IfNotDefault)) +
@ViewRes.SharedStrings.Description + @{ Html.RenderPartial("Partials/_EnglishTranslatedString", new EnglishTranslatedStringViewModel(Model.Description)); } +
@ViewRes.SharedStrings.Type + @if (Model.DiscTypeTypeTag != null) + { + @Translate.DiscTypeName(Model.DiscType) + } + else + { + @Translate.DiscTypeName(Model.DiscType) + } +
@ViewRes.SharedStrings.Tags + + @ViewRes.EntryDetailsStrings.EditTags + @if (Model.CanRemoveTagUsages) + { + @Html.ActionLink(ViewRes.EntryDetailsStrings.ManageTags, "ManageTagUsages", new { id = Model.Id }, new { id = "manageTags" }) + } +
@Res.ReleaseDate + @Model.ReleaseDate.Formatted + @if (Model.ReleaseDateIsInThePast) + { + @Res.ReleaseDateIsInThePast + } + @if (Model.ReleaseDateIsInTheNearFuture) + { + @Res.ReleaseDateIsInTheNearFuture + } + @if (Model.ReleaseDateIsInTheFarFuture) + { + @Res.ReleaseDateIsInTheFarFuture + } +
+
@Res.CatalogNumber + @Model.CatNum
+
@Res.ReleaseEvent + @Model.ReleaseEvent.Name +
@Res.PVs + @foreach (var pv in Model.PVs) + { + @Html.Partial("Partials/Shared/_FormatPVLink", new FormatPVLinkViewModel(pv, false))
+ } +
@ViewRes.EntryDetailsStrings.Stats + + @string.Format(ViewRes.EntryDetailsStrings.Hits, Model.Hits). + @string.Format(R.DetailsStrings.OwnedBy, Model.OwnedBy). @string.Format(R.DetailsStrings.WishlistedBy, Model.WishlistedBy). + +
@ViewRes.EntryDetailsStrings.AdditionDate + @Html.Partial("Partials/Shared/_UniversalTimeLabel", new UniversalTimeLabelViewModel(Model.CreateDate)) +
+
+ + @if (Model.Discs.Any() || Model.ContentFocus != ContentFocus.Illustration) + { +

+ @Res.Tracklist + @if (Model.TotalLength != TimeSpan.Zero) + { + (@string.Format(Res.TotalLength, DateTimeHelper.FormatMinSec(Model.TotalLength))) + } +

+
+ @foreach (var disc in Model.Discs) + { + if (Model.Discs.Length > 1) + { +

+ @Res.Disc @disc.DiscNumber + @if (!string.IsNullOrEmpty(disc.Name)) + { + @:- @disc.Name + } + @if (disc.TotalLength != TimeSpan.Zero) + { + (@DateTimeHelper.FormatMinSec(disc.TotalLength)) + } + @if (disc.IsVideo) + { + + } +

+ } +
    + @foreach (var song in disc.Songs) + { +
  • +
    + @song.TrackNumber +
    +
    + @if (song.Song != null) + { + @Html.Partial("Partials/Song/_SongLink", new SongLinkViewModel(song.Song, Model.Id, tooltip: true)) + if (song.Song.LengthSeconds > 0) + { + @(" (" + DateTimeUtils.FormatFromSeconds(song.Song.LengthSeconds) + ")") + } + @:  + @Html.Partial("Partials/Shared/_DraftIcon", new DraftIconViewModel(song.Song.Status)) + if (song.Song.SongType != SongType.Original && song.Song.SongType != SongType.Unspecified) + { + @Html.Partial("Partials/Song/_SongTypeLabel", new SongTypeLabelViewModel(song.Song.SongType)) + } + @Html.Partial("Partials/Shared/_PVServiceIcons", new PVServiceIconsViewModel(song.Song.PVServices)) + @Html.Partial("Partials/Song/_RatingIcon", new RatingIconViewModel(song.Rating ?? SongVoteRating.Nothing)) +
    + @song.Song.ArtistString + } + else + { + @song.Name + } +
    +
  • + } +
+ } +
+ } + + @if (!Model.Discs.Any() && Model.ContentFocus != ContentFocus.Illustration) + { +

@Res.NoTracklist

+ } + + @if (Model.PrimaryPV != null) + { +
+ @Html.Partial("Partials/PV/_EmbedPV", new EmbedPVViewModel(Model.PrimaryPV)) +
+ } + + @if (Model.CanEditPersonalDescription || !string.IsNullOrEmpty(Model.PersonalDescriptionText)) + { +

+ @EntryRes.PersonalDescription +

+
+ + Thumb + + +
+ @if (Model.CanEditPersonalDescription) + { + + } +

+ +

+
+

+
+
+ +
+ +
+ + +
+
+
+ } + + @Html.Partial("Partials/Comment/_LatestCommentsKnockout") + +

+ @ViewRes.EntryDetailsStrings.ViewAllComments +

+ + @if (Model.LatestReview != null) + { +

@Res.LatestReview

+ +
+ + @Html.Partial("Partials/User/_ProfileIcon", new ProfileIconViewModel(Model.LatestReview.User.MainPicture.UrlThumb, 70)) + + +
+
+ @Html.Partial("Partials/Html/_LanguageFlag", new LanguageFlagViewModel(Model.LatestReview.LanguageCode)) + | + @VocaDb.Web.Code.TimeAgoStringBuilder.FormatTimeAgo(Model.LatestReview.Date) +
+

+ @Html.Partial("Partials/User/_UserLink", new UserLinkViewModel(Model.LatestReview.User)) +

+ + @if (Model.LatestReviewRatingScore > 0) + { + @Html.Partial("Partials/Shared/_Stars", new StarsViewModel(Model.LatestReviewRatingScore, 5)) + } + +
+ @if (!string.IsNullOrEmpty(Model.LatestReview.Title)) + { +

@Model.LatestReview.Title

+ } + +
@Html.Partial("Partials/Html/_FormatMarkdown", new FormatMarkdownViewModel(Model.LatestReview.Text))
+
+
+
+ +

+ @string.Format(Res.ViewAllReviews, Model.ReviewCount) +

+ } + +
+ +
+ @Html.Partial("Partials/Comment/_EditableComments", new EditableCommentsViewModel(UserContext.HasPermission(PermissionToken.CreateComments), well: false)) +
+ +
+ @{ Html.RenderPartial("Partials/_AlbumReviews"); } +
+ +
+ +
    + @Html.Partial("Partials/Shared/_ThumbItem", new ThumbItemViewModel(Url.Action("CoverPicture", "Album", new { id = Model.Id, v = Model.Version }), Url.ImageThumb(Model, ImageSize.Thumb), ViewRes.Album.DetailsStrings.CoverPicture)) + @foreach (var pic in Model.Pictures) + { + @Html.Partial("Partials/Shared/_ThumbItem", new ThumbItemViewModel(Url.ImageThumb(pic, ImageSize.Original), Url.ImageThumb(pic, ImageSize.Thumb), pic.Name)) + } +
+ +
+ +
+
+ +
+
+ @ViewRes.EntryDetailsStrings.Link
+ +
+
+ Markdown
+ +
+
+ +
+ +
+ @{ Html.RenderPartial("Partials/_TagsEdit"); } +
+ +
+ +
+ +
+ +@{ Html.RenderPartial("Partials/_DownloadTagsDialog"); } + +@Html.Partial("Partials/Album/_EditCollectionDialog", new EditCollectionDialogViewModel(Model.AlbumPurchaseStatus, Model.AlbumMediaType)) + +@Html.Partial("Partials/EntryDetails/_ReportEntryPopupKnockout", new ReportEntryPopupKnockoutViewModel()) + +@section BodyScripts { + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Album/ManageTagUsages.cshtml b/VocaDbWeb.Core/Views/Album/ManageTagUsages.cshtml new file mode 100644 index 0000000000..097f03625b --- /dev/null +++ b/VocaDbWeb.Core/Views/Album/ManageTagUsages.cshtml @@ -0,0 +1,14 @@ +@using VocaDb.Model.Domain +@using VocaDb.Web.Models.Shared.Partials.Tag +@model VocaDb.Model.DataContracts.UseCases.EntryWithTagUsagesContract + +@{ + PageProperties.Title = "Manage tag usages - " + Model.DefaultName; + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.Albums, "Index"), + Html.ActionLink(Model.DefaultName, "Details", new { id = Model.Id }) + }; +} + + +@Html.Partial("Partials/Tag/_TagUsagesManageTable", new TagUsagesManageTableViewModel(EntryType.Album, Model.TagUsages, Model.CanRemoveTagUsages)) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Album/Merge.cshtml b/VocaDbWeb.Core/Views/Album/Merge.cshtml new file mode 100644 index 0000000000..de2de6a471 --- /dev/null +++ b/VocaDbWeb.Core/Views/Album/Merge.cshtml @@ -0,0 +1,64 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Knockout +@model VocaDb.Model.DataContracts.Albums.AlbumContract +@inject LaravelMixHelper LaravelMixHelper + +@{ + PageProperties.Title = "Merge album - " + Model.Name; + ViewBag.Parents = new[] { + Html.ActionLink("Albums", "Index"), + Html.ActionLink(Model.Name, "Details", new { id = Model.Id }), + Html.ActionLink("Edit", "Edit", new { id = Model.Id }), + }; +} + +@using (Html.BeginForm()) +{ + + @Html.Partial("Partials/Shared/_MergeEntryInfo") + + + +
+
+
+ @Html.Partial("Partials/Knockout/_LockingAutoComplete", new LockingAutoCompleteViewModel("albumAutoComplete", "targetSearchParams", "target.name", "target.id")) +
+
+ + + +
+
+ +
+ + + +} + +@section BodyScripts { + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Album/Partials/_AlbumReviews.cshtml b/VocaDbWeb.Core/Views/Album/Partials/_AlbumReviews.cshtml new file mode 100644 index 0000000000..4d67b8830b --- /dev/null +++ b/VocaDbWeb.Core/Views/Album/Partials/_AlbumReviews.cshtml @@ -0,0 +1,92 @@ +@using VocaDb.Model.Domain.Globalization +@using VocaDb.Model.Domain.Security +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using VocaDb.Web.Models.Shared.Partials.User +@using Res = ViewRes.Album.DetailsStrings +@inherits VocaDb.Web.Code.VocaDbPage + + + +@if (UserContext.HasPermission(PermissionToken.CreateComments)) { + +} else { +
@Res.ReviewLoginToReview
+} + +
+
+ + @Html.Partial("Partials/Knockout/_DropdownList", new DropdownListViewModel(InterfaceLanguage.UserLanguageCultures.ToDictionaryFull("Choose"), "languageCode", cssClass: "input-xlarge", required: true)) + +
+ @Res.ReviewAlreadySubmitted +
+ + + + + + @Html.Partial("Partials/Shared/_MarkdownNotice")
+ + +
+ +
+
+
+ + +
+ +
+
+ @Html.Partial("Partials/User/_IconAndLinkKnockout", new IconAndLinkKnockoutViewModel("user", "pull-left")) + +
+
+ {{languageCode}} + | + + + +    + @ViewRes.SharedStrings.Edit + + + +    + @ViewRes.SharedStrings.Delete + +
+

+ @Html.Partial("Partials/User/_NameLinkKnockout", new NameLinkKnockoutViewModel("user")) +

+ + + + + +
+

{{title}}

+

+
+
+ + +
+ + + +
+ + +
+ + +
+
+
diff --git a/VocaDbWeb.Core/Views/Album/Partials/_ArtistForAlbumEdit.cshtml b/VocaDbWeb.Core/Views/Album/Partials/_ArtistForAlbumEdit.cshtml new file mode 100644 index 0000000000..f9349b96e2 --- /dev/null +++ b/VocaDbWeb.Core/Views/Album/Partials/_ArtistForAlbumEdit.cshtml @@ -0,0 +1,38 @@ +@* Binding context: ArtistForAlbumEditViewModel *@ + + + +
+ +
+ +
+
+ +
+ @if (VocaDb.Model.Utils.AppConfig.AllowCustomArtistName) { + @ViewRes.Album.ArtistForAlbumEditRowStrings.Customize + } + + + + + +
+
+ +
+
+
+ + + + + @ViewRes.SharedStrings.Remove + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Album/Partials/_DownloadTagsDialog.cshtml b/VocaDbWeb.Core/Views/Album/Partials/_DownloadTagsDialog.cshtml new file mode 100644 index 0000000000..8fb517453d --- /dev/null +++ b/VocaDbWeb.Core/Views/Album/Partials/_DownloadTagsDialog.cshtml @@ -0,0 +1,21 @@ +@using Res = ViewRes.Album.DetailsStrings + +
+ +
+ +
+ + +
+
+ +
\ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Album/Partials/_SongInAlbumEdit.cshtml b/VocaDbWeb.Core/Views/Album/Partials/_SongInAlbumEdit.cshtml new file mode 100644 index 0000000000..2c48f553cb --- /dev/null +++ b/VocaDbWeb.Core/Views/Album/Partials/_SongInAlbumEdit.cshtml @@ -0,0 +1,32 @@ +@* + Binding context: SongInAlbumEditViewModel. +*@ + + + + + + + + + + + + + + + + + +
+ + + + @ViewRes.Album.EditStrings.TrNextDisc + + + @ViewRes.SharedStrings.Edit + + @ViewRes.SharedStrings.Remove + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Album/Partials/_TrackArtistSelection.cshtml b/VocaDbWeb.Core/Views/Album/Partials/_TrackArtistSelection.cshtml new file mode 100644 index 0000000000..085a6287a0 --- /dev/null +++ b/VocaDbWeb.Core/Views/Album/Partials/_TrackArtistSelection.cshtml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Album/Partials/_TrackProperties.cshtml b/VocaDbWeb.Core/Views/Album/Partials/_TrackProperties.cshtml new file mode 100644 index 0000000000..7752bfb960 --- /dev/null +++ b/VocaDbWeb.Core/Views/Album/Partials/_TrackProperties.cshtml @@ -0,0 +1,35 @@ + + +
+ +
+

+
+ + +
Selected artists (click to remove)
+
+
+ + +
+
+ No artists selected.
+
+ +
Album artists (click to add)
+
+ Filter + +
+
+ +
+
+ +
+
+ No selectable artists. +
\ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Album/RelatedAlbums.cshtml b/VocaDbWeb.Core/Views/Album/RelatedAlbums.cshtml new file mode 100644 index 0000000000..b72dfbe4ad --- /dev/null +++ b/VocaDbWeb.Core/Views/Album/RelatedAlbums.cshtml @@ -0,0 +1,23 @@ +@using VocaDb.Web.Models.Shared.Partials.Album +@model VocaDb.Model.DataContracts.Albums.RelatedAlbumsContract +@using R = ViewRes.Album.DetailsStrings + +@if (!Model.Any) { + @ViewRes.EntryDetailsStrings.NoMatches +} + +@if (Model.ArtistMatches.Any()) { +

@R.MatchingArtists

+ @Html.Partial("Partials/Album/_AlbumGrid", new AlbumGridViewModel(Model.ArtistMatches, 2, true, false, true)) +
+} + +@if (Model.LikeMatches.Any()) { +

@R.MatchingLikes

+ @Html.Partial("Partials/Album/_AlbumGrid", new AlbumGridViewModel(Model.LikeMatches, 2, true, false, true)) +} + +@if (Model.TagMatches.Any()) { +

@R.MatchingTags

+ @Html.Partial("Partials/Album/_AlbumGrid", new AlbumGridViewModel(Model.TagMatches, 2, true, false, true)) +} diff --git a/VocaDbWeb.Core/Views/Album/UsersWithAlbumInCollection.cshtml b/VocaDbWeb.Core/Views/Album/UsersWithAlbumInCollection.cshtml new file mode 100644 index 0000000000..3b9e4b0b7b --- /dev/null +++ b/VocaDbWeb.Core/Views/Album/UsersWithAlbumInCollection.cshtml @@ -0,0 +1,36 @@ +@using Microsoft.AspNetCore.Html +@using VocaDb.Model.DataContracts.Users +@using VocaDb.Model.Domain.Users +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.User +@model AlbumForUserContract[] + +@{ + void UserGrid(IEnumerable users) { + @Html.Grid(users, 2, u => new HtmlString(Html.Partial("Partials/User/_UserIconLink_UserContract", new UserIconLink_UserContractViewModel(u.User)).ToHtmlString() + " (" + Translate.AlbumMediaTypeNames[u.MediaType] + ")")) + } +} + +@{ + + var owned = Model.Where(u => u.User != null && u.PurchaseStatus == PurchaseStatus.Owned).ToArray(); + var wishlisted = Model.Where(u => u.User != null && u.PurchaseStatus == PurchaseStatus.Wishlisted).ToArray(); + var hiddenOwnerCount = Model.Count(u => u.PurchaseStatus == PurchaseStatus.Owned && u.User == null); + +} + +@if (owned.Any()) { +

@ViewRes.Album.DetailsStrings.OwnedByTitle

+ + UserGrid(owned); + + if (hiddenOwnerCount > 0) { +

@string.Format(ViewRes.Album.DetailsStrings.HiddenCollections, hiddenOwnerCount)

+ } +} + +@if (wishlisted.Any()) { +

@ViewRes.Album.DetailsStrings.WishlistedByTitle

+ + UserGrid(wishlisted); +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Album/Versions.cshtml b/VocaDbWeb.Core/Views/Album/Versions.cshtml new file mode 100644 index 0000000000..d1070f9aad --- /dev/null +++ b/VocaDbWeb.Core/Views/Album/Versions.cshtml @@ -0,0 +1,16 @@ +@using VocaDb.Web.Code +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@model VocaDb.Web.Models.Album.Versions + +@{ + PageProperties.Title = ViewRes.EntryDetailsStrings.Revisions + " - " + Model.Album.Name; + PageProperties.Robots = PagePropertiesData.Robots_Noindex_Nofollow; + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.Albums, "Index"), + Html.ActionLink(Model.Album.Name, "Details", new { id = Model.Album.Id }) + }; +} + +@Html.Partial("Partials/ArchivedEntry/_CurrentVersionMessage", new CurrentVersionMessageViewModel(Model.Album.Version, Model.Album.Status)) + +@Html.Partial("Partials/ArchivedEntry/_ArchivedObjectVersions", new ArchivedObjectVersionsViewModel(Model.ArchivedVersions, id => Url.Action("ViewVersion", new { id = id }))) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Album/ViewVersion.cshtml b/VocaDbWeb.Core/Views/Album/ViewVersion.cshtml new file mode 100644 index 0000000000..5e8c1262ab --- /dev/null +++ b/VocaDbWeb.Core/Views/Album/ViewVersion.cshtml @@ -0,0 +1,81 @@ +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Album +@using VocaDb.Web.Models.Shared.Partials.Album +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@model VocaDb.Model.DataContracts.Albums.ArchivedAlbumVersionDetailsContract +@inject Login Login + +@{ + + PageProperties.Title = "Revision " + Model.ArchivedVersion.Version + " for " + Model.Name; + PageProperties.Robots = PagePropertiesData.Robots_Noindex_Nofollow; + + if (Model.Album != null) { + ViewBag.Parents = new[] { + Html.ActionLink("Albums", "Index"), + Html.ActionLink(Model.Album.Name, "Details", new { id = Model.Album.Id }), + Html.ActionLink("Revisions", "Versions", new { id = Model.Album.Id }) + }; + } else { + ViewBag.Parents = new[] { + Html.ActionLink("Albums", "Index") + }; + } + +} + +@section Toolbar { +@if (Login.CanRevertEntryVersions && Model.CanBeReverted) { + @Html.ActionLink(ViewRes.ViewVersionStrings.RevertToVersion, "RevertToVersion", new { archivedAlbumVersionId = Model.ArchivedVersion.Id }, new { id = "revertLink", onclick = string.Format("return confirm(\"{0}\");", ViewRes.ViewVersionStrings.ConfirmRevertToVersion) }) +} +  +Download XML +@ViewRes.EntryDetailsStrings.ReportAnError +@if (Login.CanViewHiddenRevisions) { + if (Model.ArchivedVersion.Hidden) { + @Html.ActionLink(ViewRes.ViewVersionStrings.UnhideVersion, "UpdateVersionVisibility", new { archivedVersionId = Model.ArchivedVersion.Id, hidden = false }, new { id = "showLink", onclick = string.Format("return confirm(\"{0}\");", ViewRes.ViewVersionStrings.ConfirmUnhide) }) + } else { + @Html.ActionLink(ViewRes.ViewVersionStrings.HideVersion, "UpdateVersionVisibility", new { archivedVersionId = Model.ArchivedVersion.Id, hidden = true }, new { id = "hideLink", onclick = string.Format("return confirm(\"{0}\");", ViewRes.ViewVersionStrings.ConfirmHide) }) + } +} +} + +@if (Model.ArchivedVersion.Hidden) { + @Html.Partial("Partials/EntryDetails/_HiddenBanner") +} + +@if (Model.ComparableVersions.Any()) { + using (Html.BeginForm("ViewVersion", "Album", FormMethod.Post, new { @class = "form form-inline" })) { + @:Compare to: @Html.DropDownListFor(m => m.ComparedVersionId, ViewHelper.CreateSelectList(Model.ComparableVersions, i => i.Id, i => i.Version + " (" + Versions.GetChangeString(i.ChangedFields) + " by " + i.AgentName + ")", Model.ComparedVersionId), new { @class = "input-xlarge", onchange = "$(this).closest('form').submit();" }) + + } +} + +@Html.Partial("Partials/ArchivedEntry/_ArchivedObjectVersionProperties", new ArchivedObjectVersionPropertiesViewModel(Versions.CreateForAlbum(Model.ArchivedVersion), Model.ComparedVersion != null ? Versions.CreateForAlbum(Model.ComparedVersion) : null)) + +@Html.Partial("Partials/Album/_PrintArchivedAlbumData", new PrintArchivedAlbumDataViewModel(Model.Versions)) + +@Html.Partial("Partials/EntryDetails/_ReportEntryVersionPopupKnockout", new ReportEntryVersionPopupKnockoutViewModel()) + +@section BodyScripts { + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Artist/Create.cshtml b/VocaDbWeb.Core/Views/Artist/Create.cshtml new file mode 100644 index 0000000000..33f0ef5180 --- /dev/null +++ b/VocaDbWeb.Core/Views/Artist/Create.cshtml @@ -0,0 +1,147 @@ +@using VocaDb.Model.Helpers +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Shared +@using R = ViewRes.Artist +@model VocaDb.Web.Models.Artist.Create +@inject LaravelMixHelper LaravelMixHelper + +@{ + PageProperties.Title = R.CreateStrings.AddArtist; + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.Artists, "Index") + }; +} + +
+ + @Html.Partial("Partials/Shared/_ValidationSummaryDiv", new ValidationSummaryDivViewModel(R.CreateStrings.UnableToCreateArtist)) + +
+
+ +
+ @R.CreateStrings.Name + @Html.Partial("Partials/Shared/_RequiredField") +
+ +
+ @Html.ValidationMessage("Names") + + + + + +
+ @Html.LabelFor(m => m.NameOriginal) + + @Html.TextBoxForKnockout(m => m.NameOriginal, "value: nameOriginal, focusout: checkDuplicates", cssClass: "span12", maxLength: 255, size: 40) +
+ @Html.LabelFor(m => m.NameRomaji) + + @Html.TextBoxForKnockout(m => m.NameRomaji, "value: nameRomaji, focusout: checkDuplicates", cssClass: "span12", maxLength: 255, size: 40) +
+ @Html.LabelFor(m => m.NameEnglish) + + @Html.TextBoxForKnockout(m => m.NameEnglish, "value: nameEnglish, focusout: checkDuplicates", cssClass: "span12", maxLength: 255, size: 40) +
+
+ +
+ @Html.LabelFor(m => m.ArtistType) +
+
+ @Html.DropdownForKnockout(m => m.ArtistType, ViewHelper.CreateArtistTypesList(null), "value: artistType") +
+ +
+ @Html.LabelFor(m => m.Description) +
+
+ @Html.TextAreaFor(m => m.Description, 7, 70, new { @class = "span12" })
+
+ +
+ @BrandableStrings.Artist.NewArtistExternalLink +
+
+ + + + + + + +
+ @R.CreateStrings.WebLinkURL + @Html.Partial("Partials/Shared/_RequiredField") + + @Html.TextBoxForKnockout(m => m.WebLinkUrl, "value: webLink.url, focusout: checkDuplicates", "input-xlarge", maxLength: 512) +
+ @R.CreateStrings.WebLinkDescription @R.CreateStrings.Optional + + @Html.TextBoxForKnockout(m => m.WebLinkDescription, "value: webLink.description", "input-xlarge", maxLength: 512) +
+ @R.CreateStrings.WebLinkCategory + + @Html.DropdownForKnockout(m => m.WebLinkCategory, ViewHelper.CreateEnumList(Model.WebLinkCategory, Translate.WebLinkCategoryNames), "value: webLink.category") +
+
+ +
+ @R.CreateStrings.Picture +
+
+

@string.Format(ViewRes.EntryCreateStrings.PictureInfo, string.Join(", ", ImageHelper.AllowedExtensions), ImageHelper.MaxImageSizeMB)

+ +
+ +
+

+ +

+ +
+ + +
+
+
+ @R.CreateStrings.ArtistInfo +
+
+

@R.CreateStrings.NameHelp

+
+ + @{ Html.RenderPartial("KnockoutPartials/_DuplicateEntriesMessage"); } + +
+

+ {{artistTypeName}} +

+ +
+
+ +
+ +
+ +@section BodyScripts { + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Artist/Details.cshtml b/VocaDbWeb.Core/Views/Artist/Details.cshtml new file mode 100644 index 0000000000..4a4fd963fc --- /dev/null +++ b/VocaDbWeb.Core/Views/Artist/Details.cshtml @@ -0,0 +1,493 @@ +@using Microsoft.AspNetCore.Html +@using VocaDb.Model.DataContracts.Artists +@using VocaDb.Model.Domain +@using VocaDb.Model.Domain.Artists +@using VocaDb.Model.Domain.Images +@using VocaDb.Model.Service +@using VocaDb.Web.Helpers +@using VocaDb.Model.Domain.Security +@using VocaDb.Model.Service.QueryableExtensions +@using VocaDb.Web.Code +@using VocaDb.Web.Models.Shared +@using VocaDb.Web.Models.Shared.Partials.Album +@using VocaDb.Web.Models.Shared.Partials.Artist +@using VocaDb.Web.Models.Shared.Partials.Comment +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@using VocaDb.Web.Models.Shared.Partials.Html +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using VocaDb.Web.Models.Shared.Partials.Shared +@using VocaDb.Web.Models.Shared.Partials.Song +@using VocaDb.Web.Models.Shared.Partials.User +@using R = ViewRes.Artist +@using Res = ViewRes.Artist.DetailsStrings +@model ArtistDetailsContract +@inject LaravelMixHelper LaravelMixHelper +@inject Login Login + +@{ + ViewBag.Parents = new[] { Html.ActionLink(ViewRes.SharedStrings.Artists, "Index") }; + var url = PageProperties.CanonicalUrl; + + PageProperties.Robots = Model.Deleted ? PagePropertiesData.Robots_Noindex_Follow : string.Empty; +} + +@section Head { + @Html.Partial("Partials/Html/_OpenGraphMetaTags", new OpenGraphMetaTagsViewModel(PageProperties)) + + +} + +@section Toolbar { + @ViewRes.Artist.DetailsStrings.Follow + + @ViewRes.Artist.DetailsStrings.Unfollow + @R.DetailsStrings.Customize + + + @Html.ActionLink(ViewRes.SharedStrings.Edit, "Edit", new { id = Model.Id }, new { id = "editArtistLink", @class = (Login.CanEdit(Model) ? "" : "disabled") }) + + @Html.ActionLink(ViewRes.EntryDetailsStrings.ViewModifications, "Versions", new { id = Model.Id }, new { id = "viewVersions" }) + + @ViewRes.EntryDetailsStrings.ReportAnError + + @Html.Partial("Partials/Shared/_EntryStatusMessage", new EntryStatusMessageViewModel(Model.Status)) +} + +@{ + void DataRow(string label, IHtmlContent content) { + if (content != null) { + + @label + @content + + } + } + + void ArtistListRow(string label, ArtistContract[] artists, bool typeLabel, bool releaseYear = false) { + DataRow(label, artists.Any() ? Html.Partial("Partials/Artist/_ArtistLinkList", new ArtistLinkListViewModel(artists, typeLabel: typeLabel, releaseYear: releaseYear)) : null); + } + + void ArtistRow(string label, ArtistContract artist, bool typeLabel) { + DataRow(label, artist != null ? Html.Partial("Partials/Artist/_ArtistLink", new ArtistLinkViewModel(artist, typeLabel: typeLabel)) : null); + } +} + +@if (Model.Deleted) { + @Html.Partial("Partials/EntryDetails/_DeletedBanner", new DeletedBannerViewModel(Model.MergedTo)) +} + +@if (Model.Draft && !Model.Deleted) { + @Html.Partial("Partials/Shared/_DraftMessage", new DraftMessageViewModel("glproducers")) +} + +
+ + + +
+ +
+ +
+ + @ViewRes.Artist.DetailsStrings.ArtistPicture + +
+ + + + + + + @if (!Model.Description.IsEmpty) { + + + + + } + + @if (Model.ReleaseDate.HasValue) { + + + + + } + + @{ ArtistListRow(Res.Illustrator, Model.Illustrators, false); } + @{ ArtistListRow(Res.IllustratorOf, Model.IllustratorOf, true, releaseYear: true); } + @{ ArtistListRow(Res.VoiceProvider, Model.VoiceProviders, false); } + @{ ArtistListRow(Res.VoiceProviderOf, Model.Voicebanks, true, releaseYear: true); } + @{ ArtistRow(Res.ManagedBy, Model.Manager, false); } + @{ ArtistRow(Res.CharacterDesigner, Model.CharacterDesigner, false); } + @{ ArtistListRow(Res.CharacterDesignerOf, Model.CharacterDesignerOf, typeLabel: true); } + + + + + + + + + + + + @Html.Partial("Partials/EntryDetails/_ExternalLinksRows", new ExternalLinksRowsViewModel(Model.WebLinks)) + + @if (Model.OwnerUsers.Any()) { + + + + + } + + @if (Model.BaseVoicebank != null) { + + + + + } + + @if (Model.ChildVoicebanks.Any()) { + + + + + } + + @if (Model.Groups.Any()) { + + + + + } + + + + + + + + + + + +
@ViewRes.SharedStrings.ArtistName + @Model.Name
+ @Model.AdditionalNames +
@ViewRes.SharedStrings.Description + @{ Html.RenderPartial("Partials/_EnglishTranslatedString", new EnglishTranslatedStringViewModel(Model.Description)); } +
@Res.ReleaseDate + @Model.ReleaseDate.Value.ToShortDateString() +
@ViewRes.SharedStrings.Type + @Html.Partial("Partials/Artist/_ArtistTypeLabel", new ArtistTypeLabelViewModel(Model.ArtistType)) + @if (Model.ArtistTypeTag != null) { + @Translate.ArtistTypeName(Model.ArtistType) + } else { + @Translate.ArtistTypeName(Model.ArtistType) + } +
@ViewRes.SharedStrings.Tags + + @ViewRes.EntryDetailsStrings.EditTags + @if (Model.CanRemoveTagUsages) { + @Html.ActionLink(ViewRes.EntryDetailsStrings.ManageTags, "ManageTagUsages", new { id = Model.Id }, new { id = "manageTags" }) + } +
+ @BrandableStrings.Artist.AuthoredBy + + @foreach (var user in Model.OwnerUsers) { + @Html.Partial("Partials/User/_UserIconLink_UserContract", new UserIconLink_UserContractViewModel(user, tooltip: true)) +
+ } +
+ + @Res.BaseVoicebank + + + @Html.Partial("Partials/Artist/_ArtistLink", new ArtistLinkViewModel(Model.BaseVoicebank, releaseYear: true)) +
+ + @Res.ChildVoicebanks + + + @foreach (var a in Model.ChildVoicebanks) { + @Html.Partial("Partials/Artist/_ArtistLink", new ArtistLinkViewModel(a, releaseYear: true)) + if (a != Model.ChildVoicebanks.Last()) { + @(", ") + } + } +
+ + @Res.Groups + + + @foreach (var grp in Model.Groups) { + @Html.Partial("Partials/Artist/_ArtistLink", new ArtistLinkViewModel(grp)) + if (grp != Model.Groups.Last()) { + @(", ") + } + } +
@ViewRes.EntryDetailsStrings.Stats + @string.Format(Res.FollowCount, Model.SharedStats.FollowerCount) + @if (Model.SharedStats.RatedSongCount > 0) { + @:@string.Format(Res.RatedSongs, Model.SharedStats.RatedSongCount) + } + @if (Model.SharedStats.RatedAlbumCount > 0) { + @:@string.Format(Res.RatedAlbums, Model.SharedStats.RatedAlbumCount) + @:@string.Format(Res.AverageAlbumRating, Model.SharedStats.AlbumRatingAverage) + } + @if (Model.PersonalStats != null && Model.PersonalStats.SongRatingCount > 0) { + @string.Format(Res.YouHaveRatedSongs, Model.PersonalStats.SongRatingCount) + } + @if (Model.AdvancedStats != null && Model.AdvancedStats.TopVocaloids.Any()) { +

+ @Res.MostlyUses @Html.Partial("Partials/Artist/_ArtistLinkList", new ArtistLinkListViewModel(Model.AdvancedStats.TopVocaloids.Select(a => a.Data), typeLabel: true)). +

+ } +
@ViewRes.EntryDetailsStrings.AdditionDate + @Html.Partial("Partials/Shared/_UniversalTimeLabel", new UniversalTimeLabelViewModel(Model.CreateDate)) +
+
+ + @if (Model.Members.Any()) { +

+ @Res.Members + (@string.Format(ViewRes.EntryDetailsStrings.NumTotal, Model.Members.Length)) +

+
+ @Html.Partial("Partials/Artist/_ArtistGrid", new ArtistGridViewModel(Model.Members.Take(6).Select(g => g), 3, true)) +
+ if (Model.Members.Length > 6) { +
+ @Html.Partial("Partials/Artist/_ArtistGrid", new ArtistGridViewModel(Model.Members.Select(g => g), 3, true)) +
+ @ViewRes.SharedStrings.ShowMore + } + } + + @if (Model.LatestAlbums.Any()) { +

+ @Html.ActionLink(Res.RecentAlbums, "Index", "Search", new { searchType = EntryType.Album, artistId = Model.Id, sort = AlbumSortRule.AdditionDate }, null) + @string.Format(ViewRes.EntryDetailsStrings.NumTotalParenthesis, Model.SharedStats.AlbumCount) +

+
+ @Html.Partial("Partials/Album/_AlbumThumbs", new AlbumThumbsViewModel(Model.LatestAlbums)) +
+ } + + @if (Model.TopAlbums.Any()) { +

+ @Html.ActionLink(Res.TopAlbums, "Index", "Search", new { searchType = EntryType.Album, artistId = Model.Id, sort = AlbumSortRule.RatingTotal }, null) + (@string.Format(Res.RatedAlbums, Model.SharedStats.RatedAlbumCount)) +

+
+ @Html.Partial("Partials/Album/_AlbumThumbs", new AlbumThumbsViewModel(Model.TopAlbums)) +
+ } + + + @if (Model.LatestSongs.Any()) { +
+

+ @Html.ActionLink(Res.RecentSongs, "Index", "Search", UrlMapper.Search.Songs(artistId: Model.Id, sort: SongSortRule.PublishDate), null) + (@string.Format(ViewRes.EntryDetailsStrings.NumTotal, Model.SharedStats.SongCount)) +

+ @Html.Partial("Partials/Song/_SongGrid", new SongGridViewModel(Model.LatestSongs, 2, true, true)) + } + + @if (Model.TopSongs.Any()) { +
+

+ @Html.ActionLink(Res.TopSongs, "Index", "Search", UrlMapper.Search.Songs(artistId: Model.Id, sort: SongSortRule.RatingScore), null) + (@string.Format(Res.RatedSongsTotal, Model.SharedStats.RatedSongCount)) +

+ @Html.Partial("Partials/Song/_SongGrid", new SongGridViewModel(Model.TopSongs, 2, true, true)) + } + + @if (Model.LatestEvents.Any()) { +

+ @Html.ActionLink(Res.RecentEvents, "Index", "Search", UrlMapper.Search.Events(artistId: Model.Id, sort: EventSortRule.Date), null) + (@string.Format(ViewRes.EntryDetailsStrings.NumTotal, Model.SharedStats.EventCount)) +

+ @Html.Partial("Partials/Shared/_EventThumbs", new EventThumbsViewModel(Model.LatestEvents)) + } + +
+

@Res.SongsPerMonth

+
+
+ + @Html.Partial("Partials/Comment/_LatestCommentsKnockout") + +

+ @ViewRes.EntryDetailsStrings.ViewAllComments +

+ +
+ +
+ @Html.Partial("Partials/Comment/_EditableComments", new EditableCommentsViewModel(UserContext.HasPermission(PermissionToken.CreateComments), well: false)) +
+ +
+ +
    + @Html.Partial("Partials/Shared/_ThumbItem", new ThumbItemViewModel(Url.Action("Picture", "Artist", new { id = Model.Id }), Url.Action("PictureThumb", "Artist", new { id = Model.Id }), ViewRes.Album.DetailsStrings.CoverPicture)) + @foreach (var pic in Model.Pictures) { + @Html.Partial("Partials/Shared/_ThumbItem", new ThumbItemViewModel(Url.ImageThumb(pic, ImageSize.Original), Url.ImageThumb(pic, ImageSize.Thumb), pic.Name)) + } +
+ +
+ +@{ + void AlbumOptions(string viewModel) { +
+
+ @Html.Partial("Partials/Knockout/_SearchDropDown", new SearchDropDownViewModel("true", viewModel, Translate.AlbumSortRuleNames.ValuesAndNamesStrings)) + +
+
+ } +} + +
+ @{ AlbumOptions("mainAlbumsViewModel"); } +
+ @{ Html.RenderPartial("Partials/_AlbumSearchList"); } +
+
+ +
+ @{ AlbumOptions("collaborationAlbumsViewModel"); } +
+ @{ Html.RenderPartial("Partials/_AlbumSearchList"); } +
+
+ +
+
+
+ @Html.Partial("Partials/Knockout/_SearchDropDown", new SearchDropDownViewModel("true", "songsViewModel", Translate.SongSortRuleNames.ValuesAndNamesStrings)) + +
+
+
+ @{ Html.RenderPartial("Partials/_SongSearchList"); } +
+
+ +
+
+ @ViewRes.EntryDetailsStrings.Link
+ +
+
+ Markdown
+ +
+
+ +
+ +
+ @{ Html.RenderPartial("Partials/_TagsEdit"); } +
+ +@Html.Partial("Partials/EntryDetails/_ReportEntryPopupKnockout", new ReportEntryPopupKnockoutViewModel()) + +@if (Login.IsLoggedIn) { + Html.RenderPartial("Partials/_CustomizeArtistSubscriptionDialog"); +} + +@section BodyScripts { + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Artist/ManageTagUsages.cshtml b/VocaDbWeb.Core/Views/Artist/ManageTagUsages.cshtml new file mode 100644 index 0000000000..65d1d8dfba --- /dev/null +++ b/VocaDbWeb.Core/Views/Artist/ManageTagUsages.cshtml @@ -0,0 +1,14 @@ +@using VocaDb.Model.Domain +@using VocaDb.Web.Models.Shared.Partials.Tag +@model VocaDb.Model.DataContracts.UseCases.EntryWithTagUsagesContract + +@{ + PageProperties.Title = "Manage tag usages - " + Model.DefaultName; + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.Artists, "Index"), + Html.ActionLink(Model.DefaultName, "Details", new { id = Model.Id }) + }; +} + + +@Html.Partial("Partials/Tag/_TagUsagesManageTable", new TagUsagesManageTableViewModel(EntryType.Artist, Model.TagUsages, Model.CanRemoveTagUsages)) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Artist/Merge.cshtml b/VocaDbWeb.Core/Views/Artist/Merge.cshtml new file mode 100644 index 0000000000..448781469b --- /dev/null +++ b/VocaDbWeb.Core/Views/Artist/Merge.cshtml @@ -0,0 +1,65 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Knockout +@model VocaDb.Model.DataContracts.Artists.ArtistContract +@inject LaravelMixHelper LaravelMixHelper + +@{ + PageProperties.Title = "Merge artist - " + Model.Name; + ViewBag.Parents = new[] { + Html.ActionLink("Artists", "Index"), + Html.ActionLink(Model.Name, "Details", new { id = Model.Id }), + Html.ActionLink("Edit", "Edit", new { id = Model.Id }), + }; +} + +@using (Html.BeginForm()) +{ + + @Html.Partial("Partials/Shared/_MergeEntryInfo") + + + +
+
+
+ @Html.Partial("Partials/Knockout/_LockingAutoComplete", new LockingAutoCompleteViewModel("artistAutoComplete", "targetSearchParams", "target.name", "target.id")) +
+
+ + + +
+
+ +
+ + + +} + +@section BodyScripts { + + + +} diff --git a/VocaDbWeb.Core/Views/Artist/Partials/_CustomizeArtistSubscriptionDialog.cshtml b/VocaDbWeb.Core/Views/Artist/Partials/_CustomizeArtistSubscriptionDialog.cshtml new file mode 100644 index 0000000000..4340ef28c2 --- /dev/null +++ b/VocaDbWeb.Core/Views/Artist/Partials/_CustomizeArtistSubscriptionDialog.cshtml @@ -0,0 +1,28 @@ +@using Res = ViewRes.Artist.DetailsStrings + +
+ +
+

@Res.SubscriptionOptions

+ + + + +
+ +
\ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Artist/ViewVersion.cshtml b/VocaDbWeb.Core/Views/Artist/ViewVersion.cshtml new file mode 100644 index 0000000000..819005687c --- /dev/null +++ b/VocaDbWeb.Core/Views/Artist/ViewVersion.cshtml @@ -0,0 +1,74 @@ +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Artist +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@using VocaDb.Web.Models.Shared.Partials.Artist +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@model VocaDb.Model.DataContracts.Artists.ArchivedArtistVersionDetailsContract +@inject Login Login + +@{ + + PageProperties.Title = "Revision " + Model.ArchivedVersion.Version + " for " + Model.Name; + PageProperties.Robots = PagePropertiesData.Robots_Noindex_Nofollow; + ViewBag.Parents = new[] { + Html.ActionLink("Artist", "Index"), + Html.ActionLink(Model.Artist.Name, "Details", new { id = Model.Artist.Id }), + Html.ActionLink("Revisions", "Versions", new { id = Model.Artist.Id }) + }; + +} + +@section Toolbar { +@if (Login.CanRevertEntryVersions && Model.CanBeReverted) { + @Html.ActionLink(ViewRes.ViewVersionStrings.RevertToVersion, "RevertToVersion", new { archivedArtistVersionId = Model.ArchivedVersion.Id }, new { id = "revertLink", onclick = string.Format("return confirm(\"{0}\");", ViewRes.ViewVersionStrings.ConfirmRevertToVersion) }) +} +  +Download XML +@ViewRes.EntryDetailsStrings.ReportAnError +@if (Login.CanViewHiddenRevisions) { + if (Model.ArchivedVersion.Hidden) { + @Html.ActionLink(ViewRes.ViewVersionStrings.UnhideVersion, "UpdateVersionVisibility", new { archivedVersionId = Model.ArchivedVersion.Id, hidden = false }, new { id = "showLink", onclick = string.Format("return confirm(\"{0}\");", ViewRes.ViewVersionStrings.ConfirmUnhide) }) + } else { + @Html.ActionLink(ViewRes.ViewVersionStrings.HideVersion, "UpdateVersionVisibility", new { archivedVersionId = Model.ArchivedVersion.Id, hidden = true }, new { id = "hideLink", onclick = string.Format("return confirm(\"{0}\");", ViewRes.ViewVersionStrings.ConfirmHide) }) + } +} +} + +@if (Model.ArchivedVersion.Hidden) { + @Html.Partial("Partials/EntryDetails/_HiddenBanner") +} + +@if (Model.ComparableVersions.Any()) { + using (Html.BeginForm("ViewVersion", "Artist", FormMethod.Post, new { @class = "form form-inline" })) { + @:Compare to: @Html.DropDownListFor(m => m.ComparedVersionId, ViewHelper.CreateSelectList(Model.ComparableVersions, i => i.Id, i => i.Version + " (" + Versions.GetChangeString(i.ChangedFields) + " by " + i.AgentName + ")", Model.ComparedVersionId), new { @class = "input-xlarge", onchange = "$(this).closest('form').submit();" }) + + } +} + +@Html.Partial("Partials/ArchivedEntry/_ArchivedObjectVersionProperties", new ArchivedObjectVersionPropertiesViewModel(Versions.CreateForArtist(Model.ArchivedVersion), Model.ComparedVersion != null ? Versions.CreateForArtist(Model.ComparedVersion) : null)) + +@Html.Partial("Partials/Artist/_PrintArchivedArtistData", new PrintArchivedArtistDataViewModel(Model.Versions)) + +@Html.Partial("Partials/EntryDetails/_ReportEntryVersionPopupKnockout", new ReportEntryVersionPopupKnockoutViewModel()) + +@section BodyScripts { + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Comment/Index.cshtml b/VocaDbWeb.Core/Views/Comment/Index.cshtml new file mode 100644 index 0000000000..61ea713924 --- /dev/null +++ b/VocaDbWeb.Core/Views/Comment/Index.cshtml @@ -0,0 +1,42 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Comment +@model IEnumerable +@inject Login Login + +@{ + PageProperties.Title = ViewRes.Comment.IndexStrings.RecentComments; +} + + + +@foreach (var entry in Model) { + +
+ +
+ @foreach (var comment in entry.Comments) { + @Html.Partial("Partials/Comment/_CommentBodyLarge", new CommentBodyLargeViewModel(comment, false)) + } +
+ +
+ + @Html.Partial("Partials/Comment/_CommentEntryItem", new CommentEntryItemViewModel(entry.Entry)) + +
+ +
+ +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Discussion/Index.cshtml b/VocaDbWeb.Core/Views/Discussion/Index.cshtml new file mode 100644 index 0000000000..495dd25aa6 --- /dev/null +++ b/VocaDbWeb.Core/Views/Discussion/Index.cshtml @@ -0,0 +1,51 @@ +@using VocaDb.Model.Domain.Security +@using VocaDb.Web.Helpers +@inherits VocaDb.Web.Code.VocaDbPage +@inject LaravelMixHelper LaravelMixHelper + +@{ + PageProperties.Title = "Discussions"; +} + + + +
+ + + + + @{ Html.RenderPartial("Partials/_ViewFolders"); } + + @{ Html.RenderPartial("Partials/_ViewFolder"); } + + @{ Html.RenderPartial("Partials/_ViewTopic"); } +
+ +@section BodyScripts { + + +} diff --git a/VocaDbWeb.Core/Views/Discussion/Partials/_EditTopic.cshtml b/VocaDbWeb.Core/Views/Discussion/Partials/_EditTopic.cshtml new file mode 100644 index 0000000000..a63a639db9 --- /dev/null +++ b/VocaDbWeb.Core/Views/Discussion/Partials/_EditTopic.cshtml @@ -0,0 +1,34 @@ +@inherits VocaDb.Web.Code.VocaDbPage +@using VocaDb.Model.Domain.Security +@using Res = ViewRes.Discussion.IndexStrings + + + + +
+ +
+ + +
+ +
+ +
+
+ + @if (UserContext.HasPermission(PermissionToken.DeleteComments)) { +
+ + +
+

+ +

+ } + +
+ diff --git a/VocaDbWeb.Core/Views/Discussion/Partials/_ViewFolder.cshtml b/VocaDbWeb.Core/Views/Discussion/Partials/_ViewFolder.cshtml new file mode 100644 index 0000000000..c2d6e34389 --- /dev/null +++ b/VocaDbWeb.Core/Views/Discussion/Partials/_ViewFolder.cshtml @@ -0,0 +1,80 @@ +@using VocaDb.Model.Domain.Images +@using VocaDb.Model.Domain.Security +@using VocaDb.Web.Models.Shared.Partials.User +@using Res = ViewRes.Discussion.IndexStrings +@inherits VocaDb.Web.Code.VocaDbPage + +
+ + + +

+ {{selectedFolder() ? selectedFolder().description : ''}} +

+ + @if (UserContext.HasPermission(PermissionToken.CreateComments)) { + + +
+
+ @{ Html.RenderPartial("Partials/_EditTopic"); } +
+ +
+ } + +
+ @Html.Partial("Partials/Knockout/_ServerSidePaging") +
+ + + + + + + + + + + + + + + + + + + + +
@Res.Topic@Res.Author@Res.Comments@Res.Created@Res.LastComment
+ {{name}} + + + @Html.Partial("Partials/User/_ProfileIconKnockout_ImageSize", new ProfileIconKnockout_ImageSizeViewModel(ImageSize.TinyThumb, size: 18)) + {{name}} + + + {{commentCount}} + + {{created | formatDate:'lll'}} + + + {{lastComment.created | formatDate:'lll'}} by {{lastComment.authorName}} + +
+ +
+ @Html.Partial("Partials/Knockout/_ServerSidePaging") +
+ +
+ diff --git a/VocaDbWeb.Core/Views/Discussion/Partials/_ViewFolders.cshtml b/VocaDbWeb.Core/Views/Discussion/Partials/_ViewFolders.cshtml new file mode 100644 index 0000000000..849ea9ad2e --- /dev/null +++ b/VocaDbWeb.Core/Views/Discussion/Partials/_ViewFolders.cshtml @@ -0,0 +1,45 @@ +@using Res = ViewRes.Discussion.IndexStrings + +
+ +
+ + + + + + + + + + + + + + + +
@Res.Folder@Res.Topics@Res.LastTopic
+ {{name}} + {{description}} + + {{topicCount}} + + {{$data.lastTopicDate | formatDate:'lll'}} by {{$data.lastTopicAuthor != null ? lastTopicAuthor.name : ''}} +
+
+ +
+

@Res.RecentTopics

+ + + + + + +
+ {{name}}
+ {{created | formatDate:'lll'}} by {{author.name}} +
+
+ +
\ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Discussion/Partials/_ViewTopic.cshtml b/VocaDbWeb.Core/Views/Discussion/Partials/_ViewTopic.cshtml new file mode 100644 index 0000000000..9795c902c6 --- /dev/null +++ b/VocaDbWeb.Core/Views/Discussion/Partials/_ViewTopic.cshtml @@ -0,0 +1,35 @@ +@using VocaDb.Model.Domain.Security +@using VocaDb.Web.Models.Shared.Partials.Comment +@inherits VocaDb.Web.Code.VocaDbPage + +
+ +
+ +
+

+ +
+ @Html.Partial("Partials/Comment/_CommentKnockout", new CommentKnockoutViewModel("content", true, "$parents[1].deleteTopic", "$parent.beginEditTopic")) +
+ +
+ +
+ +
+
+ @{ Html.RenderPartial("Partials/_EditTopic"); } +
+ + +
+ +
+ @Html.Partial("Partials/Comment/_EditableComments", new EditableCommentsViewModel(UserContext.HasPermission(PermissionToken.CreateComments), commentBoxEnd: true)) +
+
+ Comments have been disabled for this topic. +
+ +
diff --git a/VocaDbWeb.Core/Views/Error/BadRequest.cshtml b/VocaDbWeb.Core/Views/Error/BadRequest.cshtml new file mode 100644 index 0000000000..02ed80cb6e --- /dev/null +++ b/VocaDbWeb.Core/Views/Error/BadRequest.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = "400 - Bad Request"; + ViewBag.Subtitle = "Sorry, the server rejected your input."; +} + +

+ The most common reason for this error is following a link with invalid characters. We're sorry for the inconvenience. +

\ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Error/Forbidden.cshtml b/VocaDbWeb.Core/Views/Error/Forbidden.cshtml new file mode 100644 index 0000000000..b66a40cc0c --- /dev/null +++ b/VocaDbWeb.Core/Views/Error/Forbidden.cshtml @@ -0,0 +1,8 @@ +@{ + ViewBag.Title = "403 - Forbidden"; + ViewBag.Subtitle = "Sorry, you don't have access to this page/resource"; +} + +

+ This error might be caused by input that was rejected by the system. We're sorry for the inconvenience. +

\ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Error/IPForbidden.cshtml b/VocaDbWeb.Core/Views/Error/IPForbidden.cshtml new file mode 100644 index 0000000000..df13668f9d --- /dev/null +++ b/VocaDbWeb.Core/Views/Error/IPForbidden.cshtml @@ -0,0 +1,12 @@ +@{ + ViewBag.Title = "403 Forbidden"; + ViewBag.Subtitle = "Sorry, you don't have access to this page/resource"; +} + +

403 Forbidden

+ +

+ Sorry, access from your host is restricted. It is possible this restriction is no longer valid. + If you think this is the case, please contact support. + We're sorry for the inconvenience. +

\ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Error/NotFound.cshtml b/VocaDbWeb.Core/Views/Error/NotFound.cshtml new file mode 100644 index 0000000000..008b91ea68 --- /dev/null +++ b/VocaDbWeb.Core/Views/Error/NotFound.cshtml @@ -0,0 +1,11 @@ +@{ + ViewBag.Title = "404 - Not found"; + ViewBag.Subtitle = "Sorry, the page/resource you were looking for was not found"; +} + + +

+ +

+ @Html.ActionLink("Video entry", "Details", "Song", new { id = 18486 }, null)
+

diff --git a/VocaDbWeb.Core/Views/Error/_Layout.cshtml b/VocaDbWeb.Core/Views/Error/_Layout.cshtml new file mode 100644 index 0000000000..9a2dcab67c --- /dev/null +++ b/VocaDbWeb.Core/Views/Error/_Layout.cshtml @@ -0,0 +1,10 @@ + + + + + @(!string.IsNullOrEmpty(ViewBag.Title) ? ViewBag.Title : "Error") + + + @RenderBody() + + diff --git a/VocaDbWeb.Core/Views/Event/Details.cshtml b/VocaDbWeb.Core/Views/Event/Details.cshtml new file mode 100644 index 0000000000..ac104753ea --- /dev/null +++ b/VocaDbWeb.Core/Views/Event/Details.cshtml @@ -0,0 +1,283 @@ +@using VocaDb.Model.DataContracts.ReleaseEvents +@using VocaDb.Model.Domain.Images +@using VocaDb.Model.Domain.ReleaseEvents +@using VocaDb.Model.Domain.Security +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Album +@using VocaDb.Web.Models.Shared.Partials.Artist +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@using VocaDb.Web.Models.Shared.Partials.Html +@using VocaDb.Web.Models.Shared.Partials.PV +@using VocaDb.Web.Models.Shared.Partials.Shared +@using VocaDb.Web.Models.Shared.Partials.Song +@using VocaDb.Web.Models.Shared.Partials.User +@using Res = ViewRes.Event.DetailsStrings +@using EntryRes = ViewRes.EntryDetailsStrings +@model VocaDb.Model.DataContracts.ReleaseEvents.ReleaseEventDetailsContract +@inject LaravelMixHelper LaravelMixHelper +@inject Login Login +@inject PVHelper PVHelper + +@{ + + if (Model.Series == null) { + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.ReleaseEvents, "Index") + }; + } else { + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.ReleaseEvents, "Index"), + Html.ActionLink(Model.Series.Name, "SeriesDetails", new { id = Model.Series.Id, slug = Model.Series.UrlSlug }) + }; + } + + var descriptionHtml = MarkdownParser.GetHtml(Model.Description); + var descriptionStripped = MarkdownParser.GetPlainText(Model.Description); + + PageProperties.Description = descriptionStripped; + PageProperties.Robots = Model.Deleted ? PagePropertiesData.Robots_Noindex_Follow : string.Empty; + + var pictureData = !string.IsNullOrEmpty(Model.PictureMime) ? (IEntryImageInformation)Model: Model.Series; + var smallThumbUrl = Url.ImageThumb(pictureData, VocaDb.Model.Domain.Images.ImageSize.SmallThumb); + var fullImageUrl = Url.ImageThumb(pictureData, VocaDb.Model.Domain.Images.ImageSize.Original); + + var primaryPV = PVHelper.PrimaryPV(Model.PVs); + +} + +@section Head { + @Html.Partial("Partials/Html/_OpenGraphMetaTags", new OpenGraphMetaTagsViewModel(PageProperties)) + +} + +@section Toolbar { + @Html.ActionLink(ViewRes.SharedStrings.Edit, "Edit", new { id = Model.Id }, new { id = "editEventLink", @class = (Login.CanEdit(Model) ? "" : "disabled") }) + @Html.ActionLink(ViewRes.EntryDetailsStrings.ViewModifications, "Versions", new { id = Model.Id }, new { id = "viewVersions" }) + @ViewRes.EntryDetailsStrings.ReportAnError + @Html.Partial("Partials/Shared/_EntryStatusMessage", new EntryStatusMessageViewModel(Model.Status)) +} + +@{ + void ArtistList(IList artists, bool showRoles = false, bool showType = false) { + foreach (var artist in artists) { + if (artist.Artist != null) { + @Html.Partial("Partials/Artist/_ArtistLink", new ArtistLinkViewModel(artist.Artist, typeLabel: showType)) + } else { + @artist.Name + } + if (showRoles && artist.EffectiveRoles != ArtistEventRoles.Default) { + @Html.Raw(" (" + Translate.ArtistEventRoleNames.GetAllNameNames(artist.EffectiveRoles, ArtistEventRoles.Default) + ")") + } + if (artist != artists.Last()) { + @(", ") + } + } + } +} + + +@if (Model.Deleted) { + @Html.Partial("Partials/EntryDetails/_DeletedBanner", new DeletedBannerViewModel(null)) +} + +
+ +
+ @if (pictureData != null && !string.IsNullOrEmpty(pictureData.Mime)) { + + Thumb + + } +
+

+ @ViewRes.SharedStrings.Name: @Model.Name + @if (!string.IsNullOrEmpty(Model.AdditionalNames)) { + (@Model.AdditionalNames) + } +

+ + @if (Model.Date != null) { +

+ @Res.OccurrenceDate: @Model.Date.Value.ToShortDateString() + @if (Model.EndDate != null && Model.EndDate.Value > Model.Date.Value) { + @:- @Model.EndDate.Value.ToShortDateString() + } +

+ } + + @if (Model.Venue == null && !string.IsNullOrEmpty(Model.VenueName)) { +

@Res.Venue: @Model.VenueName

+ } + + @if (Model.Artists.Any()) { +

Participating artists: @{ ArtistList(Model.Artists, true, true); }

+ } + +

+ @ViewRes.Event.DetailsStrings.Category: + @if (Model.InheritedCategoryTag != null) { + @Translate.ReleaseEventCategoryNames[Model.InheritedCategory] + } else { + @Translate.ReleaseEventCategoryNames[Model.InheritedCategory] + } +

+ + @if (!string.IsNullOrEmpty(Model.Description)) { +

@Html.Raw(descriptionHtml)

+ } + + @if (Model.WebLinks.Any()) { + @Html.Partial("Partials/EntryDetails/_ExternalLinksList", new ExternalLinksListViewModel(Model.WebLinks, false)) +
+ } + +

+

@ViewRes.SharedStrings.Tags:
+ + @ViewRes.EntryDetailsStrings.EditTags + @if (Model.CanRemoveTagUsages) { + @Html.ActionLink(ViewRes.EntryDetailsStrings.ManageTags, "ManageTagUsages", new {id = Model.Id}, new {id = "manageTags"}) + } +

+ +
+
+ +
+ +@if (Model.Series != null) { +

@Res.Series: @Model.Series.Name

+ if (!string.IsNullOrEmpty(Model.Series.Description)) { + @Html.Partial("Partials/Html/_FormatMarkdown", new FormatMarkdownViewModel(Model.Series.Description)) + } + @Html.Partial("Partials/EntryDetails/_ExternalLinksList", new ExternalLinksListViewModel(Model.Series.WebLinks, false)) +} + +@if (Model.SongList != null) { +

@Res.SongList: @Html.ActionLink(Model.SongList.Name, "Details", "SongList", new { id = Model.SongList.Id }, null)

+ if (Model.SongListSongs.Any()) { + + @foreach (var song in Model.SongListSongs) { + + + + + + } +

@song.Order

@Html.Partial("Partials/Song/_SongIconLink", new SongIconLinkViewModel(song.Song)) + @Html.Partial("Partials/Song/_SongLink", new SongLinkViewModel(song.Song)) +
+ @song.Song.ArtistString +
+ } +} + +@if (Model.Venue != null) { +

@Res.Venue: @Model.Venue.Name

+ + if (Model.Venue.Coordinates.HasValue) { + @Html.Partial("Partials/Shared/_EmbedGoogleMaps", new EmbedGoogleMapsViewModel(Model.Venue.Coordinates)) + } + + if (!string.IsNullOrEmpty(Model.Venue.Description)) { + @Html.Partial("Partials/Html/_FormatMarkdown", new FormatMarkdownViewModel(Model.Venue.Description)) + } + @Html.Partial("Partials/EntryDetails/_ExternalLinksList", new ExternalLinksListViewModel(Model.Venue.WebLinks, false)) +} + +@if (Model.Albums.Any()) { +

+ @Res.Albums + @string.Format(EntryRes.NumTotalParenthesis, Model.Albums.Length) +

+ @Html.Partial("Partials/Album/_AlbumGrid", new AlbumGridViewModel(Model.Albums, 2, false, false, true)) +} + +@if (Model.Songs.Any()) { +

+ @Html.ActionLink(Res.Songs, "Index", "Search", UrlMapper.Search.Songs(eventId: Model.Id), null) + @string.Format(EntryRes.NumTotalParenthesis, Model.Songs.Length) +

+ @Html.Partial("Partials/Song/_SongGrid", new SongGridViewModel(Model.Songs, 2, true, false)) +} + +@if (Model.PVs.Any()) { +

@Res.PVs

+ @Html.LinkListHtml(Model.PVs, pv => Html.Partial("Partials/Shared/_FormatPVLink", new FormatPVLinkViewModel(pv, false))) +} + +@if (primaryPV != null) { +
+ @Html.Partial("Partials/PV/_EmbedPV", new EmbedPVViewModel(primaryPV)) +
+} + +

@Res.Attending

+ +@if (Login.IsLoggedIn) { +
+ + +
+
+ + +
+} + +
+
    +
  • + @Html.Partial("Partials/User/_IconAndNameLinkKnockout", new IconAndNameLinkKnockoutViewModel()) +
  • +
+
+ +@Html.Partial("Partials/Comment/_LatestCommentsKnockout") + +
+ @{ Html.RenderPartial("Partials/_TagsEdit"); } +
+ +@Html.Partial("Partials/EntryDetails/_ReportEntryPopupKnockout", new ReportEntryPopupKnockoutViewModel()) + +@section BodyScripts { + + +} diff --git a/VocaDbWeb.Core/Views/Event/EventsByDate.cshtml b/VocaDbWeb.Core/Views/Event/EventsByDate.cshtml new file mode 100644 index 0000000000..b88d00799e --- /dev/null +++ b/VocaDbWeb.Core/Views/Event/EventsByDate.cshtml @@ -0,0 +1,48 @@ +@using Res = ViewRes.Event.EventsBySeriesStrings +@model VocaDb.Model.DataContracts.ReleaseEvents.ReleaseEventContract[] + +@{ + PageProperties.Title = ViewRes.SharedStrings.ReleaseEvents; + + var byYear = Model.Where(e => e.Date != null).GroupBy(e => e.Date.Value.Year).ToArray(); + +} + + + + + @foreach (var year in byYear) { + + + + + + foreach (var ev in year) { + + } + + } +
+

+ @year.Key +

+
+ @ev.Date.Value.ToString("m") + + + @ev.Name + +
\ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Event/EventsBySeries.cshtml b/VocaDbWeb.Core/Views/Event/EventsBySeries.cshtml new file mode 100644 index 0000000000..98f516a29e --- /dev/null +++ b/VocaDbWeb.Core/Views/Event/EventsBySeries.cshtml @@ -0,0 +1,84 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Html +@using Res = ViewRes.Event.EventsBySeriesStrings +@model VocaDb.Model.DataContracts.ReleaseEvents.ReleaseEventSeriesWithEventsContract[] +@inject Login Login + +@{ + PageProperties.Title = ViewRes.SharedStrings.ReleaseEvents; +} + +@section Toolbar { + + + + @if (Login.CanManageDb) { + @Html.ActionLink(Res.CreateEvent, "Edit", null, new { id = "createEventLink" }) + @: + @Html.ActionLink(Res.CreateSeries, "EditSeries", null, new { id = "createSeriesLink" }) + @: + @Html.ActionLink(Res.CreateVenue, "Edit", "Venue", null, new { id = "createVenueLink" }) + } +} + +@foreach (var series in Model) { + +
+ @if (!string.IsNullOrEmpty(series.PictureMime)) { + + Thumb + + } +
+

+ @if (!string.IsNullOrEmpty(series.Name)) { + @Html.ActionLink(series.Name, "SeriesDetails", new { id = series.Id }) + } else { + @ViewRes.SharedStrings.Unsorted + } +

+ + @if (!string.IsNullOrEmpty(series.Description)) { +

@Html.Partial("Partials/Html/_FormatMarkdown", new FormatMarkdownViewModel(series.Description))

+ } +
+
+ +
    + @foreach (var ev in series.Events) { +
  • + @Html.ActionLink(ev.Name, "Details", new { id = ev.Id, slug = ev.UrlSlug }) +
  • + } +
+ + if (Login.CanManageDb) { + @Html.ActionLink(Res.CreateEvent, "Edit", new { seriesId = series.Id != 0 ? (int?)series.Id : null }, new { @class = "textLink addLink" }) + } + +} + +@section BodyScripts { + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Event/EventsByVenue.cshtml b/VocaDbWeb.Core/Views/Event/EventsByVenue.cshtml new file mode 100644 index 0000000000..668eeae6d8 --- /dev/null +++ b/VocaDbWeb.Core/Views/Event/EventsByVenue.cshtml @@ -0,0 +1,79 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Html +@using Res = ViewRes.Event.EventsBySeriesStrings +@model VocaDb.Model.DataContracts.Venues.VenueForApiContract[] +@inject Login Login + +@{ + PageProperties.Title = ViewRes.SharedStrings.ReleaseEvents; +} + +@section Toolbar { + + + + @if (Login.CanManageDb) { + @Html.ActionLink(Res.CreateEvent, "Edit", null, new { id = "createEventLink" }) + @: + @Html.ActionLink(Res.CreateSeries, "EditSeries", null, new { id = "createSeriesLink" }) + @: + @Html.ActionLink(Res.CreateVenue, "Edit", "Venue", null, new { id = "createVenueLink" }) + } +} + +@foreach (var venue in Model) { + +
+
+

+ @if (!string.IsNullOrEmpty(venue.Name)) { + @Html.ActionLink(venue.Name, "Details", "Venue", new { id = venue.Id }, null) + } else { + @ViewRes.SharedStrings.Unsorted + } +

+ + @if (!string.IsNullOrEmpty(venue.Description)) { +

@Html.Partial("Partials/Html/_FormatMarkdown", new FormatMarkdownViewModel(venue.Description))

+ } +
+
+ +
    + @foreach (var ev in venue.Events) { +
  • + @Html.ActionLink(ev.Name, "Details", new { id = ev.Id, slug = ev.UrlSlug }) +
  • + } +
+ + if (Login.CanManageDb) { + @Html.ActionLink(Res.CreateEvent, "Edit", new { venueId = venue.Id != 0 ? (int?)venue.Id : null }, new { @class = "textLink addLink" }) + } + +} + +@section BodyScripts { + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Event/Index.cshtml b/VocaDbWeb.Core/Views/Event/Index.cshtml new file mode 100644 index 0000000000..c292a0d158 --- /dev/null +++ b/VocaDbWeb.Core/Views/Event/Index.cshtml @@ -0,0 +1,56 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Shared +@using rule = VocaDb.Model.Service.QueryableExtensions.EventSortRule +@using Res = ViewRes.Event.EventsBySeriesStrings +@model VocaDb.Model.DataContracts.ReleaseEvents.ReleaseEventForApiContract[] +@inject Login Login + +@{ + PageProperties.Title = ViewRes.SharedStrings.ReleaseEvents; +} + +@{ + void SortDirection(rule rule) { + if (ViewBag.SortRule == rule) { + + } + } +} + +@section Toolbar { + + + + @if (Login.CanManageDb) { + @Html.ActionLink(Res.CreateEvent, "Edit", null, new { id = "createEventLink" }) + @: + @Html.ActionLink(Res.CreateSeries, "EditSeries", null, new { id = "createSeriesLink" }) + @: + @Html.ActionLink(Res.CreateVenue, "Edit", "Venue", null, new { id = "createVenueLink" }) + } +} + +@Html.Partial("Partials/Shared/_EventThumbs", new EventThumbsViewModel(Model)) + +@section BodyScripts { + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Event/ManageTagUsages.cshtml b/VocaDbWeb.Core/Views/Event/ManageTagUsages.cshtml new file mode 100644 index 0000000000..5d8efc8c91 --- /dev/null +++ b/VocaDbWeb.Core/Views/Event/ManageTagUsages.cshtml @@ -0,0 +1,14 @@ +@using VocaDb.Model.Domain +@using VocaDb.Web.Models.Shared.Partials.Tag +@model VocaDb.Model.DataContracts.UseCases.EntryWithTagUsagesContract + +@{ + PageProperties.Title = "Manage tag usages - " + Model.DefaultName; + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.ReleaseEvents, "Index", "Event"), + Html.ActionLink(Model.DefaultName, "Details", new { id = Model.Id }) + }; +} + + +@Html.Partial("Partials/Tag/_TagUsagesManageTable", new TagUsagesManageTableViewModel(EntryType.ReleaseEvent, Model.TagUsages, Model.CanRemoveTagUsages, "Event")) diff --git a/VocaDbWeb.Core/Views/Event/SeriesDetails.cshtml b/VocaDbWeb.Core/Views/Event/SeriesDetails.cshtml new file mode 100644 index 0000000000..697248f753 --- /dev/null +++ b/VocaDbWeb.Core/Views/Event/SeriesDetails.cshtml @@ -0,0 +1,115 @@ +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@using VocaDb.Web.Models.Shared.Partials.Shared +@using VocaDb.Web.Resources.Views.Event +@model VocaDb.Model.DataContracts.ReleaseEvents.ReleaseEventSeriesDetailsContract +@inject LaravelMixHelper LaravelMixHelper +@inject Login Login + +@{ + + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.ReleaseEvents, "Index") + }; + + var descriptionHtml = MarkdownParser.GetHtml(Model.Description); + var descriptionStripped = MarkdownParser.GetPlainText(Model.Description); + + PageProperties.Description = descriptionStripped; + PageProperties.Robots = Model.Deleted ? PagePropertiesData.Robots_Noindex_Follow : string.Empty; + + var smallThumbUrl = Url.ImageThumb(Model, VocaDb.Model.Domain.Images.ImageSize.SmallThumb); + var fullImageUrl = Url.ImageThumb(Model, VocaDb.Model.Domain.Images.ImageSize.Original); + +} + +@section Toolbar { + @if (Login.CanManageDb && Login.CanEdit(Model)) { + @Html.ActionLink(ViewRes.SharedStrings.Edit, "EditSeries", new { id = Model.Id }, new { id = "editEventLink" }) + @: + @Html.ActionLink(ViewRes.EntryDetailsStrings.ViewModifications, "SeriesVersions", new { id = Model.Id }, new { id = "viewVersions" }) + } + @if (Login.CanManageDb) { + @ViewRes.Event.EventsBySeriesStrings.CreateEvent + } + @Html.Partial("Partials/Shared/_EntryStatusMessage", new EntryStatusMessageViewModel(Model.Status)) +} + +@if (Model.Deleted) { + @Html.Partial("Partials/EntryDetails/_DeletedBanner", new DeletedBannerViewModel(null)) +} + +
+ @if (!string.IsNullOrEmpty(Model.PictureMime)) { + + Thumb + + } +
+ @if (!string.IsNullOrEmpty(Model.Description)) { +

@Html.Raw(descriptionHtml)

+ } +
+ +

+ @ViewRes.Event.DetailsStrings.Category: + @Html.ActionLink(Translate.ReleaseEventCategoryNames[Model.Category], "Index", "Search", UrlMapper.Search.Events(category: Model.Category), null) +

+ + @if (Model.WebLinks.Any()) { + @Html.Partial("Partials/EntryDetails/_ExternalLinksList", new ExternalLinksListViewModel(Model.WebLinks, false)) +
+ } + + @if (!string.IsNullOrEmpty(Model.AdditionalNames)) { +

+ @SeriesDetailsStrings.Aliases: @Model.AdditionalNames +

+ } + +

+

@ViewRes.SharedStrings.Tags:
+ + @ViewRes.EntryDetailsStrings.EditTags +

+
+ +

@ViewRes.SharedStrings.ReleaseEvents

+
    + @foreach (var ev in Model.Events) { +
  • + @Html.ActionLink(ev.Name, "Details", new { id = ev.Id, slug = ev.UrlSlug }) + @if (ev.Date != null) { + (@ev.Date.Value.ToString("d")) + } +
  • + } +
+ +
+ @{ Html.RenderPartial("Partials/_TagsEdit"); } +
+ +@section BodyScripts { + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Event/SeriesVersions.cshtml b/VocaDbWeb.Core/Views/Event/SeriesVersions.cshtml new file mode 100644 index 0000000000..b295593d05 --- /dev/null +++ b/VocaDbWeb.Core/Views/Event/SeriesVersions.cshtml @@ -0,0 +1,16 @@ +@using VocaDb.Web.Code +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@model VocaDb.Web.Models.Shared.Versions + +@{ + PageProperties.Title = ViewRes.EntryDetailsStrings.Revisions + " - " + Model.Entry.Name; + PageProperties.Robots = PagePropertiesData.Robots_Noindex_Nofollow; + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.ReleaseEvents, "Index"), + Html.ActionLink(Model.Entry.Name, "SeriesDetails", new { id = Model.Entry.Id }) + }; +} + +@Html.Partial("Partials/ArchivedEntry/_CurrentVersionMessage", new CurrentVersionMessageViewModel(Model.Entry.Version, Model.Entry.Status)) + +@Html.Partial("Partials/ArchivedEntry/_ArchivedObjectVersions", new ArchivedObjectVersionsViewModel(Model.ArchivedVersions, id => Url.Action("ViewSeriesVersion", new { id = id }))) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Event/Versions.cshtml b/VocaDbWeb.Core/Views/Event/Versions.cshtml new file mode 100644 index 0000000000..a0b9535d37 --- /dev/null +++ b/VocaDbWeb.Core/Views/Event/Versions.cshtml @@ -0,0 +1,16 @@ +@using VocaDb.Web.Code +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@model VocaDb.Web.Models.Event.Versions + +@{ + PageProperties.Title = ViewRes.EntryDetailsStrings.Revisions + " - " + Model.Entry.Name; + PageProperties.Robots = PagePropertiesData.Robots_Noindex_Nofollow; + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.ReleaseEvents, "Index"), + Html.ActionLink(Model.Entry.Name, "Details", new { id = Model.Entry.Id, slug = Model.Entry.UrlSlug }) + }; +} + +@Html.Partial("Partials/ArchivedEntry/_CurrentVersionMessage", new CurrentVersionMessageViewModel(Model.Entry.Version, Model.Entry.Status)) + +@Html.Partial("Partials/ArchivedEntry/_ArchivedObjectVersions", new ArchivedObjectVersionsViewModel(Model.ArchivedVersions, id => Url.Action("ViewVersion", new { id }))) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Event/ViewSeriesVersion.cshtml b/VocaDbWeb.Core/Views/Event/ViewSeriesVersion.cshtml new file mode 100644 index 0000000000..fc770c1107 --- /dev/null +++ b/VocaDbWeb.Core/Views/Event/ViewSeriesVersion.cshtml @@ -0,0 +1,73 @@ +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@using VocaDb.Web.Models.Shared.Partials.Event +@model VocaDb.Web.Models.Shared.ViewVersion +@inject Login Login + +@{ + + PageProperties.Title = "Revision " + Model.Entry.ArchivedVersion.Version + " for " + Model.Entry.Name; + PageProperties.Robots = PagePropertiesData.Robots_Noindex_Nofollow; + + if (Model.Entry.ReleaseEventSeries != null) { + ViewBag.Parents = new[] { + Html.ActionLink("Events", "Index"), + Html.ActionLink(Model.Entry.ReleaseEventSeries.Name, "SeriesDetails", new { id = Model.Entry.ReleaseEventSeries.Id }), + Html.ActionLink("Revisions", "SeriesVersions", new { id = Model.Entry.ReleaseEventSeries.Id }) + }; + } else { + ViewBag.Parents = new[] { + Html.ActionLink("Events", "Index"), + }; + } + +} + +@section Toolbar { + Download XML + @if (Login.CanViewHiddenRevisions) { + if (Model.Entry.ArchivedVersion.Hidden) { + @Html.ActionLink(ViewRes.ViewVersionStrings.UnhideVersion, "UpdateSeriesVersionVisibility", new { archivedVersionId = Model.Entry.ArchivedVersion.Id, hidden = false }, new { id = "showLink", onclick = string.Format("return confirm(\"{0}\");", ViewRes.ViewVersionStrings.ConfirmUnhide) }) + } else { + @Html.ActionLink(ViewRes.ViewVersionStrings.HideVersion, "UpdateSeriesVersionVisibility", new { archivedVersionId = Model.Entry.ArchivedVersion.Id, hidden = true }, new { id = "hideLink", onclick = string.Format("return confirm(\"{0}\");", ViewRes.ViewVersionStrings.ConfirmHide) }) + } + } +} + +@if (Model.Entry.ArchivedVersion.Hidden) { + @Html.Partial("Partials/EntryDetails/_HiddenBanner") +} + +@if (Model.Entry.ComparableVersions.Any()) { + using (Html.BeginForm("ViewSeriesVersion", "Event", FormMethod.Post, new { @class = "form form-inline" })) { + @:Compare to: @Html.DropDownListFor(m => m.ComparedVersionId, ViewHelper.CreateSelectList(Model.Entry.ComparableVersions, i => i.Id, i => i.Version + " (" + i.TranslateChangedFields(Model.EnumTranslations) + " by " + i.AgentName + ")", Model.Entry.ComparedVersionId), new { @class = "input-xlarge", onchange = "$(this).closest('form').submit();" }) + + } +} + +@Html.Partial("Partials/ArchivedEntry/_ArchivedObjectVersionProperties", new ArchivedObjectVersionPropertiesViewModel(Model.Version(Model.Entry.ArchivedVersion), Model.Version(Model.Entry.ComparedVersion))) + +@Html.Partial("Partials/Event/_PrintArchivedEventSeriesData", new PrintArchivedEventSeriesDataViewModel(Model.Entry.Versions)) + +@Html.Partial("Partials/EntryDetails/_ReportEntryVersionPopupKnockout", new ReportEntryVersionPopupKnockoutViewModel()) + +@section BodyScripts { + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Event/ViewVersion.cshtml b/VocaDbWeb.Core/Views/Event/ViewVersion.cshtml new file mode 100644 index 0000000000..803399b60d --- /dev/null +++ b/VocaDbWeb.Core/Views/Event/ViewVersion.cshtml @@ -0,0 +1,74 @@ +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@using VocaDb.Web.Models.Shared.Partials.Event +@model VocaDb.Web.Models.Shared.ViewVersion +@inject Login Login + +@{ + + PageProperties.Title = "Revision " + Model.Entry.ArchivedVersion.Version + " for " + Model.Entry.Name; + PageProperties.Robots = PagePropertiesData.Robots_Noindex_Nofollow; + + if (Model.Entry.ReleaseEvent != null) { + ViewBag.Parents = new[] { + Html.ActionLink("Events", "Index"), + Html.ActionLink(Model.Entry.ReleaseEvent.Name, "Details", new { id = Model.Entry.ReleaseEvent.Id, slug = Model.Entry.ReleaseEvent.UrlSlug }), + Html.ActionLink("Revisions", "Versions", new { id = Model.Entry.ReleaseEvent.Id }) + }; + } else { + ViewBag.Parents = new[] { + Html.ActionLink("Events", "Index"), + }; + } + +} + +@section Toolbar { + Download XML + @ViewRes.EntryDetailsStrings.ReportAnError + @if (Login.CanViewHiddenRevisions) { + if (Model.Entry.ArchivedVersion.Hidden) { + @Html.ActionLink(ViewRes.ViewVersionStrings.UnhideVersion, "UpdateVersionVisibility", new { archivedVersionId = Model.Entry.ArchivedVersion.Id, hidden = false }, new { id = "showLink", onclick = string.Format("return confirm(\"{0}\");", ViewRes.ViewVersionStrings.ConfirmUnhide) }) + } else { + @Html.ActionLink(ViewRes.ViewVersionStrings.HideVersion, "UpdateVersionVisibility", new { archivedVersionId = Model.Entry.ArchivedVersion.Id, hidden = true }, new { id = "hideLink", onclick = string.Format("return confirm(\"{0}\");", ViewRes.ViewVersionStrings.ConfirmHide) }) + } + } +} + +@if (Model.Entry.ArchivedVersion.Hidden) { + @Html.Partial("Partials/EntryDetails/_HiddenBanner") +} + +@if (Model.Entry.ComparableVersions.Any()) { + using (Html.BeginForm("ViewVersion", "Event", FormMethod.Post, new { @class = "form form-inline" })) { + @:Compare to: @Html.DropDownListFor(m => m.ComparedVersionId, ViewHelper.CreateSelectList(Model.Entry.ComparableVersions, i => i.Id, i => i.Version + " (" + i.TranslateChangedFields(Model.EnumTranslations) + " by " + i.AgentName + ")", Model.Entry.ComparedVersionId), new { @class = "input-xlarge", onchange = "$(this).closest('form').submit();" }) + + } +} + +@Html.Partial("Partials/ArchivedEntry/_ArchivedObjectVersionProperties", new ArchivedObjectVersionPropertiesViewModel(Model.Version(Model.Entry.ArchivedVersion), Model.Version(Model.Entry.ComparedVersion))) + +@Html.Partial("Partials/Event/_PrintArchivedEventData", new PrintArchivedEventDataViewModel(Model.Entry.Versions)) + +@Html.Partial("Partials/EntryDetails/_ReportEntryVersionPopupKnockout", new ReportEntryVersionPopupKnockoutViewModel()) + +@section BodyScripts { + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Help/External.cshtml b/VocaDbWeb.Core/Views/Help/External.cshtml new file mode 100644 index 0000000000..a41487a9ed --- /dev/null +++ b/VocaDbWeb.Core/Views/Help/External.cshtml @@ -0,0 +1 @@ + diff --git a/VocaDbWeb.Core/Views/Help/Index.cshtml b/VocaDbWeb.Core/Views/Help/Index.cshtml new file mode 100644 index 0000000000..84ad0a473d --- /dev/null +++ b/VocaDbWeb.Core/Views/Help/Index.cshtml @@ -0,0 +1,524 @@ +@using VocaDb.Web.Helpers +@inherits VocaDb.Web.Code.VocaDbPage + +@{ + PageProperties.Title = "Help / About"; + var contributors = new[] { + "digited", "Zakudono", "salanos", "kyllakivi", "Goship", "mi.chan", "gaminat", "Wiktor", "Shiroizu", "Ota-kun", "Hissori", "nebulasresolution", "megaki11", "faerimagic" + }; + int freeTagId = ViewBag.FreeTagId; + int instTagId = ViewBag.InstrumentalTagId; +} + +
+ + + +
+

Overview

+

+ VocaDB is a free to use database for discography information about Vocaloid and related voice synthesizers such as UTAU. + The main goal of the site is to have accurate, translated entries for as many artists, their albums and songs as possible. + At the same time, we try to promote the Vocaloid artists by making their works more widely known, and if available, offer ways to purchase those. + Anyone is able to contribute and correct existing information by creating an account. + Vocaloid artists are encouraged to add their own information, thus providing a way to promote themselves. + Registered users may add lyrics and PVs to songs. + Entries may be tagged with specific genres and other meta-information. + Popular doujin events such as The Vocaloid M@ster may also be tracked. + The site is designed so that it would be comfortable to use by fans around the world, including both western and Japanese users. +

+

+ We specialize in Vocaloid and UTAU content, both Japanese and overseas, so the site includes multiple features designed with that in mind. + For that reason, the site supports embedding media, such as PVs from Niconico, YouTube and piapro (among other sites), that are popular among the community. + Users can choose to view titles in the original language or see translated names instead. +

+

+ VocaDB is not a music sharing site. Illegal album downloads are not allowed. + However, we are eager to promote artists who are sharing their music + @Html.ActionLink("for free", "Index", "Search", VocaDb.Model.Utils.Search.SearchRouteParamsFactory.Instance.Albums(tagId: freeTagId), null). + Whenever possible, album entries contain links to websites where you can purchase the albums. +

+ +
+

Registration benefits

+

As an unregistered user, you can browse and search for Vocaloid songs, producers, circles, PVs and song lyrics.

+

A free registration is required in order to

+
    +
  • customize your display language and date formatting
  • +
  • contribute information to VocaDB
  • +
  • add songs to your favorites and songlists
  • +
  • list your personal collection of Vocaloid albums
  • +
  • participate in the discussions
  • +
  • tag albums, artists and songs
  • +
  • among other things
  • +
+ +
+

I found an error, what should I do?

+

+ If you encounter incorrect or missing information, you can let us know by using the report feature, or better yet, create an account and correct it yourself. + Please read the + guidelines + first, though, and familiarize yourself with the conventions of this site by examining old entries. + If you have a technical problem with the site, please contact us. +

+ +
+

For artists and other content creators

+

+ Are you a producer, illustrator, animator, or some other content creator working with Vocaloid/UTAU music or videos? + If you have published any songs/videos on services like Niconico, YouTube or SoundCloud, we welcome you to add an entry for yourself (and your circle) to our database. + First, make sure that your entry hasn't already been added by someone else. + After your entry has been added, you can submit an account verification request, which allows you to be marked as the legitimate owner of that entry. +

+ +
+

For developers and webmasters

+

+ Do you have a site or service that concerns Vocaloid/UTAU discography? + We'd be happy to help you, and we're prepared to share the information on our site with other similar services. + Currently, we have a limited public API for basic queries, supporting XML and JSON, + and it's possible to expand it as needed. +

+

+ We're offering our own embeddable VocaDB player. + VocaDB embeddable player can be used on any website, just like you'd embed YouTube or NND. + With VocaDB's embeddable player, you'll get translations for the song title and artist name, and the user can switch between video services. + There's also a link to VocaDB for additional information. + The embeddable player supports OEmbed. +

+

+ Finally, we have a tooltip library that can be used to add informational tooltips + to VocaDB links on your site. +

+

+ Please contact us for more details and requests. +

+ +
+

Licensing and data sharing

+

+ VocaDB's contents are considered open data. You're free to use the information from this website + in the way you see fit. However, we ask that when directly copying information from VocaDB you'd include a note stating that you got that information from VocaDB. + Generally, when downloading data from VocaDB to your site, it's a much better idea to use the programmatic APIs rather than copying by hand. + This also ensures the data stays up to date. +

+

+ Scraping a large number of pages using an automated bot is forbidden without an explicit permission and may lead to banning your IP from accessing the site. + Please contact us if you wish to download all entries from the site. We can provide an XML dump of the complete contents of the site upon request. +

+ +
+

Privacy policy

+

+ VocaDB respects the privacy of its users. We will not share your personal details with third parties in any case. + Email addresses, IP addresses and passwords are considered sensitive information and will never be exposed through the APIs. + Users' passwords are stored in the database as encrypted and salted. +
+ VocaDB offers some privacy options: for example, all edits made by the users are public, but users are able to restrict whether their name should appear on the front page. + In order to prevent abuse, we log the IP addresses of the users accessing the site, but this information is strictly available to moderators only. We do not use cookies to track user activity. However, + cookies are used to store certain user-related settings such as the display language. We use Google Analytics to track the number of users accessing the site, but + Google Analytics will never identify individual users. For avatars, we use Gravatar, which retrieves a user's profile picture based on their email address. The original email address + is never revealed - only the hash. +

+ +
+

Staff

+

A big thank you to our major contributors: @Html.LinkList(contributors, u => Html.ActionLink(u, "Profile", "User", new { id = u }, null)) and others.

+

Japanese translation by Japanese Ninja No.1.

+ +
+

Contact Us

+ support[at]vocadb.net
+ Code on Github
+ You can also contact the + @Html.ActionLink("moderators", "Index", "User", new { groupId = VocaDb.Model.Domain.Users.UserGroupId.Moderator }, null) + or @Html.ActionLink("administrators", "Index", "User", new { groupId = VocaDb.Model.Domain.Users.UserGroupId.Admin }, null) + directly on the site if you have questions.
+
+ +
+ + + Wikipedia article
+ Vocaloid wiki
+
+

Short description

+

Vocaloid is a unique subculture of user-created content (music, illustrations, videos, software, games, cosplay, events) based on voice synthesizers and their mascots.

+

Vocaloid synthesizer software is a digital musical instrument to create human vocals, allowing anyone to have their personal virtual singer.

+

The first Vocaloid "singers," Leon and Lola, were released in 2004 in the English language, but weren't successful. Vocaloid technology became popular in 2007 in Japan with the Japanese voicebank "Hatsune Miku" (first for the VOCALOID2 engine in Japan, second in the world after Sweet Ann).

+

Being a great and fresh combination of a decent V2 voicebank and a snazzy boxart illustration, Hatsune Miku gained unexpected, unprecedented and massive success and became the symbol of all Vocaloid technology and subculture.

+

Why in Japan?

+
    +
  • The simplest language for synthesis in the world, Japanese has syllable-based phonetics as well as no accents and no rhyme concept in poetry
  • +
  • Popular anime style (moe catalyst)
  • +
  • A lot of dōjin (self-published) activity
  • +
  • Traditional high-pitched female vocal
  • +
  • Diverse and active musical scene--a lot of indie musicians
  • +
  • Pop-idols and virtual pop-star concepts in anime (Sharon Apple in "Macross Plus", 1994)
  • +
+

Japan happened to be the best country for a popularity breakthrough of the Vocaloid technology.

+

Features of Vocaloid

+
    +
  • Vocaloid synthesizer software is treated as a musical instrument, so it is free to use for any purpose.
  • +
  • Piapro license initiative from Crypton Future Media permits the non-commercial usage of their mascot images.
  • +
  • Decentralization. No one commands the community what to do and how to do it. All content (music, videos, illustrations) is created by users themselves for their own purposes (fun, self-expression, selling).
  • +
  • Collaboration of song writers, musicians, illustrators, animators and programmers.
  • +
  • Self-promotion of Vocaloids and producers. Basically, the community chose Miku and made her popular (as well as GUMI).
  • +
  • Sharing of music and support principles. Most Vocaloid music and videos are freely available on NND, Piapro and YouTube, and still the community supports producers by buying their albums. It is possible for and being done by people who live outside Japan, too. Musical instruments, hardware, software and learning cost a lot, and producers are mostly indie musicians, so every sold CD is important for them. If you really like the music that was done by a Vocaloid producer, please support him.
  • +
  • Concerts are also a large part of the Vocaloid subculture. Vocaloid mascots are projected onto a transparent screen and perform "live" with a real band.
  • +
+

PV

+

Promotional Videos are being used to attract listeners to Vocaloid songs, becoming part of the songs, the form of art by itself and a part of Vocaloid subculture.

+

Japanese flash video streaming service Niconico (originally called Nico Nico Douga or NND) greatly helped in the popularization of Vocaloid in Japan and is used for uploading new Japanese Vocaloid songs and videos, live streaming of concerts, presentations and other events.

+

Sound quality of videos on YouTube and Niconico

+

NND videos have various sound quality - from MP3 CBR 64 kbps in "economy" mode to AAC VBR ~370 kpbs, but generally it is higher than on YouTube, that has AAC VBR ~128 kbps in most videos (same for all video resolutions).

+

MMD

+

MikuMikuDance (MMD for short) is a free, popular and powerful 3D animation software made by Yu Higuchi (HiguchiM) for Vocaloid Promotional Videos.

+

MMD is a part of the success of Hatsune Miku and is also a subculture itself, having MMD Cup championships and being used to produce animation for memes, Touhou, IDOLM@STER, etc.

+

Producer

+ Description on Vocaloid Wiki +

"Producer" is the term used for any person that produces Vocaloid-related audiovisual material, generally original music or fanmade PVs.

+

Circles and Labels

+

Dōjinshi circle is a group of authors of self-published works that may act as a label for dōjin events (Comiket or Vocaloid M@ster).

+

Record label is a company that provides professional studio services (instruments, recording, mastering), production of CDs, various promotion and copyright protection. The most well known Vocaloid label is KarenT, created by Crypton Future Media.

+
+ +
+

General principles

+
    +
  • Avoid submitting entries that already exist in the database. Duplicates have to be deleted or merged by the staff.
  • +
  • When editing entries that are finalized or approved (not drafts), make sure you have a good reason, and preferably state that reason in the edit notes unless it's obvious. If you're unsure of whether your edit is appropriate and necessary, ask in the comments, on Twitter, in discussions, or by email.
  • +
  • This is a database for Vocaloid content and related voice synthesizers such as UTAU. Instrumental songs and human-sung covers are allowed when they appear on albums together with Vocaloid songs, or there's a significant number of Vocaloid covers of those songs. Albums without Vocaloid vocals are generally not relevant to our focus. Completely unrelated content will be deleted.
  • +
  • Download links to illegal file-sharing sites that provide no useful information (like Mediafire/4Shared/Torrents) are NOT allowed. Links to informative blogs/forums/wikis are allowed but should be categorized as "Unofficial" or "Reference". The staff members reserve the right to remove any links deemed inappropriate.
  • +
  • Direct download links to media files (such as .mp3) are generally not allowed, even if they're official and legal. If the song/video is officially distributed, link to the artist's official download page (for example on piapro) instead.
  • +
  • Creating multiple user accounts is not allowed. Any kind of abuse, manipulation of ratings or spamming will not be tolerated.
  • +
  • VocaDB strives for objectivity. Advertising in comments or entry descriptions is forbidden without prior approval by a staff member. Verified artists are allowed to post advertisements in the "personal description" field for entries they manage.
  • +
+ +

Language

+

+ The official language of the site is English. Please prefer using English for all non-translateable information. + For example, when adding external links, the descriptions of those links should be in English. Discussions and tags should also be mainly in English. + Other languages are allowed with a good reason, for example, if a specific piece of information cannot be properly translated into English.
+ We're looking into the possibility of allowing other languages in the future, at least for descriptions and discussions. +

+ +

How can I add new producers, albums and songs?

+

Register and use links in main navigation panel or buttons in producer, album and song list pages.

+ +

Names and translations

+

+ All names should primarily be written in the original language, which usually means Japanese, but it can also be English or Korean, for example. + The system supports 4 language options: +

+
    +
  • "Non-English" covers original names in all languages that aren't English or romanized, for example Japanese.
  • +
  • "Romanized" is for foreign original names written in the Latin (western) alphabet. Usually this means Romaji.
  • +
  • "English" is for official names in English and known translations of foreign names.
  • +
  • "Unspecified" is for names where the language doesn't matter or it's unknown. Unspecified names will never be used as display names of an entry if a name of a different option is provided.
  • +
+

+ If the original language is English, the name should be written in the English language field. Otherwise, it's written to the "Non-English" language field. + If those names have commonly used romanizations or English translations, those can be entered as well to make it easier for the international audience to find + and understand those names. Other names, for example translations to other languages, should be marked as "Unspecified" according to current rules. + Sometimes, an entry may have multiple names in one language, for example a producer's real name and artist name. + In this case, the additional names can be marked as "Unspecified" so that they won't be used as display names regardless of the entry's display option. +
+ Sometimes, the Romanization or English name of the artist is always used, even though the artist also has a name in Japanese. In this case, the Japanese + name should be marked as Unspecified so that it won't be used as a display name. +

+

For more guidelines regarding Romanization, check the wiki article.

+

+ If the entry has multiple names in one language and it's not clear which one of those should be the primary one, you should refer to official information, + such as product packaging. If the name in the album's cover doesn't conflict with the other rules on the site, that name should be preferred as the primary one. +
+ However, keep in mind that sometimes the official information contains the same name in multiple languages combined into one. In this case you should enter + the name separately for each language. For example, if the song is uploaded to Niconico with the name 秘密警察 - Himitsu Keisatsu, you should + enter both names separately: 秘密警察 in the Non-English field and Himitsu Keisatsu in the Romanized field. +

+

+ Registered users can set their display language so that they will always see the names in that language, if available. + Unregistered users and users who haven't set their display language will see the original names, which may be either English or something else. + For example, if the non-English name of an artist is ナナホシ, the romanized name is nanahoshi, and the default language option is set to "Original", then by default, users will see ナナホシ unless they have chosen to prefer romanized names instead. +

+

Generally, there is no need to add composite names, and those names should be broken into separate fields.

+

is incorrect: every field should contain only one name.

+ +

Entry statuses

+

+ Entries that are marked as drafts have missing or incomplete information. The draft status is indicated on the entry's page. + All entries should meet certain requirements before they can be marked as finished/completed. For now, these requirements are only suggestions, but + in the future, they may be enforced more strictly. Of course, when editing an entry that meets these requirements, + you can still mark it as draft if you feel that the entry needs further attention. +

+ After the entry has been finished, it can be approved by a trusted user or staff member. Most properties of approved entries cannot be edited by users with + normal permissions. Tags can still be edited by anyone and comments can be added. Trusted users are also able to change the status back from Approved to Finished or even Draft. +
+ A note for trusted users regarding entry approval: You're encouraged to mark the entry as Approved when you've checked that all the necessary information is provided and + it's correct. However, songs shouldn't be marked as approved until they have lyrics. When approving albums, make sure that ALL the artists have been added. +

+ +
+

Artists, producers and Vocaloids

+

Artist types

+

+ Every artist has a classification which also determines the artist's default roles. + Roles can be overridden per album and per song-basis, but in many cases the artist is involved only in one role. +

+
    +
  • "Producer" - the person who created the song (and in the case of a Vocaloid song, usually provided the vocals as well using Vocaloid). Not necessarily the original composer.
  • +
  • "Animator" - person who primarily animates PVs.
  • +
  • "Illustrator" - person who primarily draws static illustrations.
  • +
  • "Lyricist" - person who primarily composes lyrics for songs.
  • +
  • "Circle" - group that self-publishes and sells albums only at doujin events (such as Comiket or Vocaloid Master).
  • +
  • "Label" - a commercial company that publishes albums for other artists.
  • +
  • "Other group" - group that releases albums via Labels.
  • +
  • "Other vocalist" - human singers (NND vocalists/Utaite) and fan-made voice configurations (derivatives).
  • +
  • "Other voice synthesizer" - voice synthesizer voicebanks (machine voices) that aren't Vocaloid, UTAU or CeVIO.
  • +
  • "Other individual" - other people such as instrumentalists.
  • +
+ +

Artist pictures

+

+ When adding pictures to artists, keep in mind all the pictures should be related to the artist himself, not his works. Do not upload album covers for artist entries. +
+ That said, there is no common rule for choosing the main picture of an artist. Photos of the artist himself as well as official logos are preferred. + You may upload any number of these pictures as additional pictures for the artist, provided that they're relevant enough. + Copyrighted pictures or pictures of artists may be taken down if the copyright holder requests it. +
+ If no better picture is provided, any picture found on one of the artist's official profiles, for example on Twitter, is acceptable as well. +

+ +

Should I create an entry for artist's personal circle?

+

+ Many Vocaloid artists have a personal band/circle through which they publish their albums. Often there are no other members in this circle. + In this case, it's not necessary to create a separate entry for that personal circle, but it's not wrong either, and might be a good idea + if the artist has separate websites/blogs for himself and his circle. If there is no separate entry for the circle, the circle name + can be included as an alias for that artist. In that case, it would be advisable to mention this in the notes. +

+ +

Completed artist entries should meet the following criteria:

+
    +
  • The artist has at least one name whose language option isn't "Unspecified".
  • +
  • Artist type isn't "Unspecified".
  • +
  • The artist has a description OR at least one external link (to a wiki, artist's NND MyList etc.)
  • +
+ +
+

Albums

+

To assign producers and vocalists to songs in an album, add artists to the album first and then click on each added song in the song list to show a quick selection dialog.

+

+ Please note that the album must have at least one song with synthesized vocals (Vocaloid, UTAU and other voice synthesizers all count). + Cover albums consisting only of human-sung covers of Vocaloid songs are unfortunately not allowed. Consider checking UtaiteDB. + Unrelated albums may be deleted from VocaDB. +

+ +

A note about fanmade compilations

+

+ Unofficial, fanmade compilation albums (bootlegs) where the authors don't have permissions to use the songs are generally not allowed. + There can be some exceptions if the albums are widely known, for example the Hatsune Miku 1st Song Album. + In any case, the staff members reserve the right to remove these entries should they deem it necessary. +

+ +

Adding artists for albums

+

+ When linking artists to albums, at the very least you should add the responsible circle (usually there's only one) and vocalists. + Individual producers and the associated record label (if any) can be added as well. At the moment, all artist types may be linked to albums. +
+ Always enter artists individually. Artists such as "producer feat. vocalist", meaning "cosMo feat. Hatsune Miku", are not needed. + The system will produce these "artists strings" automatically. +
+ Sometimes, it's necessary to credit people that aren't in the database, and it doesn't make sense to add an entry for them. + For example, if the song in the database is a remix/cover and the original isn't a Vocaloid song, the original composer doesn't need to be added to the database, + but should be credited nevertheless. + These "extra artists" can then be assigned into roles just like artists that are in the database. However, for all Vocaloid-related artists, + it's necessary to create an entry for that artist. +

+ +

Album types

+ The possible album types on VocaDB are: +
    +
  • "Original album" - Album that consists mostly of previously unpublished songs.
  • +
  • "Single" - Contains one or two individual tracks. Alternate versions (instrumentals and remixes) are usually not counted.
  • +
  • "EP" - Meaning extended play. Contains 3-4 individual tracks. Alternate versions (instrumentals and remixes) are usually not counted.
  • +
  • "Split album" - Collaboration between two or more (but usually just two) equal artists, where all artists have roughly the same number of songs.
  • +
  • "Compilation" - Collection of previously published songs, gathered from one or more earlier albums. For example, "best of" collections.
  • +
  • "Video" - Disc containing mostly music videos, usually a DVD or Blu-ray.
  • +
  • "Other" - For albums that don't fit into anything above.
  • +
+

+ These are only suggestions; use common sense when determining album type. + For example, an album with a single song that by itself is as long as a regular album (the definition on Wikipedia says over 25 minutes) + can be counted as "original album" instead of single. + + If unsure of which type to use, try to find out how the artists themselves call the album. + For more elaborate descriptions, please refer to Wikipedia, Discogs or Musicbrainz. +

+ +

Completed album entries should meet the following criteria:

+
    +
  • The album has at least one name whose language option isn't "Unspecified".
  • +
  • All artists responsible for the album are specified. Usually there should be at least one producer and one vocalist. Whenever it makes sense, each of those artists should have an entry in the database. If you can't locate some of the artists, please leave the entry as draft.
  • +
  • Album type isn't "Unspecified". Try to find the correct type.
  • +
  • The album's release date, at least release year, is specified.
  • +
  • The album has a complete tracklist.
  • +
+ +
+

Songs

+

Song entries contain information about authors and vocalists (see "Artists" tab), PVs and lyrics.

+

is incorrect: all additional info (vocalists, authors, PV) should be added to appropriate fields. A song title should contain nothing more than the name itself.

+ +

Adding artists for songs

+

+ Most Vocaloid songs have one producer and one or more vocalists (i.e. Vocaloids). + Circles, labels and other groups are generally NOT credited for individual songs, unless it's clear that the whole group worked on that song. + Always prefer adding individual people to songs over adding circles or groups if possible. Note that different sources may swap producer and group names. +

+

+ When tagging Vocaloids, you should use the information provided by the artist, not guess yourself. If the artist says the singer is Hatsune Miku, you should + add Miku's original voicebank, even if you think the singer is actually an Append. Sometimes, the artist says the singer is Append, but doesn't specify which Append. + In this case, you should choose Append (unknown). Do not guess unless you are sure. +

+

+ In the case of a remix or cover, the original composer/lyricist of the song does not need to be credited if the + original song is in the database. +
+ If the original is a Vocaloid song, or a song featuring another voice synthesizer, add the original song to the database and link the original to the derived version, + or if the original is not a Vocaloid song, add the composer as an "extra artist" (see album guide for more information). + Remember to set this artist's role properly to Composer/Lyricist. +

+ +

Song types

+

+ The most important distinction is between Original and others. Original always means that the song is a completely original production. + If the song uses material from existing sources, it's a derived work and not original. +

+

+ For instrumental songs, you should use the original song type if the instrumental version is the original. + In this case, you should indicate that the song is an instrumental by tagging it with the + @Html.ActionLink("instrumental tag", "DetailsById", "Tag", new { id = instTagId, slug = "instrumental" }, null). + The instrumental song type is for instrumental versions of original songs. + Usually, if the song is an original work and not a derivative, it should be marked as original. + One exception to this rule is drama PVs. Because drama PVs are not songs, they should be separated from songs by using the Drama PV song type, even if the PV is + a completely original production, like drama PVs usually are. +

+ +

Song embeds and PVs

+

+ Songs may contain any number of embedded media files, such as Promotional Videos (PVs) on Niconico (NND), YouTube, Bilibili or Vimeo, or audio streams on Piapro or SoundCloud. + All embeds should have the same version of the song, meaning the audio should be the same. Shortened versions are sometimes accepted. Do not add karaoke versions as embeds to + the original song: either add the karaoke version as a link or create a new entry. Remixes should always be separate entries.
+ Very often, Vocaloid artists themselves upload their songs to NND, and sometimes to YouTube or SoundCloud as well, in which case these uploads are called "original". + Original uploads, if available, are highly preferred to any others.
+ If a PV made for a song is remarkable or well-known in some way, you're encouraged to create a separate entry for that PV and link it to the original song. +

+
    +
  • "Original" - the first video with that song, usually uploaded by the author himself (may lack any animation). There may be multiple Original PVs if all are uploaded by the producer.
  • +
  • "Reprint" - should be identical to "Original"; most often it's a YouTube reprint of an original NND video, uploaded by someone other than the artist.
  • +
  • "Other" - any following PV with animation, translated subtitles, MMD versions, etc. May be better known than the original upload. Note that the audio should still be the same as the original.
  • +
+

+ Important note about raw file embeds: VocaDB supports embedding raw links to .mp3 files. For security reasons, the usage of this feature is limited to trusted users only. + Only mp3 files are supported for now because it's the most widely supported format, but in the future we might allow other audio and video files as well. +
+ To do that (assuming, you're a trusted user), simply input a URL pointing to a .mp3 file. + The file must be publicly accessible and authorized by the artist - we do not support illegal distribution. + If possible, try to make sure the artist has allowed embedding the file on other sites. + Whenever adding a raw file as media, be sure to include a link to the official webpage where the link is from. If there is no such webpage, it's better not to add the media. +
+ Finally, raw file embeds should mostly be used as fallback when the song isn't available on any other service. Especially if the song is officially on YouTube or SoundCloud, raw file embeds + should not be added. Services like YouTube and SoundCloud have worldwide content delivery networks (CDNs) that most likely offer better performance + than the artist's own server hosting the file. +

+ +

Completed song entries should meet the following criteria:

+
    +
  • The song has at least one name whose language option isn't "Unspecified".
  • +
  • The song has at least one artist (usually, there should be at least one producer and one vocalist).
  • +
  • Song type isn't "Unspecified".
  • +
+ +
+

Tags

+

+ Tags are free-form metadata that can be added to all entry types. Tags allow users to link entries together by any + properties they can come up with, not being limited to the options provided by the system. Examples of tags + are genres, presentation, languages and themes. Tags can be edited more freely than other properties and some of them may even be considered subjective. + Therefore, tags are based on voting. Any user may add tags to an entry and vote on existing tags. Only the most popular tags + will be displayed for that entry. +

+ +

What to tag then?

+

+ Generally, you should avoid tagging with information that is already provided by more specialized + fields. For example, albums or songs with artist names is redundant because the artists list already handles this better. + Likewise, tagging cover songs with "cover" is useless if the song classification is already cover. Of course, albums + have no such classification, so that tag might be relevant for an album (or artist, if that artist is known for making covers). +

+

+ Most tags should be objective, meaning their validity isn't based on the listener's subjective opinion. For example, "beautiful" + is a poor tag because beauty is highly subjective. "Calm" is a slightly better tag, since that can be defined by some objective characteristics, + although it's still mostly subjective. Please prefer creating private song lists for highly subjective properties. +

+ +

Tags versus pools

+

+ Trusted users are able to create public songlists called pools. Pools can be used for largely the same purpose as tags, grouping + songs together based on some common theme. There are a few differences between tags and songlists. +

+
    +
  • Pools are not voted on, unlike tags. All pools are equal.
  • +
  • Only trusted users are able to edit pools. Any user can vote on tags. Thus tags are easier to use, but also more unreliable.
  • +
  • Pools may contain only songs. Tags can be applied to albums and artists as well.
  • +
  • Songs in a pool can be ordered. Songs with a specified tag are unordered.
  • +
  • You can add notes to songs in a pool.
  • +
+ +
+

Other guidelines

+
    +
  • Applies to trusted users: when merging duplicate entries, prefer merging the newer entry to the older one, unless the new entry is finalized while the older one is draft.
  • +
+ +
+
+ +@section BodyScripts { + +} diff --git a/VocaDbWeb.Core/Views/Help/Index.ja.cshtml b/VocaDbWeb.Core/Views/Help/Index.ja.cshtml new file mode 100644 index 0000000000..17b699ec76 --- /dev/null +++ b/VocaDbWeb.Core/Views/Help/Index.ja.cshtml @@ -0,0 +1,457 @@ +@using VocaDb.Web.Helpers +@inherits VocaDb.Web.Code.VocaDbPage + +@{ + PageProperties.Title = "サポート / DBについて"; + var contributors = new[] { + "digited", "Zakudono", "salanos", "kyllakivi", "Goship", "mi.chan", "gaminat", "Wiktor", "Shiroizu", "Ota-kun", "Hissori", "nebulasresolution", "megaki11" + }; + int freeTagId = ViewBag.FreeTagId; + int instTagId = ViewBag.InstrumentalTagId; +} + +
+ + + +
+

VocaDBとは

+

Vocaloid Database (VocaDB/VOCALOIDデータベース) はVOCALOID関連の曲、アルバム、アーティスト情報を中心としたデータベースです。ボカロ総合情報源となることを目指しています。

+

+ wiki、ブログ、フォーラムなどは、言語の異なる様々なウェブサイトに散在する情報を管理し効率的に検索したり、それらを蓄えておくには適していません。 + VocaDBは、VOCALOID音楽に関する情報のための独自のシステムとして、またその情報提供サービスとして、VOCALOID創作文化の特徴を意識しながら生み出されました。 + 同時に、創作者の作品を広めることを励んでいます。 +

+

当サイトではアカウント作成後誰もが情報を投稿、また編集することが出来ます。創作者ご自身の作品の情報の投稿・編集も勧めています。

+ +
+

このサイトでVOCALOID曲をダウンロード出来ますか?

+

いいえ。VocaDBには音楽ファイルは置いていませんし、ダウンロードリンクも提供していません。

+

詳しくはガイドラインを御覧下さい。

+ +
+

このサイトで何が出来ますか?

+

VOCALOID曲、プロデューサー、サークル、PV、歌詞の検索が出来ます。

+

また、このサイトに登録しアカウントを作ると(無料で簡単に行えます)以下のことも出来るようになります。

+
    +
  • 表示言語や日付の表示形式を変更
  • +
  • VocaDBに情報を登録・編集
  • +
  • 登録されている曲をお気に入りや曲リストに追加すること
  • +
  • VOCALOIDアルバムのコレクションリストを作ること
  • +
  • 意見交換に参加すること
  • +
  • アルバム・アーティスト・曲にタグを付けること
  • +
  • +
+ +
+

情報の誤りを報告するには、情報を追加するにはどうすれば良いですか?

+

+ もしデータベースに存在する情報に誤りや不足が有ったら、レポート機能を使用して管理スタッフに知らせることが出来ますが、アカウントを作成し直接訂正することを勧めます。サイトの技術的な問題が生じた場合は管理スタッフに御連絡下さい。 +

+ +

アーティストや創作者へ

+

+ プロデューサー、イラストレーター、アニメーター、ボーカロイド音楽や動画に関わるクリエーター、もしくはサークルの管理人ですか? + ニコニコ、Youtube、SoundCloudなどに作品を投稿されている創作者であれば、ご自身のアーティストページと作品をを登録することをお奨めします。 + (すでに登録済みでないことを確認してください。)次のリンクでアーティストアカウント証明が出来ます。これはVocaDBユーザーアカウントとVocaDBアーティストページを連携するプロセスです。 + +

+ +
+

他のサイトでVocaDBからの情報を使用しても良いですか?

+

はい。VocaDBは他の同種のサービスにも情報を提供しやすいように作られています。詳しくは開発者にお問い合わせ下さい

+ +
+

運営・管理スタッフ

+

Major contributors: @Html.LinkList(contributors, u => Html.ActionLink(u, "Profile", "User", new { id = u }, null)).

+

Japanese translation by Japanese Ninja No.1.

+ +
+

お問い合わせ

+ support[at]vocadb.net
+ VocaDB日本語話し合いページ
+ Github
+
+ +
+ + + Wikipedia article
+ Vocaloid wiki
+
+

概要

+

“VOCALOID”とは、人の歌声を再現するシンセサイザー及びそれらに付随するイメージキャラクターを元に、ユーザーが様々な創作(楽曲、イラスト、動画、ソフト、ゲーム、コスプレ、イベント)を行い作り上げたユニークな文化です。

+

VOCALOID(シンセソフト)は人の歌声を作り出すことが出来るソフト音源です。つまり、誰にでも専属のバーチャルシンガーが持てるということです。

+

最初のVOCALOIDシンガーは2004年に発売された英語のシンガー“LEON”と“LOLA”でしたが、売り上げは振るいませんでした。VOCALOIDの技術が広く世に知られるようになったのは2007年、日本語ライブラリの“初音ミク”(日本では初、世界では“Sweet Ann”に次いで二番目のVOCALOID2製品)が日本で発売されてからのことです。

+

魅力的なパッケージイラストとそれにぴったりな歌声の、最高且つ新鮮なコンビネーションによって、“初音ミク”は予想を上回る空前の大ヒット商品となり、VOCALOID技術・文化の代名詞となったのでした。

+

日本でVOCALOIDが根付いた背景

+
    +
  • 日本語は音声の合成において最もシンプルな言語――日本語は音節ベースの音声で、アクセントが無く、詩における韻の概念が無いこと
  • +
  • “萌え”要素を取り入れたアニメキャラのスタイルが浸透していること
  • +
  • 同人活動が盛んであること
  • +
  • 甲高い女声ボーカルに馴染みがあること
  • +
  • 多種多様で活発な音楽シーンと多くのインディーズアーティストが存在すること
  • +
  • アニメにおける仮想ポップアイドルの概念(シャロン・アップル『マクロスプラス』/1994年)
  • +
+

日本は偶然にもVOCALOIDがブレイクするのに最も適した国だったのです。

+

VOCALOIDの特徴

+
    +
  • VOCALOIDのソフトは楽器として扱われ、どんな目的にも自由に使うことが出来ます。
  • +
  • クリプトン・フューチャー・メディア株式会社のピアプロ・キャラクター・ライセンスはキャラクター画像の非営利使用を許可しています。
  • +
  • 全てのコンテンツ(楽曲・動画・イラスト等)は誰に強制されたわけでもなく、ユーザー達がそれぞれの目的(娯楽・自己表現・販売等)のために自分自身で創ったものです。
  • +
  • 作詞作曲家、ミュージシャン、イラストレーター、アニメーター、プログラマー等のコラボレーション。
  • +
  • VOCALOIDとプロデューサーのセルフプロモーション。基本的にはコミュニティがミクを選び彼女を有名にしました(GUMIも同様)。
  • +
  • 音楽を共有し、本源を支えること。殆どのVOCALOID曲や動画はニコ動、ピアプロ、YouTubeで無料で視聴出来、更に私達はアルバムを購入することでアーティストを支援することも出来ます。それは日本以外に住む人にも可能で、実際行われていることです。楽器やハード・ソフト、そしてその扱いを習得するには多くのコストが掛かり、また、プロデューサーの多くはインディーズで、CDの売り上げは彼らにとって重要です。もしあなたが本当にVOCALOIDプロデューサー達による音楽が好きであるなら、どうか彼らを支えて下さい。
  • +
  • コンサートがあります。
  • +
+

PV

+

PVは楽曲の一部となってリスナーをVOCALOID曲に引き込むために使用され、またPV自体の芸術性もVOCALOID文化の一端を担っています。

+

日本のニコニコはVOCALOID楽曲・動画を投稿するのに利用され、またコンサートやイベント等の配信を行い、日本におけるVOCALOIDの一般化に大きく貢献しました。

+

YouTubeとニコニコの音質

+

ニコニコには MP3 CBR 64 kbps(エコノミー)からAAC VBR ~370 kpbsまで、様々な音質のファイルがありますが、概してYouTube(多くはAAC VBR ~128 kbps)より高音質です(解像度は同程度)。

+

MMD

+

MikuMikuDanceは樋口M氏が制作したVOCALOID PVに使えるフリーで高性能な人気の3Dアニメソフトです。

+

MMDは初音ミクの成功の一因であり、MMD自体もMMD杯が開催されたり東方・アイマス等の動画制作に用いられるなど、一つの文化を形成しています。

+

プロデューサー

+ Vocaloid Wikiでの説明 +

"プロデューサー"とはVOCALOIDに関連するオーディオビジュアル――多くはオリジナル曲やPV――を制作する人を指す言葉です。

+

サークルとレーベル

+

同人サークルとはコミケやボーマスのような同人イベントで一般のレーベルの様に作品を自主販売する制作者達のグループです。

+

レコードレーベルとはプロのスタジオサービス(楽器、レコーディング、マスタリング)とCDの様々なプロモーションや著作権保護を提供するものです。VOCALOIDのレーベルとして最もよく知られているものには、クリプトン・フューチャー・メディア株式会社による“KarenT”があります。

+
+ +
+

General principles

+
    +
  • 既にデータベースに存在する事柄を新規に登録することは避けて下さい。同じ物について複数の項目が出来てしまっている場合はスタッフが削除あるいは一つの項目に統合します。
  • +
  • 完成もしくは承認された(下書きの状態ではない)記事を編集する際は、あなたがそれに手を加える適切な理由が有るのか、よく考えて下さい。そして、敢えて手を加える理由が誰の目にも明らかな場合を除いて、なるべく記事を編集した理由をはっきり示して下さい。
  • +
  • これはVOCALOIDに関連するコンテンツのためのデータベースです。UTAUやVOCALOIDの関わるコラボレーションは承認されますが、全くVOCALOIDに関係の無いコンテンツは削除されます。
  • +
  • 有用な情報をもたらさない不正なファイル共有サイト(例:Mediafire、Megaupload、Torrents)へのダウンロードリンクを貼ることは“認められていません”。有益な情報源であるブログ、フォーラム、wiki等へのリンクは“認められています”。
  • +
  • メディアファイル(mp3等)へ直接リンクすることは、それが公式・合法のものであっても基本的に認められていません。もし公式に楽曲や動画を配布しているのであれば、直リンクの代わりにそのアーティストの使用している正規のダウンロードページ(例:ピアプロ)へのリンクを貼って下さい。
  • +
+ +

Language

+

+ The official language of the site is English. Please prefer using English for all non-translateable information. + For example, when adding external links, the descriptions of those links should be in English. Discussions and tags should also be mainly in English. + Other languages are allowed with a good reason, for example, if a specific piece of information cannot be properly translated into English.
+ We're looking into the possibility of allowing other languages in the future, at least for descriptions and discussions. +

+ +

どうすれば新しくアーティスト(プロデューサー)、アルバム、曲を追加出来るのですか?

+

このサイトに登録し、トップページ左のメニューにあるリンク、または「アーティスト」、「アルバム」、「曲」それぞれのリストページにあるボタンから追加出来ます。

+ +

名前と訳

+

+ 全ての名前は第一にオリジナルの言語――多くの場合日本語で記載されているのでしょうが、例えば英語や韓国語の場合もあるでしょう。このデータベースには3つの言語オプションを用意してあります。: +

+
    +
  • "英語以外の言語"はオリジナルのタイトルが英語とローマ字以外の全ての言語、例えば日本語等の場合に使用します。
  • +
  • "ローマ字表記"は、どの国の人にも読めるようにオリジナルの名前をアルファベットで記すものです。
  • +
  • "英語"は正式名が英語である場合や正式名の英訳が判っている場合に使用します。
  • +
  • "不明な言語"は意味を持たない言葉や未知の言語に対応しています。不明な言語による名称は他の言語の名称が設定されている場合は項目名として表示されることはありません。
  • +
+

+ If the original language is English, the name should be written in the English language field. Otherwise, it's written to the "Non-English" language field. + If those names have commonly used romanizations or English translations, those can be entered as well to make it easier for the international audience to find + and understand those names. Other names, for example translations to other languages, should be marked as "Unspecified" according to current rules. + Sometimes, an entry may have multiple names in one language, for example a producer's real name and artist name. + In this case, the additional names can be marked as "Unspecified" so that they won't be used as display names regardless of the entry's display option. +
+ Sometimes, the Romanization or English name of the artist is always used, even though the artist also has a name in Japanese. In this case, the Japanese + name should be marked as Unspecified so that it won't be used as a display name. +

+

For more guidelines regarding Romanization, check the wiki article.

+

+ If the entry has multiple names in one language and it's not clear which one of those should be the primary one, you should refer to official information, + such as product packaging. If the name in the album's cover doesn't conflict with the other rules on the site, that name should be preferred as the primary one. +
+ However, keep in mind that sometimes the official information contains the same name in multiple languages combined into one. In this case you should enter + the name separately for each language. For example, if the song is uploaded to Niconico with the name 秘密警察 - Himitsu Keisatsu, you should + enter both names separately: 秘密警察 in the Non-English field and Himitsu Keisatsu in the Romanized field. +

+

+ Registered users can set their display language so that they will always see the names in that language, if available. + Unregistered users and users who haven't set their display language will see the original names, which may be either English or something else. + For example, if the non-English name of an artist is ナナホシ, the romanized name is nanahoshi, and the default language option is set to "Original", then by default, users will see ナナホシ unless they have chosen to prefer romanized names instead. +

+

Generally, there is no need to add composite names, and those names should be broken into separate fields.

+

is incorrect: every field should contain only one name.

+ +

Entry statuses

+

+ Entries that are marked as drafts have missing or incomplete information. The draft status is indicated on the entry's page. + All entries should meet certain requirements before they can be marked as finished/completed. For now, these requirements are only suggestions, but + in the future, they may be enforced more strictly. Of course, when editing an entry that meets these requirements, + you can still mark it as draft if you feel that the entry needs further attention. +

+ After the entry has been finished, it can be approved by a trusted user or staff member. Most properties of approved entries cannot be edited by users with + normal permissions. Tags can still be edited by anyone and comments can be added. Trusted users are also able to change the status back from Approved to Finished or even Draft. +
+ A note for trusted users regarding entry approval: You're encouraged to mark the entry as Approved when you've checked that all the necessary information is provided and + it's correct. However, songs shouldn't be marked as approved until they have lyrics. When approving albums, make sure that ALL the artists have been added. +

+ +
+ +

アーティスト、プロデューサー、そしてVOCALOID

+ + +

アーティストの種類

+

+ 全てのアーティストは、彼もしくは彼女が作品の中で何の役割を担う人なのか判断するための分類がなされています。 + その役割はアルバムや曲ごとに違ってくる可能性は有りますが、多くの場合、アーティストは一つの役割に深く関わります。 +

+
    +
  • "プロデューサー" - 曲(VOCALOID曲の場合、通常はボーカルにVOCALOIDを使用します)を作る人。必ずしもオリジナルの作曲家とは限りません。
  • +
  • "アニメーター" - 主にアニメーションPVを制作する人。
  • +
  • "サークル" - 同人イベント(コミケやボーマス等)で自主出版・自主販売を行うグループ。
  • +
  • "レーベル" - その他のアーティスト達のアルバムを出版する営利会社。
  • +
  • "その他のグループ" - レーベルからアルバムをリリースするグループ。
  • +
  • "その他のボーカル" - 人間のシンガー(ニコニコにおける歌い手)。
  • +
  • "その他の個人" - 作詞家やイラストレーターなど、その他の人々。
  • +
+ + +

アーティスト画像

+

+ アーティストに画像を追加する時は、その画像が彼もしくは彼女の作品(曲)にではなくアーティスト自身に関連深いものを選ぶよう心掛けて下さい。アーティストの項目にアルバムのジャケット画像をアップロードしないこと。とは言え、アーティストのメイン画像を選ぶに当たって共通のルールがあるわけではありません。アーティスト自身の写真や公式ロゴが望ましいです。アーティストに十分関連する画像であればいくつ追加しても構いません。 +
+ 画像の使用に問題がある場合は削除します。 +
+ もし適した画像が無ければ、アーティストの公式プロフィールにあるどんな画像でも――例えばツイッター等の画像でも可能です。 +

+ +

Should I create an entry for artist's personal circle?

+

+ Many Vocaloid artists have a personal band/circle through which they publish their albums. Often there are no other members in this circle. + In this case, it's not necessary to create a separate entry for that personal circle, but it's not wrong either, and might be a good idea + if the artist has separate websites/blogs for himself and his circle. If there is no separate entry for the circle, the circle name + can be included as an alias for that artist. In that case, it would be advisable to mention this in the notes. +

+ + +

アーティストの項目が完成されるには、以下の基準を満たしていなければなりません:

+
    +
  • アーティストは少なくとも一つは不明な言語によらない名前を持っていること
  • +
  • アーティストの種類が“不明”ではないこと
  • +
  • アーティストに説明、または一つ以上の外部リンク(wiki、ニコニコのマイリスト等)が記載されていること
  • +
+ +
+

Albums

+

To assign producers and vocalists to songs in an album, add artists to the album first and then click on each added song in the song list to show a quick selection dialog.

+

+ Please note that the album must have at least one song with synthesized vocals (Vocaloid, UTAU and other voice synthesizers all count). + Cover albums consisting only of human-sung covers of Vocaloid songs are unfortunately not allowed. Consider checking UtaiteDB. + Unrelated albums may be deleted from VocaDB. +

+ +

A note about fanmade compilations

+

+ Unofficial, fanmade compilation albums (bootlegs) where the authors don't have permissions to use the songs are generally not allowed. + There can be some exceptions if the albums are widely known, for example the Hatsune Miku 1st Song Album. + In any case, the staff members reserve the right to remove these entries should they deem it necessary. +

+ +

Adding artists for albums

+

+ When linking artists to albums, at the very least you should add the responsible circle (usually there's only one) and vocalists. + Individual producers and the associated record label (if any) can be added as well. At the moment, all artist types may be linked to albums. +
+ Always enter artists individually. Artists such as "producer feat. vocalist", meaning "cosMo feat. Hatsune Miku", are not needed. + The system will produce these "artists strings" automatically. +
+ Sometimes, it's necessary to credit people that aren't in the database, and it doesn't make sense to add an entry for them. + For example, if the song in the database is a remix/cover and the original isn't a Vocaloid song, the original composer doesn't need to be added to the database, + but should be credited nevertheless. + These "extra artists" can then be assigned into roles just like artists that are in the database. However, for all Vocaloid-related artists, + it's necessary to create an entry for that artist. +

+ +

Album types

+ The possible album types on VocaDB are: +
    +
  • "Original album" - Album that consists mostly of previously unpublished songs.
  • +
  • "Single" - Contains one or two individual tracks. Alternate versions (instrumentals and remixes) are usually not counted.
  • +
  • "EP" - Meaning extended play. Contains 3-4 individual tracks. Alternate versions (instrumentals and remixes) are usually not counted.
  • +
  • "Split album" - Collaboration between two or more (but usually just two) equal artists, where all artists have roughly the same number of songs.
  • +
  • "Compilation" - Collection of previously published songs, gathered from one or more earlier albums. For example, "best of" collections.
  • +
  • "Video" - Disc containing mostly music videos, usually a DVD or Blu-ray.
  • +
  • "Other" - For albums that don't fit into anything above.
  • +
+

+ These are only suggestions; use common sense when determining album type. + For example, an album with a single song that by itself is as long as a regular album (the definition on Wikipedia says over 25 minutes) + can be counted as "original album" instead of single. + + If unsure of which type to use, try to find out how the artists themselves call the album. + For more elaborate descriptions, please refer to Wikipedia, Discogs or Musicbrainz. +

+ +

Completed album entries should meet the following criteria:

+
    +
  • The album has at least one name whose language option isn't "Unspecified".
  • +
  • All artists responsible for the album are specified. Usually there should be at least one producer and one vocalist. Whenever it makes sense, each of those artists should have an entry in the database. If you can't locate some of the artists, please leave the entry as draft.
  • +
  • Album type isn't "Unspecified". Try to find the correct type.
  • +
  • The album's release date, at least release year, is specified.
  • +
  • The album has a complete tracklist.
  • +
+ +
+

Songs

+

Song entries contain information about authors and vocalists (see "Artists" tab), PVs and lyrics.

+

is incorrect: all additional info (vocalists, authors, PV) should be added to appropriate fields. A song title should contain nothing more than the name itself.

+ +

Adding artists for songs

+

+ Most Vocaloid songs have one producer and one or more vocalists (i.e. Vocaloids). + Circles, labels and other groups are generally NOT credited for individual songs, unless it's clear that the whole group worked on that song. + Always prefer adding individual people to songs over adding circles or groups if possible. Note that different sources may swap producer and group names. +

+

+ When tagging Vocaloids, you should use the information provided by the artist, not guess yourself. If the artist says the singer is Hatsune Miku, you should + add Miku's original voicebank, even if you think the singer is actually an Append. Sometimes, the artist says the singer is Append, but doesn't specify which Append. + In this case, you should choose Append (unknown). Do not guess unless you are sure. +

+

+ In the case of a remix or cover, the original composer/lyricist of the song does not need to be credited if the + original song is in the database. +
+ If the original is a Vocaloid song, or a song featuring another voice synthesizer, add the original song to the database and link the original to the derived version, + or if the original is not a Vocaloid song, add the composer as an "extra artist" (see album guide for more information). + Remember to set this artist's role properly to Composer/Lyricist. +

+ +

Song types

+

+ The most important distinction is between Original and others. Original always means that the song is a completely original production. + If the song uses material from existing sources, it's a derived work and not original. +

+

+ For instrumental songs, you should use the original song type if the instrumental version is the original. + In this case, you should indicate that the song is an instrumental by tagging it with the + @Html.ActionLink("instrumental tag", "DetailsById", "Tag", new { id = instTagId, slug = "instrumental" }, null). + The instrumental song type is for instrumental versions of original songs. + Usually, if the song is an original work and not a derivative, it should be marked as original. + One exception to this rule is drama PVs. Because drama PVs are not songs, they should be separated from songs by using the Drama PV song type, even if the PV is + a completely original production, like drama PVs usually are. +

+ +

Song embeds and PVs

+

+ Songs may contain any number of embedded media files, such as Promotional Videos (PVs) on Niconico (NND), YouTube, Bilibili or Vimeo, or audio streams on Piapro or SoundCloud. + All embeds should have the same version of the song, meaning the audio should be the same. Shortened versions are sometimes accepted. Do not add karaoke versions as embeds to + the original song: either add the karaoke version as a link or create a new entry. Remixes should always be separate entries.
+ Very often, Vocaloid artists themselves upload their songs to NND, and sometimes to YouTube or SoundCloud as well, in which case these uploads are called "original". + Original uploads, if available, are highly preferred to any others.
+ If a PV made for a song is remarkable or well-known in some way, you're encouraged to create a separate entry for that PV and link it to the original song. +

+
    +
  • "Original" - the first video with that song, usually uploaded by the author himself (may lack any animation). There may be multiple Original PVs if all are uploaded by the producer.
  • +
  • "Reprint" - should be identical to "Original"; most often it's a YouTube reprint of an original NND video, uploaded by someone other than the artist.
  • +
  • "Other" - any following PV with animation, translated subtitles, MMD versions, etc. May be better known than the original upload. Note that the audio should still be the same as the original.
  • +
+

+ Important note about raw file embeds: VocaDB supports embedding raw links to .mp3 files. For security reasons, the usage of this feature is limited to trusted users only. + Only mp3 files are supported for now because it's the most widely supported format, but in the future we might allow other audio and video files as well. +
+ To do that (assuming, you're a trusted user), simply input a URL pointing to a .mp3 file. + The file must be publicly accessible and authorized by the artist - we do not support illegal distribution. + If possible, try to make sure the artist has allowed embedding the file on other sites. + Whenever adding a raw file as media, be sure to include a link to the official webpage where the link is from. If there is no such webpage, it's better not to add the media. +
+ Finally, raw file embeds should mostly be used as fallback when the song isn't available on any other service. Especially if the song is officially on YouTube or SoundCloud, raw file embeds + should not be added. Services like YouTube and SoundCloud have worldwide content delivery networks (CDNs) that most likely offer better performance + than the artist's own server hosting the file. +

+ +

Completed song entries should meet the following criteria:

+
    +
  • The song has at least one name whose language option isn't "Unspecified".
  • +
  • The song has at least one artist (usually, there should be at least one producer and one vocalist).
  • +
  • Song type isn't "Unspecified".
  • +
+ +
+

Tags

+

+ Tags are free-form metadata that can be added to all entry types. Tags allow users to link entries together by any + properties they can come up with, not being limited to the options provided by the system. Examples of tags + are genres, presentation, languages and themes. Tags can be edited more freely than other properties and some of them may even be considered subjective. + Therefore, tags are based on voting. Any user may add tags to an entry and vote on existing tags. Only the most popular tags + will be displayed for that entry. +

+ +

What to tag then?

+

+ Generally, you should avoid tagging with information that is already provided by more specialized + fields. For example, albums or songs with artist names is redundant because the artists list already handles this better. + Likewise, tagging cover songs with "cover" is useless if the song classification is already cover. Of course, albums + have no such classification, so that tag might be relevant for an album (or artist, if that artist is known for making covers). +

+

+ Most tags should be objective, meaning their validity isn't based on the listener's subjective opinion. For example, "beautiful" + is a poor tag because beauty is highly subjective. "Calm" is a slightly better tag, since that can be defined by some objective characteristics, + although it's still mostly subjective. Please prefer creating private song lists for highly subjective properties. +

+ +

Tags versus pools

+

+ Trusted users are able to create public songlists called pools. Pools can be used for largely the same purpose as tags, grouping + songs together based on some common theme. There are a few differences between tags and songlists. +

+
    +
  • Pools are not voted on, unlike tags. All pools are equal.
  • +
  • Only trusted users are able to edit pools. Any user can vote on tags. Thus tags are easier to use, but also more unreliable.
  • +
  • Pools may contain only songs. Tags can be applied to albums and artists as well.
  • +
  • Songs in a pool can be ordered. Songs with a specified tag are unordered.
  • +
  • You can add notes to songs in a pool.
  • +
+ +
+

Other guidelines

+
    +
  • Applies to trusted users: when merging duplicate entries, prefer merging the newer entry to the older one, unless the new entry is finalized while the older one is draft.
  • +
+ +
+
+ +@section BodyScripts { + +} diff --git a/VocaDbWeb.Core/Views/Help/Index.zh-Hans.cshtml b/VocaDbWeb.Core/Views/Help/Index.zh-Hans.cshtml new file mode 100644 index 0000000000..b1befeb9ec --- /dev/null +++ b/VocaDbWeb.Core/Views/Help/Index.zh-Hans.cshtml @@ -0,0 +1,452 @@ +@using VocaDb.Web.Helpers +@inherits VocaDb.Web.Code.VocaDbPage + +@{ + PageProperties.Title = "Help / About"; + var contributors = new[] { + "digited", "Zakudono", "salanos", "kyllakivi", "Goship", "mi.chan", "gaminat", "Wiktor", "Shiroizu", "Ota-kun", "Hissori", "nebulasresolution", "megaki11", "faerimagic" + }; + int freeTagId = ViewBag.FreeTagId; + int instTagId = ViewBag.InstrumentalTagId; +} + +
+ + + +
+

概况

+

+ VocaDB是一个免费的互联网数据库。本网站旨在提供精确的Vocaloid艺术家及其所参与制 + + 作的专辑、歌曲的相关信息。同时,我们通过推广Vocaloid艺术家的作品使得他们为世人所 + + 知,并在条件允许的前提下提供购买他们作品的方式、渠道。 +

+
+

本站用户的权利(包括但不局限于以下五项)

+
    +
  • 本站的用户(被剥夺编辑权限的用户除外)都有权在遵守编辑指南的前提下添加、编辑词条信息,还可以为词条添加标签及备注。
  • +
  • 设置自己喜欢的界面语言。
  • +
  • 在自定义歌单里添加自己喜欢的歌曲并为歌曲打分。
  • +
  • 使用收藏夹记录自己购买过的Vocaloid专辑。
  • +
  • 发表评论、在讨论区讨论相关问题。
  • +
+ + 备注:Vocaloid艺术家可以在自己的词条已被添加的前提下申请账户认证,将自己的VocaDB账户与词条进行绑定。 + +
+

关于错误信息

+

+ 如果您在浏览词条的过程中发现错误信息,您可以在通过点击页面上的“报告错误信息”向我们反映。当然了,我们更建议您在发现错误之后自己对词条进行修改。 +

+ +

关于转载本网站上的词条信息

+

+ VocaDB上的一切词条信息属于开放资料(Open Data),您可以在标明来源的前提下进行转载。 + 如有疑问的话请联系站长或管理员。 +

+ +

隐私政策

+

+ VocaDB尊重本站用户的隐私。我们任何情况下都不会与任何第三方分享您的个人信息: +

    +
  • 电子邮箱
  • +
  • IP地址
  • +
  • 账户密码
  • +
+ + 同时,我们保证您能以匿名的方式添加、编辑词条信息。 +

+ +
+

联系我们(当前只支持用英文进行联系)

+ support[at]vocadb.net
+ Code on Github
+
+ +
+ + + Wikipedia article
+ Vocaloid wiki
+
+

Short description

+

Vocaloid is a unique subculture of user-created content (music, illustrations, videos, software, games, cosplay, events) based on voice synthesizers and their mascots.

+

Vocaloid synthesizer software is a digital musical instrument to create human vocals, allowing anyone to have their personal virtual singer.

+

The first Vocaloid "singers," Leon and Lola, were released in 2004 in the English language, but weren't successful. Vocaloid technology became popular in 2007 in Japan with the Japanese voicebank "Hatsune Miku" (first for the VOCALOID2 engine in Japan, second in the world after Sweet Ann).

+

Being a great and fresh combination of a decent V2 voicebank and a snazzy boxart illustration, Hatsune Miku gained unexpected, unprecedented and massive success and became the symbol of all Vocaloid technology and subculture.

+

Why in Japan?

+
    +
  • The simplest language for synthesis in the world, Japanese has syllable-based phonetics as well as no accents and no rhyme concept in poetry
  • +
  • Popular anime style (moe catalyst)
  • +
  • A lot of dōjin (self-published) activity
  • +
  • Traditional high-pitched female vocal
  • +
  • Diverse and active musical scene--a lot of indie musicians
  • +
  • Pop-idols and virtual pop-star concepts in anime (Sharon Apple in "Macross Plus", 1994)
  • +
+

Japan happened to be the best country for a popularity breakthrough of the Vocaloid technology.

+

Features of Vocaloid

+
    +
  • Vocaloid synthesizer software is treated as a musical instrument, so it is free to use for any purpose.
  • +
  • Piapro license initiative from Crypton Future Media permits the non-commercial usage of their mascot images.
  • +
  • Decentralization. No one commands the community what to do and how to do it. All content (music, videos, illustrations) is created by users themselves for their own purposes (fun, self-expression, selling).
  • +
  • Collaboration of song writers, musicians, illustrators, animators and programmers.
  • +
  • Self-promotion of Vocaloids and producers. Basically, the community chose Miku and made her popular (as well as GUMI).
  • +
  • Sharing of music and support principles. Most Vocaloid music and videos are freely available on NND, Piapro and YouTube, and still the community supports producers by buying their albums. It is possible for and being done by people who live outside Japan, too. Musical instruments, hardware, software and learning cost a lot, and producers are mostly indie musicians, so every sold CD is important for them. If you really like the music that was done by a Vocaloid producer, please support him.
  • +
  • Concerts are also a large part of the Vocaloid subculture. Vocaloid mascots are projected onto a transparent screen and perform "live" with a real band.
  • +
+

PV

+

Promotional Videos are being used to attract listeners to Vocaloid songs, becoming part of the songs, the form of art by itself and a part of Vocaloid subculture.

+

Japanese flash video streaming service Niconico (originally called Nico Nico Douga or NND) greatly helped in the popularization of Vocaloid in Japan and is used for uploading new Japanese Vocaloid songs and videos, live streaming of concerts, presentations and other events.

+

Sound quality of videos on YouTube and Niconico

+

NND videos have various sound quality - from MP3 CBR 64 kbps in "economy" mode to AAC VBR ~370 kpbs, but generally it is higher than on YouTube, that has AAC VBR ~128 kbps in most videos (same for all video resolutions).

+

MMD

+

MikuMikuDance (MMD for short) is a free, popular and powerful 3D animation software made by Yu Higuchi (HiguchiM) for Vocaloid Promotional Videos.

+

MMD is a part of the success of Hatsune Miku and is also a subculture itself, having MMD Cup championships and being used to produce animation for memes, Touhou, IDOLM@STER, etc.

+

Producer

+ Description on Vocaloid Wiki +

"Producer" is the term used for any person that produces Vocaloid-related audiovisual material, generally original music or fanmade PVs.

+

Circles and Labels

+

Dōjinshi circle is a group of authors of self-published works that may act as a label for dōjin events (Comiket or Vocaloid M@ster).

+

Record label is a company that provides professional studio services (instruments, recording, mastering), production of CDs, various promotion and copyright protection. The most well known Vocaloid label is KarenT, created by Crypton Future Media.

+
+ +
+

General principles

+
    +
  • Avoid submitting entries that already exist in the database. Duplicates have to be deleted or merged by the staff.
  • +
  • When editing entries that are finalized or approved (not drafts), make sure you have a good reason, and preferably state that reason in the edit notes unless it's obvious. If you're unsure of whether your edit is appropriate and necessary, ask in the comments, on Twitter, in discussions, or by email.
  • +
  • This is a database for Vocaloid content and related voice synthesizers such as UTAU. Instrumental songs and human-sung covers are allowed when they appear on albums together with Vocaloid songs, or there's a significant number of Vocaloid covers of those songs. Albums without Vocaloid vocals are generally not relevant to our focus. Completely unrelated content will be deleted.
  • +
  • Download links to illegal file-sharing sites that provide no useful information (like Mediafire/4Shared/Torrents) are NOT allowed. Links to informative blogs/forums/wikis are allowed but should be categorized as "Unofficial" or "Reference". The staff members reserve the right to remove any links deemed inappropriate.
  • +
  • Direct download links to media files (such as .mp3) are generally not allowed, even if they're official and legal. If the song/video is officially distributed, link to the artist's official download page (for example on piapro) instead.
  • +
  • Creating multiple user accounts is not allowed. Any kind of abuse, manipulation of ratings or spamming will not be tolerated.
  • +
  • VocaDB strives for objectivity. Advertising in comments or entry descriptions is forbidden without prior approval by a staff member. Verified artists are allowed to post advertisements in the "personal description" field for entries they manage.
  • +
+ +

Language

+

+ The official language of the site is English. Please prefer using English for all non-translateable information. + For example, when adding external links, the descriptions of those links should be in English. Discussions and tags should also be mainly in English. + Other languages are allowed with a good reason, for example, if a specific piece of information cannot be properly translated into English.
+ We're looking into the possibility of allowing other languages in the future, at least for descriptions and discussions. +

+ +

How can I add new producers, albums and songs?

+

Register and use links in main navigation panel or buttons in producer, album and song list pages.

+ +

Names and translations

+

+ All names should primarily be written in the original language, which usually means Japanese, but it can also be English or Korean, for example. + The system supports 4 language options: +

+
    +
  • "Non-English" covers original names in all languages that aren't English or romanized, for example Japanese.
  • +
  • "Romanized" is for foreign original names written in the Latin (western) alphabet. Usually this means Romaji.
  • +
  • "English" is for official names in English and known translations of foreign names.
  • +
  • "Unspecified" is for names where the language doesn't matter or it's unknown. Unspecified names will never be used as display names of an entry if a name of a different option is provided.
  • +
+

+ If the original language is English, the name should be written in the English language field. Otherwise, it's written to the "Non-English" language field. + If those names have commonly used romanizations or English translations, those can be entered as well to make it easier for the international audience to find + and understand those names. Other names, for example translations to other languages, should be marked as "Unspecified" according to current rules. + Sometimes, an entry may have multiple names in one language, for example a producer's real name and artist name. + In this case, the additional names can be marked as "Unspecified" so that they won't be used as display names regardless of the entry's display option. +
+ Sometimes, the Romanization or English name of the artist is always used, even though the artist also has a name in Japanese. In this case, the Japanese + name should be marked as Unspecified so that it won't be used as a display name. +

+

For more guidelines regarding Romanization, check the wiki article.

+

+ If the entry has multiple names in one language and it's not clear which one of those should be the primary one, you should refer to official information, + such as product packaging. If the name in the album's cover doesn't conflict with the other rules on the site, that name should be preferred as the primary one. +
+ However, keep in mind that sometimes the official information contains the same name in multiple languages combined into one. In this case you should enter + the name separately for each language. For example, if the song is uploaded to Niconico with the name 秘密警察 - Himitsu Keisatsu, you should + enter both names separately: 秘密警察 in the Non-English field and Himitsu Keisatsu in the Romanized field. +

+

+ Registered users can set their display language so that they will always see the names in that language, if available. + Unregistered users and users who haven't set their display language will see the original names, which may be either English or something else. + For example, if the non-English name of an artist is ナナホシ, the romanized name is nanahoshi, and the default language option is set to "Original", then by default, users will see ナナホシ unless they have chosen to prefer romanized names instead. +

+

Generally, there is no need to add composite names, and those names should be broken into separate fields.

+

is incorrect: every field should contain only one name.

+ +

Entry statuses

+

+ Entries that are marked as drafts have missing or incomplete information. The draft status is indicated on the entry's page. + All entries should meet certain requirements before they can be marked as finished/completed. For now, these requirements are only suggestions, but + in the future, they may be enforced more strictly. Of course, when editing an entry that meets these requirements, + you can still mark it as draft if you feel that the entry needs further attention. +

+ After the entry has been finished, it can be approved by a trusted user or staff member. Most properties of approved entries cannot be edited by users with + normal permissions. Tags can still be edited by anyone and comments can be added. Trusted users are also able to change the status back from Approved to Finished or even Draft. +
+ A note for trusted users regarding entry approval: You're encouraged to mark the entry as Approved when you've checked that all the necessary information is provided and + it's correct. However, songs shouldn't be marked as approved until they have lyrics. When approving albums, make sure that ALL the artists have been added. +

+ +
+

Artists, producers and Vocaloids

+

Artist types

+

+ Every artist has a classification which also determines the artist's default roles. + Roles can be overridden per album and per song-basis, but in many cases the artist is involved only in one role. +

+
    +
  • "Producer" - the person who created the song (and in the case of a Vocaloid song, usually provided the vocals as well using Vocaloid). Not necessarily the original composer.
  • +
  • "Animator" - person who primarily animates PVs.
  • +
  • "Illustrator" - person who primarily draws static illustrations.
  • +
  • "Lyricist" - person who primarily composes lyrics for songs.
  • +
  • "Circle" - group that self-publishes and sells albums only at doujin events (such as Comiket or Vocaloid Master).
  • +
  • "Label" - a commercial company that publishes albums for other artists.
  • +
  • "Other group" - group that releases albums via Labels.
  • +
  • "Other vocalist" - human singers (NND vocalists/Utaite) and fan-made voice configurations (derivatives).
  • +
  • "Other voice synthesizer" - voice synthesizer voicebanks (machine voices) that aren't Vocaloid, UTAU or CeVIO.
  • +
  • "Other individual" - other people such as instrumentalists.
  • +
+ +

Artist pictures

+

+ When adding pictures to artists, keep in mind all the pictures should be related to the artist himself, not his works. Do not upload album covers for artist entries. +
+ That said, there is no common rule for choosing the main picture of an artist. Photos of the artist himself as well as official logos are preferred. + You may upload any number of these pictures as additional pictures for the artist, provided that they're relevant enough. + Copyrighted pictures or pictures of artists may be taken down if the copyright holder requests it. +
+ If no better picture is provided, any picture found on one of the artist's official profiles, for example on Twitter, is acceptable as well. +

+ +

Should I create an entry for artist's personal circle?

+

+ Many Vocaloid artists have a personal band/circle through which they publish their albums. Often there are no other members in this circle. + In this case, it's not necessary to create a separate entry for that personal circle, but it's not wrong either, and might be a good idea + if the artist has separate websites/blogs for himself and his circle. If there is no separate entry for the circle, the circle name + can be included as an alias for that artist. In that case, it would be advisable to mention this in the notes. +

+ +

Completed artist entries should meet the following criteria:

+
    +
  • The artist has at least one name whose language option isn't "Unspecified".
  • +
  • Artist type isn't "Unspecified".
  • +
  • The artist has a description OR at least one external link (to a wiki, artist's NND MyList etc.)
  • +
+ +
+

Albums

+

To assign producers and vocalists to songs in an album, add artists to the album first and then click on each added song in the song list to show a quick selection dialog.

+

+ Please note that the album must have at least one song with synthesized vocals (Vocaloid, UTAU and other voice synthesizers all count). + Cover albums consisting only of human-sung covers of Vocaloid songs are unfortunately not allowed. Consider checking UtaiteDB. + Unrelated albums may be deleted from VocaDB. +

+ +

A note about fanmade compilations

+

+ Unofficial, fanmade compilation albums (bootlegs) where the authors don't have permissions to use the songs are generally not allowed. + There can be some exceptions if the albums are widely known, for example the Hatsune Miku 1st Song Album. + In any case, the staff members reserve the right to remove these entries should they deem it necessary. +

+ +

Adding artists for albums

+

+ When linking artists to albums, at the very least you should add the responsible circle (usually there's only one) and vocalists. + Individual producers and the associated record label (if any) can be added as well. At the moment, all artist types may be linked to albums. +
+ Always enter artists individually. Artists such as "producer feat. vocalist", meaning "cosMo feat. Hatsune Miku", are not needed. + The system will produce these "artists strings" automatically. +
+ Sometimes, it's necessary to credit people that aren't in the database, and it doesn't make sense to add an entry for them. + For example, if the song in the database is a remix/cover and the original isn't a Vocaloid song, the original composer doesn't need to be added to the database, + but should be credited nevertheless. + These "extra artists" can then be assigned into roles just like artists that are in the database. However, for all Vocaloid-related artists, + it's necessary to create an entry for that artist. +

+ +

Album types

+ The possible album types on VocaDB are: +
    +
  • "Original album" - Album that consists mostly of previously unpublished songs.
  • +
  • "Single" - Contains one or two individual tracks. Alternate versions (instrumentals and remixes) are usually not counted.
  • +
  • "EP" - Meaning extended play. Contains 3-4 individual tracks. Alternate versions (instrumentals and remixes) are usually not counted.
  • +
  • "Split album" - Collaboration between two or more (but usually just two) equal artists, where all artists have roughly the same number of songs.
  • +
  • "Compilation" - Collection of previously published songs, gathered from one or more earlier albums. For example, "best of" collections.
  • +
  • "Video" - Disc containing mostly music videos, usually a DVD or Blu-ray.
  • +
  • "Other" - For albums that don't fit into anything above.
  • +
+

+ These are only suggestions; use common sense when determining album type. + For example, an album with a single song that by itself is as long as a regular album (the definition on Wikipedia says over 25 minutes) + can be counted as "original album" instead of single. + + If unsure of which type to use, try to find out how the artists themselves call the album. + For more elaborate descriptions, please refer to Wikipedia, Discogs or Musicbrainz. +

+ +

Completed album entries should meet the following criteria:

+
    +
  • The album has at least one name whose language option isn't "Unspecified".
  • +
  • All artists responsible for the album are specified. Usually there should be at least one producer and one vocalist. Whenever it makes sense, each of those artists should have an entry in the database. If you can't locate some of the artists, please leave the entry as draft.
  • +
  • Album type isn't "Unspecified". Try to find the correct type.
  • +
  • The album's release date, at least release year, is specified.
  • +
  • The album has a complete tracklist.
  • +
+ +
+

Songs

+

Song entries contain information about authors and vocalists (see "Artists" tab), PVs and lyrics.

+

is incorrect: all additional info (vocalists, authors, PV) should be added to appropriate fields. A song title should contain nothing more than the name itself.

+ +

Adding artists for songs

+

+ Most Vocaloid songs have one producer and one or more vocalists (i.e. Vocaloids). + Circles, labels and other groups are generally NOT credited for individual songs, unless it's clear that the whole group worked on that song. + Always prefer adding individual people to songs over adding circles or groups if possible. Note that different sources may swap producer and group names. +

+

+ When tagging Vocaloids, you should use the information provided by the artist, not guess yourself. If the artist says the singer is Hatsune Miku, you should + add Miku's original voicebank, even if you think the singer is actually an Append. Sometimes, the artist says the singer is Append, but doesn't specify which Append. + In this case, you should choose Append (unknown). Do not guess unless you are sure. +

+

+ In the case of a remix or cover, the original composer/lyricist of the song does not need to be credited if the + original song is in the database. +
+ If the original is a Vocaloid song, or a song featuring another voice synthesizer, add the original song to the database and link the original to the derived version, + or if the original is not a Vocaloid song, add the composer as an "extra artist" (see album guide for more information). + Remember to set this artist's role properly to Composer/Lyricist. +

+ +

Song types

+

+ The most important distinction is between Original and others. Original always means that the song is a completely original production. + If the song uses material from existing sources, it's a derived work and not original. +

+

+ For instrumental songs, you should use the original song type if the instrumental version is the original. + In this case, you should indicate that the song is an instrumental by tagging it with the + @Html.ActionLink("instrumental tag", "DetailsById", "Tag", new { id = instTagId, slug = "instrumental" }, null). + The instrumental song type is for instrumental versions of original songs. + Usually, if the song is an original work and not a derivative, it should be marked as original. + One exception to this rule is drama PVs. Because drama PVs are not songs, they should be separated from songs by using the Drama PV song type, even if the PV is + a completely original production, like drama PVs usually are. +

+ +

Song embeds and PVs

+

+ Songs may contain any number of embedded media files, such as Promotional Videos (PVs) on Niconico (NND), YouTube, Bilibili or Vimeo, or audio streams on Piapro or SoundCloud. + All embeds should have the same version of the song, meaning the audio should be the same. Shortened versions are sometimes accepted. Do not add karaoke versions as embeds to + the original song: either add the karaoke version as a link or create a new entry. Remixes should always be separate entries.
+ Very often, Vocaloid artists themselves upload their songs to NND, and sometimes to YouTube or SoundCloud as well, in which case these uploads are called "original". + Original uploads, if available, are highly preferred to any others.
+ If a PV made for a song is remarkable or well-known in some way, you're encouraged to create a separate entry for that PV and link it to the original song. +

+
    +
  • "Original" - the first video with that song, usually uploaded by the author himself (may lack any animation). There may be multiple Original PVs if all are uploaded by the producer.
  • +
  • "Reprint" - should be identical to "Original"; most often it's a YouTube reprint of an original NND video, uploaded by someone other than the artist.
  • +
  • "Other" - any following PV with animation, translated subtitles, MMD versions, etc. May be better known than the original upload. Note that the audio should still be the same as the original.
  • +
+

+ Important note about raw file embeds: VocaDB supports embedding raw links to .mp3 files. For security reasons, the usage of this feature is limited to trusted users only. + Only mp3 files are supported for now because it's the most widely supported format, but in the future we might allow other audio and video files as well. +
+ To do that (assuming, you're a trusted user), simply input a URL pointing to a .mp3 file. + The file must be publicly accessible and authorized by the artist - we do not support illegal distribution. + If possible, try to make sure the artist has allowed embedding the file on other sites. + Whenever adding a raw file as media, be sure to include a link to the official webpage where the link is from. If there is no such webpage, it's better not to add the media. +
+ Finally, raw file embeds should mostly be used as fallback when the song isn't available on any other service. Especially if the song is officially on YouTube or SoundCloud, raw file embeds + should not be added. Services like YouTube and SoundCloud have worldwide content delivery networks (CDNs) that most likely offer better performance + than the artist's own server hosting the file. +

+ +

Completed song entries should meet the following criteria:

+
    +
  • The song has at least one name whose language option isn't "Unspecified".
  • +
  • The song has at least one artist (usually, there should be at least one producer and one vocalist).
  • +
  • Song type isn't "Unspecified".
  • +
+ +
+

Tags

+

+ Tags are free-form metadata that can be added to all entry types. Tags allow users to link entries together by any + properties they can come up with, not being limited to the options provided by the system. Examples of tags + are genres, presentation, languages and themes. Tags can be edited more freely than other properties and some of them may even be considered subjective. + Therefore, tags are based on voting. Any user may add tags to an entry and vote on existing tags. Only the most popular tags + will be displayed for that entry. +

+ +

What to tag then?

+

+ Generally, you should avoid tagging with information that is already provided by more specialized + fields. For example, albums or songs with artist names is redundant because the artists list already handles this better. + Likewise, tagging cover songs with "cover" is useless if the song classification is already cover. Of course, albums + have no such classification, so that tag might be relevant for an album (or artist, if that artist is known for making covers). +

+

+ Most tags should be objective, meaning their validity isn't based on the listener's subjective opinion. For example, "beautiful" + is a poor tag because beauty is highly subjective. "Calm" is a slightly better tag, since that can be defined by some objective characteristics, + although it's still mostly subjective. Please prefer creating private song lists for highly subjective properties. +

+ +

Tags versus pools

+

+ Trusted users are able to create public songlists called pools. Pools can be used for largely the same purpose as tags, grouping + songs together based on some common theme. There are a few differences between tags and songlists. +

+
    +
  • Pools are not voted on, unlike tags. All pools are equal.
  • +
  • Only trusted users are able to edit pools. Any user can vote on tags. Thus tags are easier to use, but also more unreliable.
  • +
  • Pools may contain only songs. Tags can be applied to albums and artists as well.
  • +
  • Songs in a pool can be ordered. Songs with a specified tag are unordered.
  • +
  • You can add notes to songs in a pool.
  • +
+ +
+

Other guidelines

+
    +
  • Applies to trusted users: when merging duplicate entries, prefer merging the newer entry to the older one, unless the new entry is finalized while the older one is draft.
  • +
+ +
+
+ +@section BodyScripts { + +} diff --git a/VocaDbWeb.Core/Views/Home/Chat.cshtml b/VocaDbWeb.Core/Views/Home/Chat.cshtml new file mode 100644 index 0000000000..9be6b3aa1e --- /dev/null +++ b/VocaDbWeb.Core/Views/Home/Chat.cshtml @@ -0,0 +1,10 @@ +@inherits VocaDb.Web.Code.VocaDbPage + +@{ + PageProperties.Title = "IRC chat"; +} + + + +

+For other clients, use the address irc://@Config.SiteSettings.IRCUrl. \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Home/Index.cshtml b/VocaDbWeb.Core/Views/Home/Index.cshtml new file mode 100644 index 0000000000..4925cc1e7a --- /dev/null +++ b/VocaDbWeb.Core/Views/Home/Index.cshtml @@ -0,0 +1,125 @@ +@using VocaDb.Model.Domain +@using VocaDb.Web.Helpers +@using VocaDb.Model.Utils.Search +@using VocaDb.Web.Models.Shared.Partials.Activityfeed +@using VocaDb.Web.Models.Shared.Partials.Album +@using VocaDb.Web.Models.Shared.Partials.Comment +@using VocaDb.Web.Models.Shared.Partials.Shared +@using Res = ViewRes.Home.IndexStrings +@model VocaDb.Model.DataContracts.UseCases.FrontPageContract +@inject LaravelMixHelper LaravelMixHelper +@inject PVHelper PVHelper + +@{ + ViewBag.DisplaySocialLinks = true; + ViewBag.ShowTwitterTimeline = true; + var br = BrandableStrings.Home; +} + +

+ @br.Welcome + @br.WelcomeSubtitle +

+ +@if (Model.NewSongs.Any()) { +

@Res.RecentSongs (@Html.ActionLink(ViewRes.Home.IndexStrings.ViewMore, "Index", "Search", new SearchRouteParams { searchType = EntryType.Song, sort = VocaDb.Model.Service.SongSortRule.AdditionDate, onlyWithPVs = true }, null))

+ +
+
+ +
+
+ @for (var i = 0; i * 4 < Model.NewSongs.Length; ++i) { +
+ @foreach (var song in Model.NewSongs.Skip(i * 4).Take(4)) { +
+ + @if (!string.IsNullOrEmpty(song.ThumbUrl)) { + Cover + } + @song.Name + @song.ArtistString +
+ } +
+ } +
+
+ +
+ +
+ @{ Html.RenderPartial("PVs/_PVContent", Model.FirstSong); } +
+
+} + +
+ +@if (Model.NewAlbums.Any()) { +

@Res.NewAlbums (@Html.ActionLink(Res.ViewMore, "Index", "Search", new SearchRouteParams { searchType = EntryType.Album, sort = VocaDb.Model.Service.AlbumSortRule.ReleaseDate }, null))

+
+ @Html.Partial("Partials/Album/_AlbumThumbs", new AlbumThumbsViewModel(Model.NewAlbums)) +
+} + +@if (Model.TopAlbums.Any()) { +

@Res.TopAlbums (@Html.ActionLink(Res.ViewMore, "Index", "Search", new SearchRouteParams { searchType = EntryType.Album, sort = VocaDb.Model.Service.AlbumSortRule.RatingTotal }, null))

+
+ @Html.Partial("Partials/Album/_AlbumThumbs", new AlbumThumbsViewModel(Model.TopAlbums)) +
+} + +@if (Model.NewEvents.Any()) { +

@Res.RecentEvents

+ @Html.Partial("Partials/Shared/_EventThumbs", new EventThumbsViewModel(Model.NewEvents)) +} + +@{ Html.RenderPartial("Partials/_NewsItems"); } + +
+
+

@Res.RecentActivity (@Html.ActionLink(Res.ViewMore, "Index", "ActivityEntry"))

+ + @foreach (var entry in Model.ActivityEntries) { + @Html.Partial("Partials/Activityfeed/_ActivityEntry", new ActivityEntryViewModel(entry)) + } + + @Html.ActionLink(Res.ViewMore, "Index", "ActivityEntry") +
+
+

@ViewRes.Comment.IndexStrings.RecentComments (@Html.ActionLink(Res.ViewMore, "Index", "Comment"))

+ + @foreach (var comment in Model.RecentComments) { + @Html.Partial("Partials/Comment/_CommentWithEntryVertical", new CommentWithEntryVerticalViewModel(comment, 400)) + } + + @Html.ActionLink(Res.ViewMore, "Index", "Comment") +
+
+ +@section Head { + + +} + +@section BodyScripts { + + + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Home/Partials/_NewsItems.cshtml b/VocaDbWeb.Core/Views/Home/Partials/_NewsItems.cshtml new file mode 100644 index 0000000000..bc003dcdf5 --- /dev/null +++ b/VocaDbWeb.Core/Views/Home/Partials/_NewsItems.cshtml @@ -0,0 +1,30 @@ + + + +

@ViewRes.Home.IndexStrings.News

+ +
+
+
+
+

+ +   + + Avatar + + @HelperRes.ActivityFeedHelperStrings.At + + +

+
+
+

+ +

+
+
+
+ + @ViewRes.SharedStrings.Loading +
\ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Home/Wiki.cshtml b/VocaDbWeb.Core/Views/Home/Wiki.cshtml new file mode 100644 index 0000000000..5faf8185d1 --- /dev/null +++ b/VocaDbWeb.Core/Views/Home/Wiki.cshtml @@ -0,0 +1,5 @@ +@{ + +} + + diff --git a/VocaDbWeb.Core/Views/MikuDbAlbum/Index.cshtml b/VocaDbWeb.Core/Views/MikuDbAlbum/Index.cshtml new file mode 100644 index 0000000000..8f042341ad --- /dev/null +++ b/VocaDbWeb.Core/Views/MikuDbAlbum/Index.cshtml @@ -0,0 +1,69 @@ +@using VocaDb.Web.Helpers +@model VocaDb.Web.Models.MikuDbAlbums.Index + +@{ + PageProperties.Title = "Imported albums"; +} + +@section Toolbar { +} + +@using (Html.BeginForm("ImportOne", "MikuDbAlbum", FormMethod.Post, new { @class = "form form-inline" })) { + +

Download one album from KarenT:

+ @Html.TextBox("AlbumUrl", null, new { size = 30, maxlength = 255 }) + + + +} + +@using (Html.BeginForm("ImportFromFile", "MikuDbAlbum", FormMethod.Post, new { @class = "form form-inline", enctype = "multipart/form-data" })) { + +

Parse from CSV file:

+ + + + +} + + +@using (Html.BeginForm("Index", "MikuDbAlbum", FormMethod.Post, new { @class = "form form-inline"})) { + +

+ Filter: + @Html.TextBoxFor(m => m.TitleFilter) + @Html.DropDownListFor(m => m.Status, new SelectList(Model.AllStatuses, "Key", "Value"), new { @class = "input-medium" }) +   + +

+ +} + +
+ Import new albums, or manage imported albums (if any) using the buttons below. +
+ +@using (Html.BeginForm("PrepareForImport", "MikuDbAlbum")) { + + + + + + + + @foreach (var album in Model.Albums) { + Html.RenderPartial("MikuDbAlbumRow", album); + } +
TitleInfoTracks
+ +} + +@section BodyScripts { + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/MikuDbAlbum/InspectedAlbumRow.cshtml b/VocaDbWeb.Core/Views/MikuDbAlbum/InspectedAlbumRow.cshtml new file mode 100644 index 0000000000..4d89e3202c --- /dev/null +++ b/VocaDbWeb.Core/Views/MikuDbAlbum/InspectedAlbumRow.cshtml @@ -0,0 +1,73 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Album +@using VocaDb.Web.Models.Shared.Partials.Artist +@using VocaDb.Web.Models.Shared.Partials.Shared +@model VocaDb.Model.DataContracts.MikuDb.InspectedAlbum + +
+

@Model.ImportedAlbum.Title

+ @Html.HiddenFor(m => m.ImportedAlbum.Id) + + + + + +
+ Cover picture +

+

+ Existing album:
+ @Html.DropDownListFor(m => m.MergedAlbumId, new SelectList(Model.ExistingAlbums, "Id", "Name", Model.MergedAlbumId), new { id = "mergedAlbumId" }) + +
+ @if (Model.MergedAlbum != null) { + @Html.Partial("Partials/Album/_AlbumLink", new AlbumLinkViewModel(Model.MergedAlbum)) + } else { + (no match) + } +

+
+ @Html.Partial("Partials/Shared/_EntrySearchNameBox", new EntrySearchNameBoxViewModel("albumSearchName")) +
+

+ Language for names: + @Html.LanguageSelectionDropDownListFor(m => m.SelectedLanguage, null, true) +

+ @if (Model.ImportedAlbum.Data.ReleaseYear != null) { +

Release year: @Model.ImportedAlbum.Data.ReleaseYear

+ } +

Artists

+
    + @foreach (var artist in Model.Artists) { +
  • + @artist.Name: + @if (artist.ExistingArtist != null) { + @Html.Partial("Partials/Artist/_ArtistLink", new ArtistLinkViewModel(artist.ExistingArtist)) + } else { + (no match) + } +
  • + } +
+ +

Tracks

+ + + @if (Model.Tracks != null) { +
+
    + @foreach (var track in Model.Tracks) { +
  • + @{ Html.RenderPartial("InspectedAlbumTrack", track); } +
  • + } +
+
+ } else { +

No tracks will be imported.

+ } +
+
diff --git a/VocaDbWeb.Core/Views/MikuDbAlbum/InspectedAlbumTrack.cshtml b/VocaDbWeb.Core/Views/MikuDbAlbum/InspectedAlbumTrack.cshtml new file mode 100644 index 0000000000..9fbb474b6a --- /dev/null +++ b/VocaDbWeb.Core/Views/MikuDbAlbum/InspectedAlbumTrack.cshtml @@ -0,0 +1,14 @@ +@using VocaDb.Web.Helpers +@model VocaDb.Model.DataContracts.MikuDb.InspectedTrack + +@{ var item = Html.BeginCollectionItem("Tracks"); } + + (Disc @Model.ImportedTrack.DiscNum) @Model.ImportedTrack.TrackNum. @Model.ImportedTrack.Title (@Model.ImportedTrack.ArtistString): + @if (Model.ExistingSong != null) { + @Html.HiddenFor(m => m.ExistingSong.Id) + @Html.CheckBoxFor(m => m.Selected) + @Html.ActionLink(Model.ExistingSong.Name + " (" + Model.ExistingSong.ArtistString + ")", "Details", "Song", new { id = Model.ExistingSong.Id }, new { title = Model.ExistingSong.AdditionalNames }) + } else { + (no match) + } +@{ item.Dispose(); } diff --git a/VocaDbWeb.Core/Views/MikuDbAlbum/MikuDbAlbumRow.cshtml b/VocaDbWeb.Core/Views/MikuDbAlbum/MikuDbAlbumRow.cshtml new file mode 100644 index 0000000000..a7e7fa62cd --- /dev/null +++ b/VocaDbWeb.Core/Views/MikuDbAlbum/MikuDbAlbumRow.cshtml @@ -0,0 +1,56 @@ +@using VocaDb.Web.Helpers +@model VocaDb.Model.DataContracts.MikuDb.MikuDbAlbumContract + +@{ + void ImportedAlbumData(VocaDb.Model.DataContracts.MikuDb.ImportedAlbumDataContract album) { + + if (album != null) { +

+ Artists: @string.Join(", ", album.ArtistNames)
+ @if (album.VocalistNames.Any()) { + Vocalists: @string.Join(", ", album.VocalistNames)
+ } + @if (!string.IsNullOrEmpty(album.CircleName)) { + Circle: @album.CircleName
+ } + @if (album.ReleaseYear != null) { + Year: @album.ReleaseYear
+ } + Imported: @Model.Created +

+ } + + } +} + + + @using (Html.BeginCollectionItem("albums")) { + + @Html.HiddenFor(m => m.Id) +

@Model.Title

+ Cover picture +

+ + + Process + + + + Reject + + + + Delete + +
+ + @{ ImportedAlbumData(Model.Data); } + + @if (Model.Data != null && Model.Data.Tracks.Any()) { + foreach (var track in Model.Data.Tracks) { + @track.TrackNum@track.Title
+ } + } + + } + diff --git a/VocaDbWeb.Core/Views/MikuDbAlbum/PrepareForImport.cshtml b/VocaDbWeb.Core/Views/MikuDbAlbum/PrepareForImport.cshtml new file mode 100644 index 0000000000..af314d38b9 --- /dev/null +++ b/VocaDbWeb.Core/Views/MikuDbAlbum/PrepareForImport.cshtml @@ -0,0 +1,30 @@ +@model VocaDb.Model.DataContracts.MikuDb.InspectedAlbum + +@{ + PageProperties.Title = "Processing albums"; + ViewBag.Parents = new[] { + Html.ActionLink("Imported albums", "Index"), + }; +} + +@using (Html.BeginForm("AcceptImported", "MikuDbAlbum")) { + + Html.RenderPartial("InspectedAlbumRow", Model); + + + + + Reject + + +} + +@section BodyScripts { + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Search/Index.cshtml b/VocaDbWeb.Core/Views/Search/Index.cshtml new file mode 100644 index 0000000000..d753e123a6 --- /dev/null +++ b/VocaDbWeb.Core/Views/Search/Index.cshtml @@ -0,0 +1,267 @@ +@using Resources +@using VocaDb.Model.Domain +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using VocaDb.Web.Resources.Domain +@using Res = ViewRes.Search.IndexStrings +@using SharedRes = ViewRes.SharedStrings +@model VocaDb.Web.Models.Search.SearchIndexViewModel +@inject LaravelMixHelper LaravelMixHelper +@inject Login Login + +@section Head { + +} + +@{ + void SearchCategory(EntryType entryType, string title) { +
  • + @title +
  • + } +} + + + +
    + +
    + @{ SearchDropDown("showArtistSearch", "artistSearchViewModel", Translate.ArtistSortRuleNames.ValuesAndNamesStrings); } + @{ SearchDropDown("showAlbumSearch", "albumSearchViewModel", Translate.AlbumSortRuleNames.ValuesAndNamesStrings); } + @{ SearchDropDown("showSongSearch", "songSearchViewModel", Translate.SongSortRuleNames.ValuesAndNamesStrings); } + @{ SearchDropDown("showEventSearch", "eventSearchViewModel", Translate.EventSortRuleNames.ValuesAndNamesStrings); } + +
    + +
    + +
    + +
    + +
    + + + +
    + +
    + +
    + +
    +
    +
    +
    + + +   +
    + +
    +
    + +
    + +
    +
    @ViewRes.SharedStrings.Tag
    +
    + @Html.Partial("Partials/Knockout/_TagFilters", new TagFiltersViewModel(true)) +
    +
    + + @{ Html.RenderPartial("Partials/_ArtistSearchOptions"); } + + @{ Html.RenderPartial("Partials/_AlbumSearchOptions"); } + + @{ Html.RenderPartial("Partials/_SongSearchOptions"); } + + @{ Html.RenderPartial("Partials/_EventSearchOptions"); } + + +
    +
    @Res.TagCategory
    +
    +
    + @Html.Partial("Partials/Knockout/_LockingAutoComplete", new LockingAutoCompleteViewModel("tagCategoryAutoComplete", "categoryName", extraBindings: "clearValue: true")) +
    +
    +
    + + +
    +
    + +
    + +
    + +
    + + + +
    + +
    + +
    + + + +
    +
    + +
    + +
    + +@{ Html.RenderPartial("Partials/_AnythingSearchList"); } + +@{ Html.RenderPartial("Partials/_ArtistSearchList"); } + +
    + @{ Html.RenderPartial("Partials/_AlbumSearchList"); } +
    + +
    + @{ Html.RenderPartial("Partials/_SongSearchList"); } +
    + +
    + @{ Html.RenderPartial("Partials/_EventSearchList"); } +
    + +@{ Html.RenderPartial("Partials/_TagSearchList"); } + + + +@section BodyScripts { + + + + + +} + +@{ + void SearchDropDown(string visibleBinding, string viewModel, Dictionary sortRules) { + @Html.Partial("Partials/Knockout/_SearchDropDown", new VocaDb.Web.Models.Shared.Partials.Knockout.SearchDropDownViewModel(visibleBinding, viewModel, sortRules)) + } +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Search/Partials/_AlbumSearchOptions.cshtml b/VocaDbWeb.Core/Views/Search/Partials/_AlbumSearchOptions.cshtml new file mode 100644 index 0000000000..2674d36460 --- /dev/null +++ b/VocaDbWeb.Core/Views/Search/Partials/_AlbumSearchOptions.cshtml @@ -0,0 +1,30 @@ +@using VocaDb.Model.Service.Search +@using VocaDb.Web.Models.Shared.Partials.Album +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using VocaDb.Web.Models.Shared.Partials.Search +@using VocaDb.Web.Resources.Domain +@using Res = ViewRes.Search.IndexStrings + + +
    +
    +
    @Res.AlbumType
    +
    + @Html.Partial("Partials/Album/_DiscTypesDropdownKnockout", new DiscTypesDropdownKnockoutViewModel("albumType")) +
    +
    + +
    +
    @EntryTypeNames.Artist
    +
    + @Html.Partial("Partials/Knockout/_ArtistFilters", new ArtistFiltersViewModel(artistParticipationStatus: true)) +
    +
    + +
    +
    +
    + @Html.Partial("Partials/Search/_AdvancedFilters", new AdvancedFiltersViewModel(AdvancedSearchFilters.AlbumFilters)) +
    +
    +
    diff --git a/VocaDbWeb.Core/Views/Search/Partials/_AnythingSearchList.cshtml b/VocaDbWeb.Core/Views/Search/Partials/_AnythingSearchList.cshtml new file mode 100644 index 0000000000..f3493832fd --- /dev/null +++ b/VocaDbWeb.Core/Views/Search/Partials/_AnythingSearchList.cshtml @@ -0,0 +1,69 @@ +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using SharedRes = ViewRes.SharedStrings +@using Res = ViewRes.Search.IndexStrings + +
    + + @{ Html.RenderPartial("Partials/_EntryCountBox"); } + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + + + + + + + + + + + + + + + + + +
    + @SharedRes.Name + + @Res.EntryType + + @SharedRes.Tags +
    + + + + + +   + + + @Html.Partial("Partials/Knockout/_DraftIcon", new DraftIconViewModel("status")) + +
    + + +
    + + + + + +
    + + + , + +
    +
    + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + +
    + + diff --git a/VocaDbWeb.Core/Views/Search/Partials/_ArtistSearchList.cshtml b/VocaDbWeb.Core/Views/Search/Partials/_ArtistSearchList.cshtml new file mode 100644 index 0000000000..8eb8e6d2c4 --- /dev/null +++ b/VocaDbWeb.Core/Views/Search/Partials/_ArtistSearchList.cshtml @@ -0,0 +1,69 @@ +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using SharedRes = ViewRes.SharedStrings + +
    + + @{ Html.RenderPartial("Partials/_EntryCountBox"); } + + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + + + + + + + + + + + + + + + + + + + +
    + + @ViewRes.SharedStrings.ArtistName + + + @ViewRes.Search.IndexStrings.ArtistType + @SharedRes.Tags +
    + + + + + +   + + @Html.Partial("Partials/Knockout/_DraftIcon", new DraftIconViewModel("status")) +
    + +
    + + + + @ViewRes.Search.IndexStrings.VoicebankReleaseDate + + +
    + + + , + +
    +
    + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + +
    + diff --git a/VocaDbWeb.Core/Views/Search/Partials/_ArtistSearchOptions.cshtml b/VocaDbWeb.Core/Views/Search/Partials/_ArtistSearchOptions.cshtml new file mode 100644 index 0000000000..be83ffffd1 --- /dev/null +++ b/VocaDbWeb.Core/Views/Search/Partials/_ArtistSearchOptions.cshtml @@ -0,0 +1,21 @@ +@using VocaDb.Model.Service.Search +@using VocaDb.Web.Models.Shared.Partials.Artist +@using VocaDb.Web.Models.Shared.Partials.Search +@using Res = ViewRes.Search.IndexStrings + + +
    +
    +
    @Res.ArtistType
    +
    + @Html.Partial("Partials/Artist/_ArtistTypesDropdownKnockout", new ArtistTypesDropdownKnockoutViewModel("artistType")) +
    +
    + +
    +
    +
    + @Html.Partial("Partials/Search/_AdvancedFilters", new AdvancedFiltersViewModel(AdvancedSearchFilters.ArtistFilters)) +
    +
    +
    diff --git a/VocaDbWeb.Core/Views/Search/Partials/_EventSearchList.cshtml b/VocaDbWeb.Core/Views/Search/Partials/_EventSearchList.cshtml new file mode 100644 index 0000000000..7812a0c874 --- /dev/null +++ b/VocaDbWeb.Core/Views/Search/Partials/_EventSearchList.cshtml @@ -0,0 +1,85 @@ +@using VocaDb.Model.Domain +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using SharedRes = ViewRes.SharedStrings +@using EventRes = ViewRes.Event.DetailsStrings + + + +@{ Html.RenderPartial("Partials/_EntryCountBox"); } + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + + + + + + + + + + + + + + + + + + + + + +
    + + @SharedRes.Name + + + + @SharedRes.Tags + + + @EventRes.OccurrenceDate + + + + + @EventRes.Series + + + + + @EventRes.Venue + + +
    + + + + + + + @Html.Partial("Partials/Knockout/_DraftIcon", new DraftIconViewModel("status")) + +
    + +
    +
    + + + , + +
    +
    + + + + + + +
    + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    diff --git a/VocaDbWeb.Core/Views/Search/Partials/_EventSearchOptions.cshtml b/VocaDbWeb.Core/Views/Search/Partials/_EventSearchOptions.cshtml new file mode 100644 index 0000000000..cec75cd2e2 --- /dev/null +++ b/VocaDbWeb.Core/Views/Search/Partials/_EventSearchOptions.cshtml @@ -0,0 +1,35 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using VocaDb.Web.Resources.Domain +@using Res = ViewRes.Search.IndexStrings +@using EventRes = ViewRes.Event.DetailsStrings + + + +
    +
    +
    @Res.EventCategory
    +
    +
    + @Html.Partial("Partials/Knockout/_DropdownList", new DropdownListViewModel(Translate.ReleaseEventCategoryNames.ValuesAndNamesStrings, "category")) +
    +
    +
    + +
    +
    @EntryTypeNames.Artist
    +
    + @Html.Partial("Partials/Knockout/_ArtistFilters", new ArtistFiltersViewModel(artistParticipationStatus: false)) +
    +
    + +
    +
    @EventRes.OccurrenceDate
    +
    + + - + + +
    +
    +
    diff --git a/VocaDbWeb.Core/Views/Search/Partials/_SongSearchOptions.cshtml b/VocaDbWeb.Core/Views/Search/Partials/_SongSearchOptions.cshtml new file mode 100644 index 0000000000..ccee204418 --- /dev/null +++ b/VocaDbWeb.Core/Views/Search/Partials/_SongSearchOptions.cshtml @@ -0,0 +1,88 @@ +@using VocaDb.Model.Service.Search +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using VocaDb.Web.Models.Shared.Partials.Search +@using VocaDb.Web.Models.Shared.Partials.Song +@using VocaDb.Web.Resources.Domain +@using Res = ViewRes.Search.IndexStrings + + + + +
    +
    +
    @Res.SongType
    +
    +
    + @Html.Partial("Partials/Song/_SongTypesDropdownKnockout", new SongTypesDropdownKnockoutViewModel("songType")) +
    +
    + +
    +
    +
    + +
    +
    @EntryTypeNames.Artist
    +
    + @Html.Partial("Partials/Knockout/_ArtistFilters", new ArtistFiltersViewModel(artistParticipationStatus: true)) +
    +
    + +
    +
    @Res.MoreRecentThan
    +
    + +
    +
    + +
    +
    @Res.MinScore
    +
    + +
    +
    + +
    +
    @Res.ReleaseEvent
    +
    + @Html.Partial("Partials/Knockout/_BasicEntryLinkLockingAutoComplete", new BasicEntryLinkLockingAutoCompleteViewModel("releaseEventAutoComplete", "releaseEvent")) +
    +
    + +
    +
    @Res.ReleaseDate
    +
    + + +
    +
    + +
    +
    @Res.ParentVersion
    +
    + @Html.Partial("Partials/Knockout/_SongLockingAutoComplete", new SongLockingAutoCompleteViewModel("parentVersion")) +
    +
    + +
    +
    +
    + @Html.Partial("Partials/Search/_AdvancedFilters", new AdvancedFiltersViewModel(AdvancedSearchFilters.SongFilters)) +
    +
    +
    + diff --git a/VocaDbWeb.Core/Views/Search/Partials/_TagSearchList.cshtml b/VocaDbWeb.Core/Views/Search/Partials/_TagSearchList.cshtml new file mode 100644 index 0000000000..787adf9bb2 --- /dev/null +++ b/VocaDbWeb.Core/Views/Search/Partials/_TagSearchList.cshtml @@ -0,0 +1,62 @@ +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using SharedRes = ViewRes.SharedStrings +@using Res = ViewRes.Search.IndexStrings + +
    + + @{ Html.RenderPartial("Partials/_EntryCountBox"); } + + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + + + + + + + + + + + + + + + + + +
    + + @SharedRes.Name + + + + @Res.Category + + + @Res.TagUsages + + +
    + + + + + + @Html.Partial("Partials/Knockout/_DraftIcon", new DraftIconViewModel("status")) +
    + +
    +

    +
    +

    +
    + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + +
    + diff --git a/VocaDbWeb.Core/Views/Shared/AlbumPopupContent.cshtml b/VocaDbWeb.Core/Views/Shared/AlbumPopupContent.cshtml new file mode 100644 index 0000000000..63d1a59b1a --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/AlbumPopupContent.cshtml @@ -0,0 +1,48 @@ +@using VocaDb.Model.Domain.Albums +@using VocaDb.Web.Helpers +@model VocaDb.Model.DataContracts.Albums.AlbumContract + +@{ + void Stars(int current, int max) { + for (int i = 1; i <= max; ++i) { + if (current >= i) { + * + } else { + + } + } + } + + void StarsSpan(double current, int max) { + + @{ Stars((int)Math.Round(current, MidpointRounding.AwayFromZero), max); } + + } +} + +

    + @Model.Name + @if (!string.IsNullOrEmpty(Model.AdditionalNames)) { +
    + @Model.AdditionalNames + } +

    + +

    + @Model.ArtistString
    + @if (Model.DiscType != DiscType.Unknown) { + @Translate.DiscTypeName(Model.DiscType)
    + } +

    +@if (!Model.ReleaseDate.IsEmpty) { +

    + @HelperRes.AlbumHelpersStrings.Released @Model.ReleaseDate.Formatted + @if (Model.ReleaseEvent != null) { + @:(@Model.ReleaseEvent.Name) + } +

    +} +@if (Model.RatingCount > 0) { + @* Unable to call helpers here, might need some additional initializations to RazorHelper *@ + StarsSpan(Model.RatingAverage, 5); @("(" + Model.RatingCount + " " + ViewRes.Album.DetailsStrings.Ratings + ")") +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/AlbumWithCoverPopupContent.cshtml b/VocaDbWeb.Core/Views/Shared/AlbumWithCoverPopupContent.cshtml new file mode 100644 index 0000000000..d21ee3cb9f --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/AlbumWithCoverPopupContent.cshtml @@ -0,0 +1,7 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Model.Domain.Images +@model VocaDb.Model.DataContracts.Albums.AlbumContract + +Cover +

    +@{ Html.RenderPartial("AlbumPopupContent", Model); } \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/ArtistPopupContent.cshtml b/VocaDbWeb.Core/Views/Shared/ArtistPopupContent.cshtml new file mode 100644 index 0000000000..3a6cc50f44 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/ArtistPopupContent.cshtml @@ -0,0 +1,25 @@ +@using VocaDb.Web.Helpers +@model VocaDb.Model.DataContracts.Artists.ArtistContract + +
    + Thumb +
    + +

    + @Model.Name + + @if (!string.IsNullOrEmpty(Model.AdditionalNames)) { +
    + @Model.AdditionalNames + } +

    + +

    + @Translate.ArtistTypeName(Model.ArtistType)
    +

    + +@if (Model.ReleaseDate.HasValue) { +

    + @ViewRes.Artist.DetailsStrings.ReleaseDate @Model.ReleaseDate.Value.ToShortDateString() +

    +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/GlobalSearchBox.cshtml b/VocaDbWeb.Core/Views/Shared/GlobalSearchBox.cshtml new file mode 100644 index 0000000000..dab4133cfe --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/GlobalSearchBox.cshtml @@ -0,0 +1,143 @@ +@using VocaDb.Model +@using VocaDb.Model.Domain.Globalization +@using VocaDb.Model.Domain.Images +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.User +@using Res = ViewRes.TopBarStrings +@model VocaDb.Web.Models.GlobalSearchBoxModel +@inject Login Login + +@using (Html.BeginForm("GlobalSearch", "Home", FormMethod.Post, new { @class = "navbar-form form-inline pull-left navbar-search", id = "globalSearchBox" })) { + + + + + + + + + +
    + @Html.ActionLink(BrandableStrings.SiteName, "Index", "Home") +
    + + + + + + if (Login.Manager.IsLoggedIn) { + + + + + +
  • +

    + @ViewRes.TopBarStrings.NoUnreadMessages +

    +
  • +
  • +
  • + @ViewRes.TopBarStrings.ViewAllMessages +
  • + + + } + + + +} diff --git a/VocaDbWeb.Core/Views/Shared/KnockoutPartials/_DuplicateEntriesMessage.cshtml b/VocaDbWeb.Core/Views/Shared/KnockoutPartials/_DuplicateEntriesMessage.cshtml new file mode 100644 index 0000000000..a7b7ea0fdf --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/KnockoutPartials/_DuplicateEntriesMessage.cshtml @@ -0,0 +1,17 @@ + + diff --git a/VocaDbWeb.Core/Views/Shared/KnockoutPartials/_EntryPictureFileEdit.cshtml b/VocaDbWeb.Core/Views/Shared/KnockoutPartials/_EntryPictureFileEdit.cshtml new file mode 100644 index 0000000000..225de5328f --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/KnockoutPartials/_EntryPictureFileEdit.cshtml @@ -0,0 +1,13 @@ + + + + Preview + + + + + + + @ViewRes.SharedStrings.Delete + + diff --git a/VocaDbWeb.Core/Views/Shared/KnockoutPartials/_IPManage.cshtml b/VocaDbWeb.Core/Views/Shared/KnockoutPartials/_IPManage.cshtml new file mode 100644 index 0000000000..1fedc97bb8 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/KnockoutPartials/_IPManage.cshtml @@ -0,0 +1,8 @@ + + +(View on GeoIPTool) +(View on StopForumSpam) +(View in audit log) diff --git a/VocaDbWeb.Core/Views/Shared/KnockoutPartials/_NamesEditor.cshtml b/VocaDbWeb.Core/Views/Shared/KnockoutPartials/_NamesEditor.cshtml new file mode 100644 index 0000000000..0c15bdfaca --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/KnockoutPartials/_NamesEditor.cshtml @@ -0,0 +1,43 @@ +@model VocaDb.Web.Models.Shared.NamesEditorViewModel + + + + + + + + + + + + + + +
    @Resources.ContentLanguageSelectionNames.Japanese + +
    @Resources.ContentLanguageSelectionNames.Romaji + +
    @Resources.ContentLanguageSelectionNames.English + +
    + +@if (Model == null || Model.ShowAliases) { + + + + + + + + + +
    + + + @ViewRes.SharedStrings.Delete +
    + +@HelperRes.HelperStrings.NameNewRow +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/LayoutPartials/_TwitterCard.cshtml b/VocaDbWeb.Core/Views/Shared/LayoutPartials/_TwitterCard.cshtml new file mode 100644 index 0000000000..b2b4933f3e --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/LayoutPartials/_TwitterCard.cshtml @@ -0,0 +1,11 @@ +@inherits VocaDb.Web.Code.VocaDbPage + +@if (PageProperties.OpenGraph.ShowTwitterCard && !string.IsNullOrEmpty(PageProperties.OpenGraph.Title) && !string.IsNullOrEmpty(PageProperties.OpenGraph.Description)) { + + + + + if (!string.IsNullOrEmpty(PageProperties.OpenGraph.Image)) { + + } +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/PVs/_PVContent.cshtml b/VocaDbWeb.Core/Views/Shared/PVs/_PVContent.cshtml new file mode 100644 index 0000000000..e7114916ea --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/PVs/_PVContent.cshtml @@ -0,0 +1,34 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.PV +@model VocaDb.Model.DataContracts.Songs.SongWithPVAndVoteContract +@inject PVHelper PVHelper + + +

    + + @Html.ActionLink(Model.Name, "Details", "Song", new { id = Model.Id }, null) + +   + + @Model.ArtistString + (@Translate.SongTypeNames[Model.SongType]) + +

    +@Model.AdditionalNames + + +
    + @Html.Partial("Partials/PV/_EmbedPV", new EmbedPVViewModel(PVHelper.PrimaryPV(Model.PVs), 560, 340)) +
    + + + @ViewRes.Home.IndexStrings.ViewSongInfo + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/PVs/_PVEdit.cshtml b/VocaDbWeb.Core/Views/Shared/PVs/_PVEdit.cshtml new file mode 100644 index 0000000000..a793f0d6ed --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/PVs/_PVEdit.cshtml @@ -0,0 +1,45 @@ +@using VocaDb.Model.Domain.PVs +@using VocaDb.Web.Helpers +@* Binding context: PVEditViewModel, parent context: PVListEditViewModel *@ + + + + + {{service}} + {{service}} + + + + @foreach (var pvType in Translate.PVTypeNames) { + @pvType.Name + } + + + + + + {{ length | formatLengthSeconds }} + + + {{#if $parent.showPublishDates }} + {{ publishDate | formatDate:'l' }} + {{/if}} + + + {{#if author && $parent.canBulkDeletePVs}} + {{author}} + {{/if}} + {{#ifnot author && $parent.canBulkDeletePVs}} + {{ author }} + {{/if}} + + + + + @ViewRes.EntryEditStrings.PVUnavailable + + + + @ViewRes.SharedStrings.Delete + + diff --git a/VocaDbWeb.Core/Views/Shared/PVs/_PVEmbedDynamic.cshtml b/VocaDbWeb.Core/Views/Shared/PVs/_PVEmbedDynamic.cshtml new file mode 100644 index 0000000000..74fddb74db --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/PVs/_PVEmbedDynamic.cshtml @@ -0,0 +1,4 @@ +@using VocaDb.Web.Models.Shared.Partials.PV +@model VocaDb.Model.DataContracts.PVs.PVContract + +@Html.Partial("Partials/PV/_EmbedPV", new EmbedPVViewModel(Model)) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/PVs/_PVEmbedDynamicCustom.cshtml b/VocaDbWeb.Core/Views/Shared/PVs/_PVEmbedDynamicCustom.cshtml new file mode 100644 index 0000000000..75bb6b967f --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/PVs/_PVEmbedDynamicCustom.cshtml @@ -0,0 +1,4 @@ +@using VocaDb.Web.Models.Shared.Partials.PV +@model VocaDb.Web.Models.Shared.PVEmbedParams + +@Html.Partial("Partials/PV/_EmbedPV", new EmbedPVViewModel(Model.PV, enableApi: Model.EnableScriptAccess, id: Model.ElementId)) diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Activityfeed/_ActivityEntry.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Activityfeed/_ActivityEntry.cshtml new file mode 100644 index 0000000000..2b9c110155 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Activityfeed/_ActivityEntry.cshtml @@ -0,0 +1,46 @@ +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Activityfeed +@using VocaDb.Web.Models.Shared.Partials.User +@model ActivityEntryViewModel + +@{ + var thumbUrl = Model.Entry.Entry.MainPicture != null ? VocaDb.Model.Service.Helpers.UrlHelper.UpgradeToHttps(Model.Entry.Entry.MainPicture.GetSmallestThumb(VocaDb.Model.Domain.Images.ImageSize.TinyThumb)) : null; +} + +
    + @if (Model.Entry.Author != null) + { + @Html.Partial("Partials/User/_UserIconLink_UserForApiContract", new UserIconLink_UserForApiContractViewModel(Model.Entry.Author, userInfo: true)) + } + else + { + @HelperRes.ActivityFeedHelperStrings.Someone + } + + @Html.Partial("Partials/Activityfeed/_EntryType", new EntryTypeViewModel(Model.Entry)) + + + @TimeAgoStringBuilder.FormatTimeAgo(Model.Entry.CreateDate) + + +
    + @if (!string.IsNullOrEmpty(thumbUrl)) + { + + thumb + + } +
    +

    + + @Model.Entry.Entry.Name + +

    + @if (!string.IsNullOrEmpty(Model.Entry.Entry.ArtistString)) + { + @Model.Entry.Entry.ArtistString + } +
    +
    +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Activityfeed/_ActivityEntryKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Activityfeed/_ActivityEntryKnockout.cshtml new file mode 100644 index 0000000000..ac86009356 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Activityfeed/_ActivityEntryKnockout.cshtml @@ -0,0 +1,43 @@ +@using HelperRes +@using VocaDb.Web.Models.Shared.Partials.Activityfeed +@using VocaDb.Web.Models.Shared.Partials.User +@model ActivityEntryKnockoutViewModel + +
    + + @Html.Partial("Partials/User/_IconNameAndTypeLinkKnockout", new IconNameAndTypeLinkKnockoutViewModel("$parents[1].resources.resources().userGroupNames")) + + @ActivityFeedHelperStrings.Someone + + + + @if (Model.ShowDetails) + { + + ({{ @(Model.ChangedFieldNamesBinding)($parent.entry, $data) }}) + {{ '\"' + $data.notes + '\"' }} + + + (@ViewRes.MiscStrings.Details) + + + } + + + +
    + + thumb + +
    +

    + + {{ entry.name }} + + ({{ @(Model.EntryTypeNamesBinding)(entry) }}) +

    + + {{ entry.artistString }} +
    +
    +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Activityfeed/_EntryType.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Activityfeed/_EntryType.cshtml new file mode 100644 index 0000000000..d3756c2c23 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Activityfeed/_EntryType.cshtml @@ -0,0 +1,25 @@ +@using VocaDb.Model.Domain.Activityfeed +@using VocaDb.Web.Models.Shared.Partials.Activityfeed +@model EntryTypeViewModel + +@{ + var tr = HelperRes.ActivityFeedHelperStrings.ResourceManager.GetString(Model.Entry.EditEvent.ToString() + Model.Entry.Entry.EntryType); +} + +@if (!string.IsNullOrEmpty(tr)) +{ + @tr +} +else +{ + var entryTypeName = HelperRes.ActivityFeedHelperStrings.ResourceManager.GetString("Entry" + Model.Entry.Entry.EntryType); + + if (Model.Entry.EditEvent == EntryEditEvent.Created) + { + @string.Format(HelperRes.ActivityFeedHelperStrings.CreatedNew, entryTypeName) + } + else + { + @string.Format(HelperRes.ActivityFeedHelperStrings.Updated, entryTypeName) + } +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Album/_AlbumGrid.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Album/_AlbumGrid.cshtml new file mode 100644 index 0000000000..2178f81280 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Album/_AlbumGrid.cshtml @@ -0,0 +1,41 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Album +@model AlbumGridViewModel + + + @{ int i = 0; } + + @foreach (var album in Model.Albums) + { + if (i % Model.Columns == 0) + { + @Html.Raw("") + } + + + { i++; } + if (i % Model.Columns == 0) + { + @Html.Raw("") + } + } + @if (i % Model.Columns != 0) + { + @Html.Raw("") + } +
    + @Html.Partial("Partials/Album/_AlbumIconLink", new AlbumIconLinkViewModel(album)) + + @Html.ActionLink(album.Name, "Details", "Album", new { id = album.Id }, new { title = album.AdditionalNames }) + @if (Model.DisplayType) + { + @("(")@Translate.DiscTypeName(album.DiscType)@(")") + } +
    + @album.ArtistString + @if (Model.DisplayReleaseDate && !album.ReleaseDate.IsEmpty) + { +
    + @HelperRes.AlbumHelpersStrings.Released @album.ReleaseDate.Formatted + } +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Album/_AlbumIconLink.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Album/_AlbumIconLink.cshtml new file mode 100644 index 0000000000..b4f7a9595c --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Album/_AlbumIconLink.cshtml @@ -0,0 +1,8 @@ +@using VocaDb.Model.Domain.Images +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Album +@model AlbumIconLinkViewModel + + + Cover + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Album/_AlbumLink.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Album/_AlbumLink.cshtml new file mode 100644 index 0000000000..5d6d7a0816 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Album/_AlbumLink.cshtml @@ -0,0 +1,5 @@ +@using Microsoft.AspNetCore.Routing +@using VocaDb.Web.Models.Shared.Partials.Album +@model AlbumLinkViewModel + +@Html.ActionLink(Model.Album.Name, "Details", "Album", new RouteValueDictionary { { "id", Model.Album.Id } }, new Dictionary { { "title", Model.Album.AdditionalNames }, { "data-entry-id", Model.Album.Id }, { "class", "albumLink" } }) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Album/_AlbumThumbs.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Album/_AlbumThumbs.cshtml new file mode 100644 index 0000000000..7764ddb406 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Album/_AlbumThumbs.cshtml @@ -0,0 +1,12 @@ +@using VocaDb.Model.Domain.Images +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Album +@using VocaDb.Web.Models.Shared.Partials.Shared +@model AlbumThumbsViewModel + +
      + @foreach (var album in Model.Albums) + { + @Html.Partial("Partials/Shared/_ThumbItem", new ThumbItemViewModel(Url.Action("Details", "Album", new { id = album.Id }), Url.ImageThumb(album.MainPicture, ImageSize.SmallThumb), album.Name, album)) + } +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Album/_CoverLink.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Album/_CoverLink.cshtml new file mode 100644 index 0000000000..573da92624 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Album/_CoverLink.cshtml @@ -0,0 +1,8 @@ +@using VocaDb.Model.Domain.Images +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Album +@model CoverLinkViewModel + + + @ViewRes.Album.DetailsStrings.CoverPicture + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Album/_DiscTypesDropdownKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Album/_DiscTypesDropdownKnockout.cshtml new file mode 100644 index 0000000000..637110d804 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Album/_DiscTypesDropdownKnockout.cshtml @@ -0,0 +1,7 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Album +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using VocaDb.Model.Utils +@model DiscTypesDropdownKnockoutViewModel + +@Html.Partial("Partials/Knockout/_CheckBoxButtons", new CheckBoxButtonsViewModel(Translate.DiscTypeNames.GetValuesAndNamesStrings(AppConfig.AlbumTypes), Model.Binding)) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Album/_EditCollectionDialog.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Album/_EditCollectionDialog.cshtml new file mode 100644 index 0000000000..7cce119fe2 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Album/_EditCollectionDialog.cshtml @@ -0,0 +1,22 @@ +@using VocaDb.Model.Domain.Users +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Album +@model EditCollectionDialogViewModel + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Album/_PrintArchivedAlbumData.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Album/_PrintArchivedAlbumData.cshtml new file mode 100644 index 0000000000..0a551a1dcc --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Album/_PrintArchivedAlbumData.cshtml @@ -0,0 +1,31 @@ +@using Microsoft.AspNetCore.Html +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Album +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@model PrintArchivedAlbumDataViewModel + +
    +

    Content

    + + + @Html.Partial("Partials/ArchivedEntry/_DataRow", new DataRowViewModel("Album Id", Model.ComparedAlbums.FirstData.Id)) + @Html.PictureRow("Main picture", Model.ComparedAlbums, id => Url.Action("ArchivedVersionCoverPicture", "Album", new { id })) + @Html.DataRow("Main picture MIME", Model.ComparedAlbums, d => d.MainPictureMime) + @Html.TranslatedNameRow(Model.ComparedAlbums, d => d.TranslatedName) + @Html.DataRowList("Names", Model.ComparedAlbums, d => DataFormatUtils.GenerateHtml(d.Names, name => Html.Partial("Partials/ArchivedEntry/_NameInfo", new NameInfoViewModel(name)))) + @Html.DataRow("Description", Model.ComparedAlbums, d => d.Description, preserveLineBreaks: true) + @Html.DataRow("Description (en)", Model.ComparedAlbums, d => d.DescriptionEng, preserveLineBreaks: true) + @Html.DataRow("Disc type", Model.ComparedAlbums, d => Translate.DiscTypeName(d.DiscType)) + @Html.DataRow("Release date", Model.ComparedAlbums, d => d.OriginalRelease != null && d.OriginalRelease.ReleaseDate != null && !d.OriginalRelease.ReleaseDate.IsEmpty ? ArchivedEntryHtmlExtensions.FormatReleaseDate(d.OriginalRelease.ReleaseDate) : string.Empty) + @Html.DataRow("Catalog number", Model.ComparedAlbums, d => d.OriginalRelease != null ? d.OriginalRelease.CatNum : string.Empty) + @Html.DataRow("Release event", Model.ComparedAlbums, d => Html.Partial("Partials/ArchivedEntry/_ObjectRefInfo", new ObjectRefInfoViewModel(d.OriginalRelease != null ? d.OriginalRelease.ReleaseEvent : null))) + @Html.DataRowList("Barcodes", Model.ComparedAlbums, d => DataFormatUtils.GenerateHtml(d.Identifiers, d2 => new HtmlString(d2.Value))) + @Html.DataRowList("External links", Model.ComparedAlbums, d => DataFormatUtils.GenerateHtml(d.WebLinks, webLink => Html.Partial("Partials/ArchivedEntry/_WebLinkInfo", new WebLinkInfoViewModel(webLink)))) + @Html.DataRowList("Artists", Model.ComparedAlbums, d => DataFormatUtils.GenerateHtml(d.Artists != null ? d.Artists.OrderBy(a => a.NameHint) : null, artist => new HtmlString(artist.NameHint + " [" + artist.Id + "] - IsSupport: " + artist.IsSupport + ", Roles: " + artist.Roles))) + @Html.DataRowList("Discs", Model.ComparedAlbums, d => DataFormatUtils.GenerateHtml(d.Discs != null ? d.Discs.OrderBy(s => s.DiscNumber) : null, disc => new HtmlString(disc.DiscNumber + ": " + disc.Name + " (" + disc.MediaType + ") [" + disc.Id + "]"))) + @Html.DataRowList("Tracks", Model.ComparedAlbums, d => DataFormatUtils.GenerateHtml(d.Songs != null ? d.Songs.OrderBy(s => s.DiscNumber).ThenBy(s => s.TrackNumber) : null, song => new HtmlString("(Disc " + song.DiscNumber + ") " + song.TrackNumber + ". " + song.NameHint + " [" + song.Id + "]"))) + @Html.DataRowList("Pictures", Model.ComparedAlbums, d => DataFormatUtils.GenerateHtml(d.Pictures, picture => Html.Partial("Partials/ArchivedEntry/_PictureFileInfo", new PictureFileInfoViewModel(picture)))) + @Html.DataRowList("PVs", Model.ComparedAlbums, d => DataFormatUtils.GenerateHtml(d.PVs, pv => Html.Partial("Partials/ArchivedEntry/_PVInfo", new PVInfoViewModel(pv)))) +
    + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_ArchivedObjectVersionProperties.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_ArchivedObjectVersionProperties.cshtml new file mode 100644 index 0000000000..36d2d44653 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_ArchivedObjectVersionProperties.cshtml @@ -0,0 +1,92 @@ +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@using VocaDb.Web.Models.Shared.Partials.Shared +@using VocaDb.Web.Models.Shared.Partials.User +@model ArchivedObjectVersionPropertiesViewModel + +
    +

    Properties

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Version@Model.Ver.Contract.Version + @if (Model.CompareTo != null) + { + @Model.CompareTo.Contract.Version + } +
    Status@Model.Ver.Status + @if (Model.CompareTo != null) + { + @Model.CompareTo.Status + } +
    Snapshot@Model.Ver.Contract.IsSnapshot + @if (Model.CompareTo != null) + { + @Model.CompareTo.Contract.IsSnapshot + } +
    Created@Html.Partial("Partials/Shared/_UniversalTimeLabel", new UniversalTimeLabelViewModel(Model.Ver.Contract.Created)) + @if (Model.CompareTo != null) + { + @Html.Partial("Partials/Shared/_UniversalTimeLabel", new UniversalTimeLabelViewModel(Model.CompareTo.Contract.Created)) + } +
    Author@Html.Partial("Partials/User/_UserIconLinkOrName", new UserIconLinkOrNameViewModel(Model.Ver.Contract.Author, Model.Ver.Contract.AgentName)) + @if (Model.CompareTo != null) + { + @Html.Partial("Partials/User/_UserIconLinkOrName", new UserIconLinkOrNameViewModel(Model.CompareTo.Contract.Author, Model.CompareTo.Contract.AgentName)) + } +
    Reason@Model.Ver.Reason + @if (Model.CompareTo != null) + { + @Model.CompareTo.Reason + } +
    Changed@Model.Ver.ChangeMessage + @if (Model.CompareTo != null) + { + @Model.CompareTo.ChangeMessage + } +
    Notes@Model.Ver.Notes + @if (Model.CompareTo != null) + { + @Model.CompareTo.Notes + } +
    + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_ArchivedObjectVersions.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_ArchivedObjectVersions.cshtml new file mode 100644 index 0000000000..22b8384c32 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_ArchivedObjectVersions.cshtml @@ -0,0 +1,64 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@using VocaDb.Web.Models.Shared.Partials.Shared +@using VocaDb.Web.Models.Shared.Partials.User +@model ArchivedObjectVersionsViewModel +@inject Login Login + + + + + + + + + + + + + @foreach (var ver in Model.ArchivedVersions.OrderByDescending(v => v.Contract.Version)) + { + var style = (ver.AnythingChanged ? "" : "muted"); + + + + + + + + } + +
    @ViewRes.ArchivedObjectVersionsStrings.Version@ViewRes.ArchivedObjectVersionsStrings.Created@ViewRes.ArchivedObjectVersionsStrings.Author@ViewRes.ArchivedObjectVersionsStrings.Changes@ViewRes.ArchivedObjectVersionsStrings.Notes
    + @if (Model.LinkFunc != null && (Login.CanViewHiddenRevisions || !ver.Hidden)) + { + + @ver.Contract.Version + (@Translate.EntryStatusNames[ver.Status]) + + } + else + { + + @ver.Contract.Version + (@Translate.EntryStatusNames[ver.Status]) + + } + + + @Html.Partial("Partials/Shared/_UniversalTimeLabel", new UniversalTimeLabelViewModel(ver.Contract.Created)) + + + @Html.Partial("Partials/User/_UserIconLinkOrName", new UserIconLinkOrNameViewModel(ver.Contract.Author, ver.Contract.AgentName)) + + + @ver.Reason + @if (!string.IsNullOrEmpty(ver.ChangeMessage)) + { + @(" (" + ver.ChangeMessage + ")") + } + + + + @ver.Notes + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_CurrentVersionMessage.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_CurrentVersionMessage.cshtml new file mode 100644 index 0000000000..15e2da181c --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_CurrentVersionMessage.cshtml @@ -0,0 +1,5 @@ +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@using VocaDb.Web.Helpers +@model CurrentVersionMessageViewModel + +

    @ViewRes.ArchivedObjectVersionsStrings.CurrentVersionIs @Model.Version (@Translate.EntryStatusNames[Model.Status]).

    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_DataRow.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_DataRow.cshtml new file mode 100644 index 0000000000..a9a8d221ec --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_DataRow.cshtml @@ -0,0 +1,13 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@model DataRowViewModel + + + @Model.Name + @Model.Val + @if (Model.CompareVal != null) + { + @Model.CompareVal + + } + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_DataRowList.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_DataRowList.cshtml new file mode 100644 index 0000000000..103c996bbf --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_DataRowList.cshtml @@ -0,0 +1,26 @@ +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@model DataRowListViewModel + +@if (Model.Rows != null) { + + @Model.Name + +
      + @foreach (var row in Model.Rows) { + var isNew = (Model.CompareRows != null && !Model.CompareRows.Any(r => row.ToString().Equals(r.ToString()))); +
    • @row
    • + } +
    + + @if (Model.CompareRows != null) { + +
      + @foreach (var row in Model.CompareRows) { + var isRemoved = !Model.Rows.Any(r => row.ToString().Equals(r.ToString())); +
    • @row
    • + } +
    + + } + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_NameInfo.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_NameInfo.cshtml new file mode 100644 index 0000000000..518ca859c3 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_NameInfo.cshtml @@ -0,0 +1,5 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@model NameInfoViewModel + +@Translate.ContentLanguageSelectionName(Model.Name.Language): @Model.Name.Value \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_ObjectRefInfo.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_ObjectRefInfo.cshtml new file mode 100644 index 0000000000..0f8a8edd4e --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_ObjectRefInfo.cshtml @@ -0,0 +1,7 @@ +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@model ObjectRefInfoViewModel + +@if (Model.ObjRef != null) +{ + @Model.ObjRef.NameHint [@Model.ObjRef.Id] +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_PVInfo.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_PVInfo.cshtml new file mode 100644 index 0000000000..0ab4e5cd31 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_PVInfo.cshtml @@ -0,0 +1,16 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@model PVInfoViewModel + +@Model.PV.Service (@Model.PV.PVType): +@Model.PV.PVId +by @Model.PV.Author +(@DateTimeUtils.FormatFromSeconds(Model.PV.Length)) +@if (Model.PV.PublishDate != null) +{ + @:at @Model.PV.PublishDate.Value.ToShortDateString() +} +@if (Model.PV.Disabled) +{ + @:(unavailable) +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_PictureFileInfo.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_PictureFileInfo.cshtml new file mode 100644 index 0000000000..bff1c0f4b6 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_PictureFileInfo.cshtml @@ -0,0 +1,4 @@ +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@model PictureFileInfoViewModel + +@Model.Pic.Name (@Model.Pic.Mime) - @Model.Pic.Author, @Model.Pic.Created \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_PictureRow.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_PictureRow.cshtml new file mode 100644 index 0000000000..d3f249909c --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_PictureRow.cshtml @@ -0,0 +1,15 @@ +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@model PictureRowViewModel + + + @Model.Name + + @Model.Name + + @if (!string.IsNullOrEmpty(Model.CompareToUrl)) + { + + @Model.Name + + } + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_TranslatedNameRow.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_TranslatedNameRow.cshtml new file mode 100644 index 0000000000..50ec36c814 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_TranslatedNameRow.cshtml @@ -0,0 +1,36 @@ +@using VocaDb.Model.Domain.Globalization +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@model TranslatedNameRowViewModel + + + Original language + + @(Model.Name != null ? Translate.ContentLanguageSelectionName(Model.Name.DefaultLanguage) : string.Empty) + + @if (Model.CompareToName != null) + { + + @Translate.ContentLanguageSelectionName(Model.CompareToName.DefaultLanguage) + + } + + + Primary names + + @if (Model.Name != null) + { + @:@Translate.ContentLanguageSelectionName(ContentLanguageSelection.Japanese): @Model.Name.Japanese
    + @:@Translate.ContentLanguageSelectionName(ContentLanguageSelection.Romaji): @Model.Name.Romaji
    + @:@Translate.ContentLanguageSelectionName(ContentLanguageSelection.English): @Model.Name.English
    + } + + @if (Model.CompareToName != null) + { + + @(Translate.ContentLanguageSelectionName(ContentLanguageSelection.Japanese) + ": " + Model.CompareToName.Japanese)
    + @(Translate.ContentLanguageSelectionName(ContentLanguageSelection.Romaji) + ": " + Model.CompareToName.Romaji)
    + @(Translate.ContentLanguageSelectionName(ContentLanguageSelection.English) + ": " + Model.CompareToName.English)
    + + } + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_WebLinkInfo.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_WebLinkInfo.cshtml new file mode 100644 index 0000000000..9476fe8ae5 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/ArchivedEntry/_WebLinkInfo.cshtml @@ -0,0 +1,13 @@ +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@model WebLinkInfoViewModel + +@if (!string.IsNullOrEmpty(Model.Link.Description)) +{ + @Model.Link.Description: +} +@Model.Link.Url +@(" (")@Model.Link.Category@(")") +@if (Model.Link.Disabled) +{ + @:(unavailable) +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistGrid.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistGrid.cshtml new file mode 100644 index 0000000000..657751d8c1 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistGrid.cshtml @@ -0,0 +1,39 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Artist +@model ArtistGridViewModel + + + @{ int i = 0; } + + @foreach (var artist in Model.Artists) + { + if (i % Model.Columns == 0) + { + @Html.Raw("") + } + + + { i++; } + if (i % Model.Columns == 0) + { + @Html.Raw("") + } + } + @if (i % Model.Columns != 0) + { + @Html.Raw("") + } +
    + @Html.Partial("Partials/Artist/_ArtistIconLink", new ArtistIconLinkViewModel(artist)) + + @Html.Partial("Partials/Artist/_ArtistLink", new ArtistLinkViewModel(artist)) + @if (Model.DisplayType) + { + @("(")@Translate.ArtistTypeName(artist.ArtistType)@(")") + } + @if (!string.IsNullOrEmpty(artist.AdditionalNames)) + { +
    + @artist.AdditionalNames + } +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistIconLink.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistIconLink.cshtml new file mode 100644 index 0000000000..1be79e7680 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistIconLink.cshtml @@ -0,0 +1,8 @@ +@using VocaDb.Model.Domain.Images +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Artist +@model ArtistIconLinkViewModel + + + Thumb + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistLink.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistLink.cshtml new file mode 100644 index 0000000000..8e67077dd8 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistLink.cshtml @@ -0,0 +1,10 @@ +@using Microsoft.AspNetCore.Routing +@using VocaDb.Web.Models.Shared.Partials.Artist +@model ArtistLinkViewModel + +@if (Model.TypeLabel) +{ + @Html.Partial("Partials/Artist/_ArtistTypeLabel", new ArtistTypeLabelViewModel(Model.Artist.ArtistType)) +} +@Html.ActionLink(Model.Name ?? Model.Artist.Name, "Details", "Artist", new RouteValueDictionary { { "id", Model.Artist.Id } }, new Dictionary { { "title", Model.Artist.AdditionalNames }, { "data-entry-id", Model.Artist.Id }, { "class", "artistLink" } }) +@if (Model.ReleaseYear && Model.Artist.ReleaseDate.HasValue) { (@Model.Artist.ReleaseDate.Value.Year)} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistLinkList.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistLinkList.cshtml new file mode 100644 index 0000000000..8983f9b5c3 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistLinkList.cshtml @@ -0,0 +1,6 @@ +@using Microsoft.AspNetCore.Html +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Artist +@model ArtistLinkListViewModel + +@Html.LinkList(Model.Artists, a => new HtmlString(Html.Partial("Partials/Artist/_ArtistLink", new ArtistLinkViewModel(a, Model.TypeLabel, releaseYear: Model.ReleaseYear)).ToString())) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistList.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistList.cshtml new file mode 100644 index 0000000000..0d17f9ec15 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistList.cshtml @@ -0,0 +1,59 @@ +@using VocaDb.Model.DataContracts.Songs +@using VocaDb.Model.Domain.Artists +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Artist +@model ArtistListViewModel + +@functions { + public static bool ShowRoles(IArtistLinkContract artist, ShowRolesMode showRoles) + { + switch (showRoles) + { + case ShowRolesMode.IfNotDefault: + return artist.EffectiveRoles != ArtistRoles.Default; + case ShowRolesMode.IfNotVocalist: + return artist.EffectiveRoles != ArtistRoles.Default + && artist.EffectiveRoles != ArtistRoles.Vocalist + && !artist.Categories.HasFlag(ArtistCategories.Subject) + && !artist.Categories.HasFlag(ArtistCategories.Producer); + default: + return false; + } + } +} + +@{ + var ordered = Model.Artists.OrderBy(a => a.IsSupport).ToArray(); +} + +@foreach (var artist in ordered) +{ + if (artist.IsSupport) + { + @("(") + } + if (artist.Artist != null) + { + if (Model.ShowType) + { + @Html.Partial("Partials/Artist/_ArtistTypeLabel", new ArtistTypeLabelViewModel(artist.Artist.ArtistType)) + } + @Html.Partial("Partials/Artist/_ArtistLink", new ArtistLinkViewModel(artist.Artist, name: artist.Name)) + } + else + { + @artist.Name + } + if (ShowRoles(artist, Model.ShowRoles)) + { + @Html.Raw(" (" + Translate.ArtistRoleNames.GetAllNameNames(artist.EffectiveRoles, ArtistRoles.Default) + ")") + } + if (artist.IsSupport) + { + @(")") + } + if (artist != ordered.Last()) + { + @(", ") + } +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistTypeLabel.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistTypeLabel.cshtml new file mode 100644 index 0000000000..4e38990b99 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistTypeLabel.cshtml @@ -0,0 +1,34 @@ +@using VocaDb.Model.Domain.Artists +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Artist +@model ArtistTypeLabelViewModel + +@{ + var title = Translate.ArtistTypeName(Model.ArtistType); + switch (Model.ArtistType) + { + case ArtistType.Vocaloid: + V + break; + + case ArtistType.UTAU: + U + break; + + case ArtistType.CeVIO: + C + break; + + case ArtistType.OtherVoiceSynthesizer: + O + break; + + case ArtistType.Utaite: + U + break; + + case ArtistType.OtherVocalist: + O + break; + } +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistTypesDropdownKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistTypesDropdownKnockout.cshtml new file mode 100644 index 0000000000..efb4e79989 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Artist/_ArtistTypesDropdownKnockout.cshtml @@ -0,0 +1,11 @@ +@using VocaDb.Model.Utils +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Artist +@model ArtistTypesDropdownKnockoutViewModel + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Artist/_PrintArchivedArtistData.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Artist/_PrintArchivedArtistData.cshtml new file mode 100644 index 0000000000..b293a6f460 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Artist/_PrintArchivedArtistData.cshtml @@ -0,0 +1,27 @@ +@using Microsoft.AspNetCore.Html +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@using VocaDb.Web.Models.Shared.Partials.Artist +@model PrintArchivedArtistDataViewModel + +
    +

    Content

    + + + @Html.Partial("Partials/ArchivedEntry/_DataRow", new DataRowViewModel("Id", Model.ComparedArtists.FirstData.Id)) + @Html.PictureRow("Main picture", Model.ComparedArtists, id => Url.Action("ArchivedVersionPicture", "Artist", new { id })) + @Html.DataRow("Main picture MIME", Model.ComparedArtists, d => d.MainPictureMime) + @Html.TranslatedNameRow(Model.ComparedArtists, d => d.TranslatedName) + @Html.DataRowList("Names", Model.ComparedArtists, d => DataFormatUtils.GenerateHtml(d.Names, name => Html.Partial("Partials/ArchivedEntry/_NameInfo", new NameInfoViewModel(name)))) + @Html.DataRow("Description", Model.ComparedArtists, d => d.Description, preserveLineBreaks: true) + @Html.DataRow("Description (en)", Model.ComparedArtists, d => d.DescriptionEng, preserveLineBreaks: true) + @Html.DataRow("Release date", Model.ComparedArtists, d => d.ReleaseDate) + @Html.DataRow("Artist type", Model.ComparedArtists, d => Translate.ArtistTypeName(d.ArtistType)) + @Html.DataRowList("External links", Model.ComparedArtists, d => DataFormatUtils.GenerateHtml(d.WebLinks, webLink => Html.Partial("Partials/ArchivedEntry/_WebLinkInfo", new WebLinkInfoViewModel(webLink)))) + @Html.DataRow("Base voicebank", Model.ComparedArtists, d => Html.Partial("Partials/ArchivedEntry/_ObjectRefInfo", new ObjectRefInfoViewModel(d.BaseVoicebank))) + @Html.DataRowList("Associated artists", Model.ComparedArtists, d => DataFormatUtils.GenerateHtml(d.Groups, g => new HtmlString(g.NameHint + " [" + g.Id + "] (" + g.LinkType + ")"))) + @Html.ObjectRefList("Members", Model.ComparedArtists, d => d.Members) + @Html.DataRowList("Pictures", Model.ComparedArtists, d => DataFormatUtils.GenerateHtml(d.Pictures, picture => Html.Partial("Partials/ArchivedEntry/_PictureFileInfo", new PictureFileInfoViewModel(picture)))) +
    + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentBodyEditableKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentBodyEditableKnockout.cshtml new file mode 100644 index 0000000000..6c4cd7e015 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentBodyEditableKnockout.cshtml @@ -0,0 +1,11 @@ +@using VocaDb.Web.Models.Shared.Partials.Comment +@model CommentBodyEditableKnockoutViewModel + +
    + @Html.Partial("Partials/Comment/_CommentBodyKnockout", new CommentBodyKnockoutViewModel(Model.MessageBinding)) +
    +
    + + + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentBodyKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentBodyKnockout.cshtml new file mode 100644 index 0000000000..d66f9badae --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentBodyKnockout.cshtml @@ -0,0 +1,4 @@ +@using VocaDb.Web.Models.Shared.Partials.Comment +@model CommentBodyKnockoutViewModel + +

    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentBodyLarge.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentBodyLarge.cshtml new file mode 100644 index 0000000000..e4e3b4c1c9 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentBodyLarge.cshtml @@ -0,0 +1,30 @@ +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Comment +@using VocaDb.Web.Models.Shared.Partials.Html +@using VocaDb.Web.Models.Shared.Partials.User +@model CommentBodyLargeViewModel +@inject Login Login + +
    + + @Html.Partial("Partials/User/_ProfileIcon_IUserWithEmail", new ProfileIcon_IUserWithEmailViewModel(Model.Contract.Author, 70)) + + +
    +
    + + @TimeAgoStringBuilder.FormatTimeAgo(Model.Contract.Created) + + @if (Model.AlwaysAllowDelete || (Model.AllowDelete && Login.CanDeleteComment(Model.Contract))) + { + @:   + @ViewRes.SharedStrings.Delete + } +
    +

    + @Html.Partial("Partials/User/_UserLink", new UserLinkViewModel(Model.Contract.Author)) +

    + @Html.Partial("Partials/Html/_FormatMarkdown", new FormatMarkdownViewModel(Model.Contract.Message)) +
    +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentEntryItem.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentEntryItem.cshtml new file mode 100644 index 0000000000..d55721a520 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentEntryItem.cshtml @@ -0,0 +1,28 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Comment +@model CommentEntryItemViewModel + +@{ + var thumbUrl = (Model.Entry.MainPicture != null ? VocaDb.Model.Service.Helpers.UrlHelper.UpgradeToHttps(Model.Entry.MainPicture.GetSmallestThumb(VocaDb.Model.Domain.Images.ImageSize.TinyThumb)) : null); +} + +
    + @if (!string.IsNullOrEmpty(thumbUrl)) + { + + thumb + + } + +
    +

    + + @Model.Entry.Name + +

    + @if (!string.IsNullOrEmpty(Model.Entry.ArtistString)) + { + @Model.Entry.ArtistString + } +
    +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentKnockout.cshtml new file mode 100644 index 0000000000..672156c654 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentKnockout.cshtml @@ -0,0 +1,40 @@ +@using VocaDb.Model.Domain.Images +@using VocaDb.Web.Models.Shared.Partials.Comment +@using VocaDb.Web.Models.Shared.Partials.User +@model CommentKnockoutViewModel + +
    + + @Html.Partial("Partials/User/_ProfileIconKnockout_ImageSize", new ProfileIconKnockout_ImageSizeViewModel(ImageSize.Thumb, binding: "author", size: 70)) + + +
    +
    + + + @if (Model.EditHandler != null) + { + + @:   + @ViewRes.SharedStrings.Edit + + } + + +    + @ViewRes.SharedStrings.Delete + +
    +

    + +

    + @if (Model.Body != null) + { + @Model.Body + } + else + { + @Html.Partial("Partials/Comment/_CommentBodyKnockout", new CommentBodyKnockoutViewModel(Model.MessageBinding)) + } +
    +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentWithEntryVertical.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentWithEntryVertical.cshtml new file mode 100644 index 0000000000..a74a4cf056 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CommentWithEntryVertical.cshtml @@ -0,0 +1,14 @@ +@using VocaDb.Web.Models.Shared.Partials.Comment +@model CommentWithEntryVerticalViewModel + +
    + @Html.Partial("Partials/Comment/_CommentEntryItem", new CommentEntryItemViewModel(Model.Entry.Entry)) + @foreach (var comment in Model.Entry.Comments) + { + @Html.Partial("Partials/Comment/_PrintComment", new PrintCommentViewModel(comment, false, maxLength: Model.MaxLength)) + if (comment != Model.Entry.Comments.Last()) + { +
    + } + } +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CreateComment.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CreateComment.cshtml new file mode 100644 index 0000000000..422911c814 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_CreateComment.cshtml @@ -0,0 +1,9 @@ +@using VocaDb.Web.Models.Shared.Partials.Comment +@model CreateCommentViewModel + +
    +
    + + +
    +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Comment/_EditableComments.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_EditableComments.cshtml new file mode 100644 index 0000000000..1a4908db40 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_EditableComments.cshtml @@ -0,0 +1,37 @@ +@using VocaDb.Web.Models.Shared.Partials.Comment +@model EditableCommentsViewModel + +@{ + var cssClass = (Model.Well ? "well well-transparent" : "standalone"); +} + + +@if (Model.AllowCreateComment && !Model.CommentBoxEnd) +{ + @Html.Partial("Partials/Comment/_CreateComment", new CreateCommentViewModel(cssClass, Model.NewCommentRows)) +} + +@if (Model.Pagination) +{ +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    +} + +
    +
    + @Html.Partial("Partials/Comment/_CommentKnockout", new CommentKnockoutViewModel("message", false, editHandler: "$parent.beginEditComment", standalone: !Model.Well, body: Html.Partial("Partials/Comment/_CommentBodyEditableKnockout", new CommentBodyEditableKnockoutViewModel("message")))) +
    +
    + +@if (Model.Pagination) +{ +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    +} + +@if (Model.AllowCreateComment && Model.CommentBoxEnd) +{ + @Html.Partial("Partials/Comment/_CreateComment", new CreateCommentViewModel(cssClass, Model.NewCommentRows)) +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Comment/_LatestCommentsKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_LatestCommentsKnockout.cshtml new file mode 100644 index 0000000000..1dcf4252d8 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_LatestCommentsKnockout.cshtml @@ -0,0 +1,11 @@ +@using VocaDb.Model.Domain.Security +@using VocaDb.Web.Models.Shared.Partials.Comment +@inherits VocaDb.Web.Code.VocaDbPage + +

    @ViewRes.EntryDetailsStrings.LatestComments

    +
    + @Html.Partial("Partials/Comment/_EditableComments", new EditableCommentsViewModel(UserContext.HasPermission(PermissionToken.CreateComments), well: false, commentsBinding: "topComments", newCommentRows: 3, pagination: false)) + +

    @ViewRes.EntryDetailsStrings.NoComments

    + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Comment/_PrintComment.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_PrintComment.cshtml new file mode 100644 index 0000000000..8d40311fd9 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Comment/_PrintComment.cshtml @@ -0,0 +1,23 @@ +@using VocaDb.Model.Helpers +@using VocaDb.Web.Helpers +@using VocaDb.Web.Code +@using VocaDb.Web.Models.Shared.Partials.Comment +@using VocaDb.Web.Models.Shared.Partials.Html +@using VocaDb.Web.Models.Shared.Partials.User +@model PrintCommentViewModel +@inject Login Login + +
    +

    + @Html.Partial("Partials/User/_UserIconLink_IUserWithEmail", new UserIconLink_IUserWithEmailViewModel(Model.Contract.Author, 25)) + + @if (Model.AlwaysAllowDelete || (Model.AllowDelete && Login.CanDeleteComment(Model.Contract))) + { + - @ViewRes.SharedStrings.Delete + } + + @TimeAgoStringBuilder.FormatTimeAgo(Model.Contract.Created) + +

    + @Html.Partial("Partials/Html/_FormatMarkdown", new FormatMarkdownViewModel(Model.Contract.Message.TruncateWithEllipsis(Model.MaxLength))) +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_DeletedBanner.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_DeletedBanner.cshtml new file mode 100644 index 0000000000..6159a7ddf4 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_DeletedBanner.cshtml @@ -0,0 +1,14 @@ +@using VocaDb.Model.DataContracts +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@using VocaDb.Web.Models.Shared.Partials.Shared +@model DeletedBannerViewModel + +
    + @Resources.CommonMessages.EntryDeleted + @if (Model.MergedTo != null) + { + @(Resources.CommonMessages.EntryMergedTo + " ") + @Html.Partial("Partials/Shared/_EntryLink", new EntryLinkViewModel(new EntryBaseContract(Model.MergedTo))) + @(".") + } +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_EntryDeletePopup.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_EntryDeletePopup.cshtml new file mode 100644 index 0000000000..5496c4ef26 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_EntryDeletePopup.cshtml @@ -0,0 +1,4 @@ +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@model EntryDeletePopupViewModel + +@Html.Partial("Partials/EntryDetails/_EntryDeletePopupBase", new EntryDeletePopupBaseViewModel(Model.ConfirmText, Model.ViewModelBindingName, Model.DeleteButtonId, ViewRes.SharedStrings.Delete, ViewRes.SharedStrings.Delete)) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_EntryDeletePopupBase.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_EntryDeletePopupBase.cshtml new file mode 100644 index 0000000000..a6a2bc8bd8 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_EntryDeletePopupBase.cshtml @@ -0,0 +1,24 @@ +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@model EntryDeletePopupBaseViewModel + + +
    + +

    + @Model.ConfirmText +

    + + + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_EntryTrashPopup.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_EntryTrashPopup.cshtml new file mode 100644 index 0000000000..4adb8f580c --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_EntryTrashPopup.cshtml @@ -0,0 +1,4 @@ +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@model EntryTrashPopupViewModel + +@Html.Partial("Partials/EntryDetails/_EntryDeletePopupBase", new EntryDeletePopupBaseViewModel(Model.ConfirmText, Model.ViewModelBindingName, Model.DeleteButtonId, ViewRes.EntryEditStrings.MoveToTrash, ViewRes.EntryEditStrings.MoveToTrash)) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_ExternalLinksList.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_ExternalLinksList.cshtml new file mode 100644 index 0000000000..fb4ba0c461 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_ExternalLinksList.cshtml @@ -0,0 +1,20 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@model ExternalLinksListViewModel + +@foreach (var link in Model.WebLinks) +{ + if (link.Disabled) + { + @link.DescriptionOrUrl + } + else + { + @link.DescriptionOrUrl + } + if (Model.ShowCategory) + { + @("(")@Translate.WebLinkCategoryNames[link.Category]@(")") + } +
    +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_ExternalLinksRows.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_ExternalLinksRows.cshtml new file mode 100644 index 0000000000..6bb3fad45e --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_ExternalLinksRows.cshtml @@ -0,0 +1,28 @@ +@using VocaDb.Model.Domain.ExtLinks +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@model ExternalLinksRowsViewModel + +@{ + var official = Model.WebLinks.Where(l => l.Category == WebLinkCategory.Official || l.Category == WebLinkCategory.Commercial).ToArray(); + var other = Model.WebLinks.Where(l => !official.Contains(l)).ToArray(); +} + +@if (official.Any()) +{ + + @ViewRes.EntryDetailsStrings.OfficialLinks + + @Html.Partial("Partials/EntryDetails/_ExternalLinksList", new ExternalLinksListViewModel(official, false)) + + +} + +@if (other.Any()) +{ + + @ViewRes.EntryDetailsStrings.OtherLinks + + @Html.Partial("Partials/EntryDetails/_ExternalLinksList", new ExternalLinksListViewModel(other, true)) + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_HiddenBanner.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_HiddenBanner.cshtml new file mode 100644 index 0000000000..250341a88b --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_HiddenBanner.cshtml @@ -0,0 +1,4 @@ + +
    + @Resources.CommonMessages.RevisionHidden +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_ReportEntryPopupKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_ReportEntryPopupKnockout.cshtml new file mode 100644 index 0000000000..30bbd0d141 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_ReportEntryPopupKnockout.cshtml @@ -0,0 +1,26 @@ +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@model ReportEntryPopupKnockoutViewModel + + +
    + + + + + + + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_ReportEntryVersionPopupKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_ReportEntryVersionPopupKnockout.cshtml new file mode 100644 index 0000000000..2b8848410a --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/EntryDetails/_ReportEntryVersionPopupKnockout.cshtml @@ -0,0 +1,23 @@ +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@model ReportEntryVersionPopupKnockoutViewModel + +
    + +

    + @ViewRes.EntryDetailsStrings.ReportArchivedVersionExplanation +

    + + + + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Event/_PrintArchivedEventData.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Event/_PrintArchivedEventData.cshtml new file mode 100644 index 0000000000..46f5217dc8 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Event/_PrintArchivedEventData.cshtml @@ -0,0 +1,27 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@using VocaDb.Web.Models.Shared.Partials.Event +@model PrintArchivedEventDataViewModel + +
    +

    Content

    + + + @Html.Partial("Partials/ArchivedEntry/_DataRow", new DataRowViewModel("Id", Model.ComparedEvents.FirstData.Id)) + @Html.TranslatedNameRow(Model.ComparedEvents, d => d.TranslatedName) + @Html.DataRowList("Names", Model.ComparedEvents, d => DataFormatUtils.GenerateHtml(d.Names, name => Html.Partial("Partials/ArchivedEntry/_NameInfo", new NameInfoViewModel(name)))) + @Html.DataRow("Description", Model.ComparedEvents, d => d.Description) + @Html.DataRowList("External links", Model.ComparedEvents, d => DataFormatUtils.GenerateHtml(d.WebLinks, webLink => Html.Partial("Partials/ArchivedEntry/_WebLinkInfo", new WebLinkInfoViewModel(webLink)))) + @Html.DataRow("Category", Model.ComparedEvents, d => d.Category) + @Html.DataRow("Date", Model.ComparedEvents, d => d.Date) + @Html.DataRow("Venue", Model.ComparedEvents, d => Html.Partial("Partials/ArchivedEntry/_ObjectRefInfo", new ObjectRefInfoViewModel(d.Venue))) + @Html.DataRow("Venue name", Model.ComparedEvents, d => d.VenueName) + @Html.DataRow("Main picture MIME", Model.ComparedEvents, d => d.MainPictureMime) + @Html.DataRow("Series", Model.ComparedEvents, d => Html.Partial("Partials/ArchivedEntry/_ObjectRefInfo", new ObjectRefInfoViewModel(d.Series))) + @Html.DataRow("Series number", Model.ComparedEvents, d => d.SeriesNumber) + @Html.DataRowList("Artists", Model.ComparedEvents, d => DataFormatUtils.GenerateHtml(d.Artists, artist => Html.Partial("Partials/ArchivedEntry/_ObjectRefInfo", new ObjectRefInfoViewModel(artist)))) + @Html.DataRowList("PVs", Model.ComparedEvents, d => DataFormatUtils.GenerateHtml(d.PVs, pv => Html.Partial("Partials/ArchivedEntry/_PVInfo", new PVInfoViewModel(pv)))) + @Html.DataRow("Song list", Model.ComparedEvents, d => Html.Partial("Partials/ArchivedEntry/_ObjectRefInfo", new ObjectRefInfoViewModel(d.SongList))) +
    + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Event/_PrintArchivedEventSeriesData.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Event/_PrintArchivedEventSeriesData.cshtml new file mode 100644 index 0000000000..f078263d05 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Event/_PrintArchivedEventSeriesData.cshtml @@ -0,0 +1,19 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@using VocaDb.Web.Models.Shared.Partials.Event +@model PrintArchivedEventSeriesDataViewModel + +
    +

    Content

    + + + @Html.Partial("Partials/ArchivedEntry/_DataRow", new DataRowViewModel("Id", Model.ComparedSeries.FirstData.Id)) + @Html.TranslatedNameRow(Model.ComparedSeries, d => d.TranslatedName) + @Html.DataRowList("Names", Model.ComparedSeries, d => DataFormatUtils.GenerateHtml(d.Names, name => Html.Partial("Partials/ArchivedEntry/_NameInfo", new NameInfoViewModel(name)))) + @Html.DataRow("Description", Model.ComparedSeries, d => d.Description) + @Html.DataRowList("External links", Model.ComparedSeries, d => DataFormatUtils.GenerateHtml(d.WebLinks, webLink => Html.Partial("Partials/ArchivedEntry/_WebLinkInfo", new WebLinkInfoViewModel(webLink)))) + @Html.DataRow("Category", Model.ComparedSeries, d => d.Category) + @Html.DataRow("Main picture MIME", Model.ComparedSeries, d => d.MainPictureMime) +
    + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Event/_VenueLinkOrVenueName.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Event/_VenueLinkOrVenueName.cshtml new file mode 100644 index 0000000000..3c30827ebc --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Event/_VenueLinkOrVenueName.cshtml @@ -0,0 +1,12 @@ +@using VocaDb.Web.Models.Shared.Partials.Event +@using VocaDb.Web.Models.Shared.Partials.Venue +@model VenueLinkOrVenueNameViewModel + +@if (Model.Event.Venue != null) +{ + @Html.Partial("Partials/Venue/_VenueLink", new VenueLinkViewModel(Model.Event.Venue)) +} +else if (!string.IsNullOrEmpty(Model.Event.VenueName)) +{ + @Model.Event.VenueName +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Html/_FormatMarkdown.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Html/_FormatMarkdown.cshtml new file mode 100644 index 0000000000..4bdaf08b10 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Html/_FormatMarkdown.cshtml @@ -0,0 +1,11 @@ +@using VocaDb.Web.Models.Shared.Partials.Html +@model FormatMarkdownViewModel + +@* Transforms Markdown-formatted text into HTML. + The input will be sanitized for any HTML tags first. + Note that markdown parser will always surround the block in

    tags, + and

    tags are not allowed inside other

    tags. +*@ + +@Html.Raw(MarkdownParser.GetHtml(Model.Text)) + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Html/_LanguageFlag.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Html/_LanguageFlag.cshtml new file mode 100644 index 0000000000..dcb4443955 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Html/_LanguageFlag.cshtml @@ -0,0 +1,12 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Html +@model LanguageFlagViewModel + +@if (!string.IsNullOrEmpty(Model.LanguageCode)) +{ + @Model.LanguageCode +} +else +{ + @VocaDb.Web.Resources.Domain.Globalization.InterfaceLanguageStrings.Other +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Html/_MetaTag.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Html/_MetaTag.cshtml new file mode 100644 index 0000000000..f824564d91 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Html/_MetaTag.cshtml @@ -0,0 +1,7 @@ +@using VocaDb.Web.Models.Shared.Partials.Html +@model MetaTagViewModel + +@if (!string.IsNullOrEmpty(Model.Content)) +{ + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Html/_OpenGraphMetaTags.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Html/_OpenGraphMetaTags.cshtml new file mode 100644 index 0000000000..3613924717 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Html/_OpenGraphMetaTags.cshtml @@ -0,0 +1,9 @@ +@using VocaDb.Web.Models.Shared.Partials.Html +@model OpenGraphMetaTagsViewModel + +@{ + @Html.Partial("Partials/Html/_MetaTag", new MetaTagViewModel("og:url", Model.PageProperties.CanonicalUrl)) + @Html.Partial("Partials/Html/_MetaTag", new MetaTagViewModel("og:title", Model.PageProperties.OpenGraph.Title)) + @Html.Partial("Partials/Html/_MetaTag", new MetaTagViewModel("og:description", Model.PageProperties.OpenGraph.Description)) + @Html.Partial("Partials/Html/_MetaTag", new MetaTagViewModel("og:type", Model.PageProperties.OpenGraph.Type)) +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_ArtistFilters.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_ArtistFilters.cshtml new file mode 100644 index 0000000000..c86e66db36 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_ArtistFilters.cshtml @@ -0,0 +1,42 @@ +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using VocaDb.Web.Models.Shared.Partials.Song +@model ArtistFiltersViewModel + + + +

    +
    + + + +
    + + +
    +
    + @if (Model.ArtistParticipationStatus) + { + + @Html.Partial("Partials/Song/_ArtistParticipationStatusOptionsKnockout", new ArtistParticipationStatusOptionsKnockoutViewModel("$parent.artistParticipationStatus")) + + } +
    + + +
    + +
    + +
    + +
    + +
    + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_ArtistLockingAutoComplete.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_ArtistLockingAutoComplete.cshtml new file mode 100644 index 0000000000..15b92e03d3 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_ArtistLockingAutoComplete.cshtml @@ -0,0 +1,4 @@ +@using VocaDb.Web.Models.Shared.Partials.Knockout +@model ArtistLockingAutoCompleteViewModel + +@Html.Partial("Partials/Knockout/_LockingAutoComplete", new LockingAutoCompleteViewModel("artistAutoComplete", "{ acceptSelection: " + Model.Binding + ".id, extraQueryParams: { artistTypes: '" + Model.ArtistTypes + "' }, ignoreId: " + Model.IgnoreId + " }", textBinding: Model.Binding + ".name", valBinding: Model.Binding + ".id")) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_BasicEntryLinkLockingAutoComplete.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_BasicEntryLinkLockingAutoComplete.cshtml new file mode 100644 index 0000000000..4af939e80c --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_BasicEntryLinkLockingAutoComplete.cshtml @@ -0,0 +1,8 @@ +@using VocaDb.Web.Models.Shared.Partials.Knockout +@model BasicEntryLinkLockingAutoCompleteViewModel + + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_CheckBoxButtons.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_CheckBoxButtons.cshtml new file mode 100644 index 0000000000..ce5f3979b6 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_CheckBoxButtons.cshtml @@ -0,0 +1,9 @@ +@using VocaDb.Web.Models.Shared.Partials.Knockout +@model CheckBoxButtonsViewModel + +
    + @foreach (var option in Model.Options) + { + + } +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_DraftIcon.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_DraftIcon.cshtml new file mode 100644 index 0000000000..8cacde566f --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_DraftIcon.cshtml @@ -0,0 +1,4 @@ +@using VocaDb.Web.Models.Shared.Partials.Knockout +@model DraftIconViewModel + +draft \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_Dropdown.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_Dropdown.cshtml new file mode 100644 index 0000000000..b86c9ab695 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_Dropdown.cshtml @@ -0,0 +1,15 @@ +@using VocaDb.Web.Models.Shared.Partials.Knockout +@model DropdownViewModel + +
    + + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_DropdownList.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_DropdownList.cshtml new file mode 100644 index 0000000000..c320083134 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_DropdownList.cshtml @@ -0,0 +1,10 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Knockout +@model DropdownListViewModel + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_EntryCount.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_EntryCount.cshtml new file mode 100644 index 0000000000..8e2c984b0a --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_EntryCount.cshtml @@ -0,0 +1,10 @@ + + + @Html.Raw(string.Format(ViewRes.Search.IndexStrings.ShowingItemsOf, "", "")) + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_EntryValidationMessage.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_EntryValidationMessage.cshtml new file mode 100644 index 0000000000..349f6242f4 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_EntryValidationMessage.cshtml @@ -0,0 +1,52 @@ +@using VocaDb.Web.Models.Shared.Partials.Knockout +@model EntryValidationMessageViewModel + +@if (Model.Draft) +{ + +} +else +{ + +} + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_LanguageSelectionDropDown.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_LanguageSelectionDropDown.cshtml new file mode 100644 index 0000000000..18c4df05b2 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_LanguageSelectionDropDown.cshtml @@ -0,0 +1,5 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Knockout +@model LanguageSelectionDropDownViewModel + +@Html.Partial("Partials/Knockout/_DropdownList", new DropdownListViewModel(Translate.ContentLanguageSelectionNames.ValuesAndNamesStrings, Model.ValueBinding)) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_LockingAutoComplete.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_LockingAutoComplete.cshtml new file mode 100644 index 0000000000..a3960eff03 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_LockingAutoComplete.cshtml @@ -0,0 +1,18 @@ +@using VocaDb.Model.Domain +@using VocaDb.Web.Models.Shared.Partials.Knockout +@model LockingAutoCompleteViewModel + +@* + Autocomplete box that allows selection of one item. When an item is selected, "clear" button will be displayed. +*@ + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_SearchDropDown.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_SearchDropDown.cshtml new file mode 100644 index 0000000000..18b0f4550d --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_SearchDropDown.cshtml @@ -0,0 +1,18 @@ +@using VocaDb.Web.Models.Shared.Partials.Knockout +@model SearchDropDownViewModel + +
    + @ViewRes.EntryIndexStrings.SortBy +
    + + +
    +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_ServerSidePaging.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_ServerSidePaging.cshtml new file mode 100644 index 0000000000..8cddf9df51 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_ServerSidePaging.cshtml @@ -0,0 +1,33 @@ + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_SongLockingAutoComplete.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_SongLockingAutoComplete.cshtml new file mode 100644 index 0000000000..cba90fa8bd --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_SongLockingAutoComplete.cshtml @@ -0,0 +1,10 @@ +@using VocaDb.Model.Domain +@using VocaDb.Model.Domain.Songs +@using VocaDb.Web.Models.Shared.Partials.Knockout +@model SongLockingAutoCompleteViewModel + +@* Locking autocomplete for song selection. Allows selection of one (existing) song. When song is selected, clear button is displayed. *@ +@{ + var songTypesStr = Model.SongTypes != SongTypes.Unspecified ? string.Join(",", Model.SongTypes.ToIndividualSelections().Select(t => t.ToString())) : ""; +} +@Html.Partial("Partials/Knockout/_LockingAutoComplete", new LockingAutoCompleteViewModel("songAutoComplete", "{ acceptSelection: " + Model.Binding + ".id, extraQueryParams: { songTypes: '" + songTypesStr + "' }, ignoreId: " + Model.IgnoreId + " }", textBinding: Model.Binding + ".name", valBinding: Model.Binding + ".id", entryType: EntryType.Song)) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_TagFilters.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_TagFilters.cshtml new file mode 100644 index 0000000000..59916ccc5a --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_TagFilters.cshtml @@ -0,0 +1,45 @@ +@using VocaDb.Web.Models.Shared.Partials.Knockout +@model TagFiltersViewModel + + + +
    +
    + + + +
    + + +
    +
    +
    + + +
    + +
    + +
    + + + + @if (Model.TopGenres) + { +
    + + +
    + } + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_WebLinksEditViewKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_WebLinksEditViewKnockout.cshtml new file mode 100644 index 0000000000..e377420fe6 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Knockout/_WebLinksEditViewKnockout.cshtml @@ -0,0 +1,45 @@ +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using Res = ViewRes.Song.EditStrings +@model WebLinksEditViewKnockoutViewModel + + + + + + + + @if (Model.ShowCategory) + { + + } + + + + + + + + + @if (Model.ShowCategory) + { + + } + + + + +
    + @HelperRes.HelperStrings.WebLinkUrlTitle + @Html.Partial("Partials/Shared/_RequiredField") + @HelperRes.HelperStrings.WebLinkDescriptionTitle@HelperRes.HelperStrings.WebLinkCategoryTitle@Res.PvStatus
    + + + + + @ViewRes.SharedStrings.Delete +
    + +@HelperRes.HelperStrings.WebLinkNewRow \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/PV/_EmbedBili.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/PV/_EmbedBili.cshtml new file mode 100644 index 0000000000..c88dbbeb29 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/PV/_EmbedBili.cshtml @@ -0,0 +1,20 @@ +@using VocaDb.Model.Service.VideoServices +@using VocaDb.Web.Models.Shared.Partials.PV +@model EmbedBiliViewModel + +@{ + var meta = Model.PV.ExtendedMetadata != null ? Model.PV.ExtendedMetadata.GetExtendedMetadata() : null; + var widthStr = (Model.Width > 0 ? Model.Width.ToString() : ""); +} + +@if (Model.Height >= 274 && Model.Width >= 480) +{ + var unmaskedHeight = Model.Height + 67; + var unmaskedHeightStr = (Model.Height > 0 ? unmaskedHeight.ToString() : ""); +
    +} +else +{ + var heightStr = (Model.Width > 0 ? Model.Width.ToString() : ""); + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/PV/_EmbedNico.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/PV/_EmbedNico.cshtml new file mode 100644 index 0000000000..e9a3170cab --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/PV/_EmbedNico.cshtml @@ -0,0 +1,6 @@ +@using VocaDb.Web.Models.Shared.Partials.PV +@model EmbedNicoViewModel + +
    + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/PV/_EmbedPV.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/PV/_EmbedPV.cshtml new file mode 100644 index 0000000000..940b1066de --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/PV/_EmbedPV.cshtml @@ -0,0 +1,65 @@ +@using VocaDb.Model.Domain.PVs +@using VocaDb.Model.Service.VideoServices +@using VocaDb.Web.Models.Shared.Partials.PV +@model EmbedPVViewModel + +@{ + var widthStr = (Model.Width > 0 ? Model.Width.ToString() : ""); + var heightStr = (Model.Height > 0 ? Model.Height.ToString() : ""); + Model.Id = Model.Id ?? string.Empty; +} + +@switch (Model.PV.Service) +{ + case PVService.Bandcamp: + + break; + + case PVService.Bilibili: + @Html.Partial("Partials/PV/_EmbedBili", new EmbedBiliViewModel(Model.PV, Model.Width, Model.Height)) + break; + + case PVService.File: + case PVService.LocalFile: + if (LocalFileManager.IsAudio(Model.PV.Url)) + { + + } + else + { +
    + + @Model.PV.Name + +
    + } + break; + + case PVService.NicoNicoDouga: + @Html.Partial("Partials/PV/_EmbedNico", new EmbedNicoViewModel(Model.PV.PVId, widthStr, heightStr, Model.Id, Model.EnableApi)) + break; + + case PVService.Piapro: + @Html.Partial("Partials/PV/_EmbedPiapro", new EmbedPiaproViewModel(Model.PV, widthStr, heightStr)) + break; + + case PVService.SoundCloud: + var scId = new VocaDb.Model.Service.VideoServices.SoundCloudId(Model.PV.PVId); + + break; + + case PVService.Youtube: + var apParam = (Model.Autoplay ? 1 : 0); + var apiParam = (Model.EnableApi ? 1 : 0); + var originParam = (Model.EnableApi ? VocaDb.Model.Utils.VocaUriBuilder.HostAddress : string.Empty); + + break; + + case PVService.Vimeo: + + break; + + case PVService.Creofuga: + + break; +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/PV/_EmbedPiapro.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/PV/_EmbedPiapro.cshtml new file mode 100644 index 0000000000..1d9f72b7f1 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/PV/_EmbedPiapro.cshtml @@ -0,0 +1,22 @@ +@using VocaDb.Model.Service.VideoServices +@using VocaDb.Web.Models.Shared.Partials.PV +@model EmbedPiaproViewModel + +@{ + var meta = Model.PV.ExtendedMetadata != null ? Model.PV.ExtendedMetadata.GetExtendedMetadata() : null; +} + +@if (meta != null && !string.IsNullOrEmpty(meta.Timestamp)) +{ + var prefix = Model.PV.PVId.Substring(0, 2); + +} +else +{ + @* embed instead of iframe because iframe doesn't work with flash disabled *@ + var piaproSwf = string.Format("//piapro.jp/modpub/swf/player03_h.swf?030503&id={0}&c=1", Model.PV.PVId); + + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Search/_AdvancedFilter.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Search/_AdvancedFilter.cshtml new file mode 100644 index 0000000000..f47818a7af --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Search/_AdvancedFilter.cshtml @@ -0,0 +1,8 @@ +@using VocaDb.Web.Models.Shared.Partials.Search +@model AdvancedFilterViewModel + +
  • + + @Model.Description + +
  • \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Search/_AdvancedFilters.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Search/_AdvancedFilters.cshtml new file mode 100644 index 0000000000..199d99b58a --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Search/_AdvancedFilters.cshtml @@ -0,0 +1,21 @@ +@using VocaDb.Web.Models.Shared.Partials.Search +@model AdvancedFiltersViewModel + +
    + + +
    +
    +
    + + +
    +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_AjaxLoader.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_AjaxLoader.cshtml new file mode 100644 index 0000000000..f3aad93ef6 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_AjaxLoader.cshtml @@ -0,0 +1,11 @@ +@using VocaDb.Web.Models.Shared.Partials.Shared +@model AjaxLoaderViewModel + +@if (Model.Id != null) +{ + +} +else +{ + loading... +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_DraftIcon.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_DraftIcon.cshtml new file mode 100644 index 0000000000..88bda0ca3e --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_DraftIcon.cshtml @@ -0,0 +1,8 @@ +@using VocaDb.Model.Domain +@using VocaDb.Web.Models.Shared.Partials.Shared +@model DraftIconViewModel + +@if (Model.Status == EntryStatus.Draft) +{ + draft +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_DraftMessage.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_DraftMessage.cshtml new file mode 100644 index 0000000000..feb68ebe0f --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_DraftMessage.cshtml @@ -0,0 +1,8 @@ +@using VocaDb.Web.Models.Shared.Partials.Shared +@model DraftMessageViewModel + +
    + @Html.Partial("Partials/Shared/_NotificationIcon") + @HelperRes.HelperStrings.DraftMessage + @HelperRes.HelperStrings.SeeGuidePre @HelperRes.HelperStrings.SeeGuide @HelperRes.HelperStrings.SeeGuidePost +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EmbedGoogleMaps.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EmbedGoogleMaps.cshtml new file mode 100644 index 0000000000..6b8bf969d1 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EmbedGoogleMaps.cshtml @@ -0,0 +1,7 @@ +@using VocaDb.Model.Helpers +@using VocaDb.Web.Models.Shared.Partials.Shared +@model EmbedGoogleMapsViewModel + +
    + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EntryLink.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EntryLink.cshtml new file mode 100644 index 0000000000..0861fe6b6c --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EntryLink.cshtml @@ -0,0 +1,4 @@ +@using VocaDb.Web.Models.Shared.Partials.Shared +@model EntryLinkViewModel + +@Html.ActionLink(Model.Entry.DefaultName, "Details", Model.Entry.EntryType.ToString(), new { id = Model.Entry.Id }, null) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EntryPictureFileLink.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EntryPictureFileLink.cshtml new file mode 100644 index 0000000000..e5ff851d59 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EntryPictureFileLink.cshtml @@ -0,0 +1,8 @@ +@using VocaDb.Model.Domain.Images +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Shared +@model EntryPictureFileLinkViewModel + + + Preview + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EntrySearchNameBox.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EntrySearchNameBox.cshtml new file mode 100644 index 0000000000..c3a0d5992b --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EntrySearchNameBox.cshtml @@ -0,0 +1,4 @@ +@using VocaDb.Web.Models.Shared.Partials.Shared +@model EntrySearchNameBoxViewModel + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EntryStatusMessage.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EntryStatusMessage.cshtml new file mode 100644 index 0000000000..d09f7c1dda --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EntryStatusMessage.cshtml @@ -0,0 +1,36 @@ +@using VocaDb.Model.Domain +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Shared +@model EntryStatusMessageViewModel + +@switch (Model.Status) +{ + case EntryStatus.Draft: + + + @Translate.EntryStatusNames[EntryStatus.Draft] + + break; + + case EntryStatus.Finished: + + + @Translate.EntryStatusNames[EntryStatus.Finished] + + break; + + case EntryStatus.Approved: + + + @Translate.EntryStatusNames[EntryStatus.Approved] + + break; + + case EntryStatus.Locked: + + + @Translate.EntryStatusNames[EntryStatus.Locked] + + break; + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EventSeriesThumbs.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EventSeriesThumbs.cshtml new file mode 100644 index 0000000000..553c086f9c --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EventSeriesThumbs.cshtml @@ -0,0 +1,28 @@ +@using VocaDb.Model.Domain.Images +@using VocaDb.Model.Domain.ReleaseEvents +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Shared +@model EventSeriesThumbsViewModel + +
    + @foreach (var ev in Model.Events) + { +
    + @if (ev.PictureMime != null) + { + + Thumb + + } +
    + + @ev.Name + + @if (ev.Category != EventCategory.Unspecified && ev.Category != EventCategory.Other) + { + @:(@Translate.ReleaseEventCategoryNames[ev.Category]) + } +
    +
    + } +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EventThumbs.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EventThumbs.cshtml new file mode 100644 index 0000000000..26627af7b3 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_EventThumbs.cshtml @@ -0,0 +1,52 @@ +@using VocaDb.Model.Domain.Images +@using VocaDb.Model.Domain.ReleaseEvents +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Event +@using VocaDb.Web.Models.Shared.Partials.Shared +@model EventThumbsViewModel + +@{ + var css = Model.ImageSize == ImageSize.SmallThumb ? "event-teaser" : "event-teaser-tiny"; +} + +
    + @foreach (var ev in Model.Events) + { + var cat = ev.Series != null ? ev.Series.Category : ev.Category; +
    + @if (ev.MainPicture != null) + { + + Thumb + + } +
    + + @ev.Name + + @if (cat != EventCategory.Unspecified && cat != EventCategory.Other) + { + @:(@Translate.ReleaseEventCategoryNames[cat]) + } + @if (ev.Date.HasValue) + { +
    + + @(ev.Date.Value.ToShortDateString()) + @if (ev.EndDate.HasValue) + { + @:- @ev.EndDate.Value.ToShortDateString() + } + + } + @if (ev.HasVenueOrVenueName) + { +
    + + @Html.Partial("Partials/Event/_VenueLinkOrVenueName", new VenueLinkOrVenueNameViewModel(ev)) + + } +
    +
    + } +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_FormatPVLink.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_FormatPVLink.cshtml new file mode 100644 index 0000000000..72bafb875c --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_FormatPVLink.cshtml @@ -0,0 +1,11 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Shared +@model FormatPVLinkViewModel + + + @Model.PV.Service @(!string.IsNullOrEmpty(Model.PV.Name) ? Model.PV.Name : Model.PV.Service.ToString()) + @if (Model.Type) + { + @("(" + Translate.PVTypeNames[Model.PV.PVType] + ")") + } + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_HelpLabel.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_HelpLabel.cshtml new file mode 100644 index 0000000000..e59c55c610 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_HelpLabel.cshtml @@ -0,0 +1,5 @@ +@using VocaDb.Web.Models.Shared.Partials.Shared +@model HelpLabelViewModel + +@* Displays label element with attached qTip tooltip *@ + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ImageUploadMessage.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ImageUploadMessage.cshtml new file mode 100644 index 0000000000..dc47388cd5 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ImageUploadMessage.cshtml @@ -0,0 +1,5 @@ +@using VocaDb.Model.Helpers + +

    + @string.Format(HelperRes.HelperStrings.UploadPictureInfo, string.Join(", ", ImageHelper.AllowedExtensions), ImageHelper.MaxImageSizeMB) +

    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_MarkdownNotice.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_MarkdownNotice.cshtml new file mode 100644 index 0000000000..3c15236b4f --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_MarkdownNotice.cshtml @@ -0,0 +1,2 @@ + +(@HelperRes.HelperStrings.GitHubMarkdown @HelperRes.HelperStrings.IsSupportedForFormatting) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_MergeEntryInfo.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_MergeEntryInfo.cshtml new file mode 100644 index 0000000000..d79e6c0f4e --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_MergeEntryInfo.cshtml @@ -0,0 +1,7 @@ + +
    +

    @ViewRes.EntryMergeStrings.MergeWarning

    +
    +
    + @ViewRes.EntryMergeStrings.MergeInfo +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_NotificationIcon.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_NotificationIcon.cshtml new file mode 100644 index 0000000000..1988bbbd90 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_NotificationIcon.cshtml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_NotificationPanel.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_NotificationPanel.cshtml new file mode 100644 index 0000000000..047855a8f7 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_NotificationPanel.cshtml @@ -0,0 +1,7 @@ +@using VocaDb.Web.Models.Shared.Partials.Shared +@model NotificationPanelViewModel + +
    + @Html.Partial("Partials/Shared/_NotificationIcon") + @Model.Message +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_PVServiceIcon.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_PVServiceIcon.cshtml new file mode 100644 index 0000000000..e31f2d1c36 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_PVServiceIcon.cshtml @@ -0,0 +1,9 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Shared +@model PVServiceIconViewModel + +@{ + var iconUrl = @Html.VideoServiceLinkUrl(Model.Service); +} + +@Model.Service.ToString() \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_PVServiceIcons.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_PVServiceIcons.cshtml new file mode 100644 index 0000000000..2566908d42 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_PVServiceIcons.cshtml @@ -0,0 +1,9 @@ +@using VocaDb.Model +@using VocaDb.Model.Domain.PVs +@using VocaDb.Web.Models.Shared.Partials.Shared +@model PVServiceIconsViewModel + +@foreach (var service in EnumVal.Values.Where(service => Model.Services.HasFlag((PVServices)service))) +{ + @Html.Partial("Partials/Shared/_PVServiceIcon", new PVServiceIconViewModel(service)) +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ReCaptcha2.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ReCaptcha2.cshtml new file mode 100644 index 0000000000..05038f6e7c --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ReCaptcha2.cshtml @@ -0,0 +1,3 @@ +@using VocaDb.Model.Utils + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_RequiredField.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_RequiredField.cshtml new file mode 100644 index 0000000000..08f5a4dc6a --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_RequiredField.cshtml @@ -0,0 +1,2 @@ + +* \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_SaveAndBackBtn.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_SaveAndBackBtn.cshtml new file mode 100644 index 0000000000..18d91f7c60 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_SaveAndBackBtn.cshtml @@ -0,0 +1,13 @@ +@using VocaDb.Web.Models.Shared.Partials.Shared +@model SaveAndBackBtnViewModel + +

    + + + +  @HelperRes.HelperStrings.ReturnWithoutSaving + +

    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_SaveBtn.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_SaveBtn.cshtml new file mode 100644 index 0000000000..3e75f34521 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_SaveBtn.cshtml @@ -0,0 +1,7 @@ + +

    + +

    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ShowMore.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ShowMore.cshtml new file mode 100644 index 0000000000..83e8510e64 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ShowMore.cshtml @@ -0,0 +1,8 @@ +@using VocaDb.Web.Models.Shared.Partials.Shared +@model ShowMoreViewModel + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_Stars.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_Stars.cshtml new file mode 100644 index 0000000000..2fe473a759 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_Stars.cshtml @@ -0,0 +1,24 @@ +@using VocaDb.Web.Code +@using VocaDb.Web.Models.Shared.Partials.Shared +@model StarsViewModel + +@functions { + + string GetAbsoluteUrl(string relative) + { + return UrlMapper.FullAbsolute(relative); + } + +} + +@for (int i = 1; i <= Model.Max; ++i) +{ + if (Model.Current >= i) + { + * + } + else + { + + } +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_StarsMeta.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_StarsMeta.cshtml new file mode 100644 index 0000000000..3ba30c061d --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_StarsMeta.cshtml @@ -0,0 +1,10 @@ +@using VocaDb.Web.Models.Shared.Partials.Shared +@model StarsMetaViewModel + +@for (int i = 1; i <= Model.Max; ++i) { + if (Model.Current >= i) { + @Model.Current + } else { + @Model.Current + } +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_StarsMetaSpan.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_StarsMetaSpan.cshtml new file mode 100644 index 0000000000..124ff1a408 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_StarsMetaSpan.cshtml @@ -0,0 +1,7 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Shared +@model StarsMetaSpanViewModel + + + @Html.Partial("Partials/Shared/_StarsMeta", new StarsMetaViewModel((int)Math.Round(Model.Current, MidpointRounding.AwayFromZero), Model.Max)) + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ThumbItem.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ThumbItem.cshtml new file mode 100644 index 0000000000..22d4322667 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ThumbItem.cshtml @@ -0,0 +1,21 @@ +@using VocaDb.Web.Models.Shared.Partials.Shared +@model ThumbItemViewModel + +
  • + +
    + @if (Model.Entry != null) + { + Preview + } + else + { + Preview + } +
    +
    + @if (!string.IsNullOrEmpty(Model.Caption)) + { +

    @Model.Caption

    + } +
  • \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_UniversalTimeLabel.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_UniversalTimeLabel.cshtml new file mode 100644 index 0000000000..7fb1b81b6d --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_UniversalTimeLabel.cshtml @@ -0,0 +1,4 @@ +@using VocaDb.Web.Models.Shared.Partials.Shared +@model UniversalTimeLabelViewModel + +@Model.DateTime.ToUniversalTime() \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ValidationSummaryDiv.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ValidationSummaryDiv.cshtml new file mode 100644 index 0000000000..e2ac795714 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ValidationSummaryDiv.cshtml @@ -0,0 +1,4 @@ +@using VocaDb.Web.Models.Shared.Partials.Shared +@model ValidationSummaryDivViewModel + +@Html.Partial("Partials/Shared/_ValidationSummaryPanel", new ValidationSummaryPanelViewModel(Model.Message)) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ValidationSummaryPanel.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ValidationSummaryPanel.cshtml new file mode 100644 index 0000000000..663bb15d5a --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Shared/_ValidationSummaryPanel.cshtml @@ -0,0 +1,12 @@ +@using VocaDb.Web.Models.Shared.Partials.Shared +@model ValidationSummaryPanelViewModel + +@if (!ViewData.ModelState.IsValid) +{ +
    +

    + @Model.Message +

    + @Html.ValidationSummary(false) +
    +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Song/_ArtistParticipationStatusOptionsKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Song/_ArtistParticipationStatusOptionsKnockout.cshtml new file mode 100644 index 0000000000..a69ebcd46c --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Song/_ArtistParticipationStatusOptionsKnockout.cshtml @@ -0,0 +1,14 @@ +@using VocaDb.Model.Service.Search.AlbumSearch +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using VocaDb.Web.Models.Shared.Partials.Song +@model ArtistParticipationStatusOptionsKnockoutViewModel + +@{ + var options = new Dictionary + { + { ArtistAlbumParticipationStatus.Everything.ToString(), "Everything" }, + { ArtistAlbumParticipationStatus.OnlyMainAlbums.ToString(), "Only main songs" }, + { ArtistAlbumParticipationStatus.OnlyCollaborations.ToString(), "Only collaborations" }, + }; + @Html.Partial("Partials/Knockout/_CheckBoxButtons", new CheckBoxButtonsViewModel(options, Model.Binding)) +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Song/_LyricsInfo.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Song/_LyricsInfo.cshtml new file mode 100644 index 0000000000..c68e450dbb --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Song/_LyricsInfo.cshtml @@ -0,0 +1,13 @@ +@using System.Globalization +@using VocaDb.Web.Models.Shared.Partials.Song +@model LyricsInfoViewModel + +@Model.Lyrics.TranslationType +@if (!string.IsNullOrEmpty(Model.Lyrics.CultureCode)) +{ + @:(@CultureInfo.GetCultureInfo(Model.Lyrics.CultureCode).EnglishName) +} +@if (!string.IsNullOrEmpty(Model.Lyrics.Source)) +{ + @:(@Model.Lyrics.Source) +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Song/_PVPreviewKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Song/_PVPreviewKnockout.cshtml new file mode 100644 index 0000000000..1b88838635 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Song/_PVPreviewKnockout.cshtml @@ -0,0 +1,21 @@ +@using VocaDb.Web.Models.Shared.Partials.Song +@model PVPreviewKnockoutViewModel + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Song/_PrintArchivedSongData.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Song/_PrintArchivedSongData.cshtml new file mode 100644 index 0000000000..3d6cc298e0 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Song/_PrintArchivedSongData.cshtml @@ -0,0 +1,27 @@ +@using Microsoft.AspNetCore.Html +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@using VocaDb.Web.Models.Shared.Partials.Song +@model PrintArchivedSongDataViewModel + +
    +

    Content

    + + + @Html.Partial("Partials/ArchivedEntry/_DataRow", new DataRowViewModel("Id", Model.ComparedSongs.FirstData.Id)) + @Html.TranslatedNameRow(Model.ComparedSongs, d => d.TranslatedName) + @Html.DataRowList("Names", Model.ComparedSongs, d => DataFormatUtils.GenerateHtml(d.Names, name => Html.Partial("Partials/ArchivedEntry/_NameInfo", new NameInfoViewModel(name)))) + @Html.DataRow("Notes", Model.ComparedSongs, d => d.Notes, preserveLineBreaks: true) + @Html.DataRow("Notes (en)", Model.ComparedSongs, d => d.NotesEng, preserveLineBreaks: true) + @Html.DataRow("Song type", Model.ComparedSongs, d => d.SongType) + @Html.DataRow("Original version", Model.ComparedSongs, d => Html.Partial("Partials/ArchivedEntry/_ObjectRefInfo", new ObjectRefInfoViewModel(d.OriginalVersion))) + @Html.DataRow("Duration", Model.ComparedSongs, d => DateTimeUtils.FormatFromSeconds(d.LengthSeconds)) + @Html.DataRow("Release event", Model.ComparedSongs, d => Html.Partial("Partials/ArchivedEntry/_ObjectRefInfo", new ObjectRefInfoViewModel(d.ReleaseEvent))) + @Html.DataRow("Publish date", Model.ComparedSongs, d => d.PublishDate) + @Html.DataRowList("External links", Model.ComparedSongs, d => DataFormatUtils.GenerateHtml(d.WebLinks, webLink => Html.Partial("Partials/ArchivedEntry/_WebLinkInfo", new WebLinkInfoViewModel(webLink)))) + @Html.DataRowList("Artists", Model.ComparedSongs, d => DataFormatUtils.GenerateHtml(d.Artists != null ? (d.Artists.OrderBy(a => a.NameHint)) : null, artist => new HtmlString(artist.NameHint + " [" + artist.Id + "] - IsSupport: " + artist.IsSupport + ", Roles: " + artist.Roles))) + @Html.DataRowList("PVs", Model.ComparedSongs, d => DataFormatUtils.GenerateHtml(d.PVs, pv => Html.Partial("Partials/ArchivedEntry/_PVInfo", new PVInfoViewModel(pv)))) + @Html.DataRowList("Lyrics", Model.ComparedSongs, d => DataFormatUtils.GenerateHtml(d.Lyrics, lyrics => Html.Partial("Partials/Song/_LyricsInfo", new LyricsInfoViewModel(lyrics)))) +
    + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Song/_RatingIcon.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Song/_RatingIcon.cshtml new file mode 100644 index 0000000000..acec840282 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Song/_RatingIcon.cshtml @@ -0,0 +1,13 @@ +@using VocaDb.Model.Domain.Songs +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Song +@model RatingIconViewModel + +@if (Model.Rating == SongVoteRating.Like) +{ + +} +@if (Model.Rating == SongVoteRating.Favorite) +{ + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongGrid.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongGrid.cshtml new file mode 100644 index 0000000000..d632a78dd5 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongGrid.cshtml @@ -0,0 +1,58 @@ +@using VocaDb.Model.DataContracts.Songs +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Song +@model SongGridViewModel + +@{ + void SongIconLink(SongForApiContract song) { + + Cover + + } + + void SongLink(SongForApiContract song, int? albumId = null) { + + @song.Name + + } +} + + + @{ int i = 0; } + + @foreach (var song in Model.Songs) + { + if (i % Model.Columns == 0) + { + @Html.Raw("") + } + + + { i++; } + if (i % Model.Columns == 0) + { + @Html.Raw("") + } + } + @if (i % Model.Columns != 0) + { + @Html.Raw("") + } +
    + @if (!string.IsNullOrEmpty(song.ThumbUrl)) + { + SongIconLink(song); + } + + @{ SongLink(song); } + @if (Model.DisplayType) + { + @Html.Partial("Partials/Song/_SongTypeLabel", new SongTypeLabelViewModel(song.SongType)) + } + @if (Model.DisplayPublishDate && song.PublishDate.HasValue) + { + + } +
    + @song.ArtistString +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongIconLink.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongIconLink.cshtml new file mode 100644 index 0000000000..24ec0bbe37 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongIconLink.cshtml @@ -0,0 +1,7 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Song +@model SongIconLinkViewModel + + + Cover + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongLink.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongLink.cshtml new file mode 100644 index 0000000000..d4d3c26510 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongLink.cshtml @@ -0,0 +1,5 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Song +@model SongLinkViewModel + +@Html.ActionLink(Model.Song.Name, "Details", "Song", ViewHelper.GetRouteValueDictionary(Model.Song, Model.AlbumId), new Dictionary { { "title", Model.Song.AdditionalNames }, { "data-bind", Model.Tooltip ? "songToolTip: " + Model.Song.Id : null } }) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongLinkKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongLinkKnockout.cshtml new file mode 100644 index 0000000000..462bdd6c3c --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongLinkKnockout.cshtml @@ -0,0 +1,4 @@ +@using VocaDb.Web.Models.Shared.Partials.Song +@model SongLinkKnockoutViewModel + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongListsKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongListsKnockout.cshtml new file mode 100644 index 0000000000..2014941f51 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongListsKnockout.cshtml @@ -0,0 +1,35 @@ +@using VocaDb.Web.Models.Shared.Partials.Song +@model SongListsKnockoutViewModel + + + + + + + + + + + + + + +
    +

    {{ $data.eventDate | formatDate: 'YYYY' }}

    +
    + + Picture + + + {{ name }} +
    + {{ $data.eventDate | formatDate: 'l' }} +
    +
    +
    + + + , + +
    +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongTypeLabel.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongTypeLabel.cshtml new file mode 100644 index 0000000000..195b62be4a --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongTypeLabel.cshtml @@ -0,0 +1,47 @@ +@using VocaDb.Model.Domain.Songs +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Song +@model SongTypeLabelViewModel + +@switch (Model.SongType) +{ + case SongType.Arrangement: + A + break; + + case SongType.Cover: + C + break; + + case SongType.DramaPV: + D + break; + + case SongType.Instrumental: + I + break; + + case SongType.Mashup: + M + break; + + case SongType.Original: + O + break; + + case SongType.Other: + O + break; + + case SongType.Remaster: + R + break; + + case SongType.Remix: + R + break; + + case SongType.MusicPV: + PV + break; +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongTypesDropdownKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongTypesDropdownKnockout.cshtml new file mode 100644 index 0000000000..a6f7efd668 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Song/_SongTypesDropdownKnockout.cshtml @@ -0,0 +1,7 @@ +@using VocaDb.Model.Utils +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using VocaDb.Web.Models.Shared.Partials.Song +@model SongTypesDropdownKnockoutViewModel + +@Html.Partial("Partials/Knockout/_CheckBoxButtons", new CheckBoxButtonsViewModel(Translate.SongTypeNames.GetValuesAndNamesStrings(AppConfig.SongTypes), Model.Binding)) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Tag/_PrintArchivedTagData.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Tag/_PrintArchivedTagData.cshtml new file mode 100644 index 0000000000..41952a087f --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Tag/_PrintArchivedTagData.cshtml @@ -0,0 +1,22 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@using VocaDb.Web.Models.Shared.Partials.Tag +@model PrintArchivedTagDataViewModel + +
    +

    Content

    + + + @Html.Partial("Partials/ArchivedEntry/_DataRow", new DataRowViewModel("Id", Model.ComparedTags.FirstData.Id)) + @Html.TranslatedNameRow(Model.ComparedTags, d => d.TranslatedName) + @Html.DataRowList("Names", Model.ComparedTags, d => DataFormatUtils.GenerateHtml(d.Names, name => Html.Partial("Partials/ArchivedEntry/_NameInfo", new NameInfoViewModel(name)))) + @Html.DataRow("Description", Model.ComparedTags, d => d.Description) + @Html.DataRow("Description English", Model.ComparedTags, d => d.DescriptionEng) + @Html.ObjectRefList("Related tags", Model.ComparedTags, d => d.RelatedTags) + @Html.DataRow("Valid for", Model.ComparedTags, d => d.Targets) + @Html.DataRowList("External links", Model.ComparedTags, d => DataFormatUtils.GenerateHtml(d.WebLinks, webLink => Html.Partial("Partials/ArchivedEntry/_WebLinkInfo", new WebLinkInfoViewModel(webLink)))) + @Html.DataRow("Category name", Model.ComparedTags, d => d.CategoryName) + @Html.DataRow("Parent", Model.ComparedTags, d => Html.Partial("Partials/ArchivedEntry/_ObjectRefInfo", new ObjectRefInfoViewModel(d.Parent))) +
    + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Tag/_TagLink.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Tag/_TagLink.cshtml new file mode 100644 index 0000000000..960448cb6d --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Tag/_TagLink.cshtml @@ -0,0 +1,5 @@ +@using Microsoft.AspNetCore.Routing +@using VocaDb.Web.Models.Shared.Partials.Tag +@model TagLinkViewModel + +@Html.ActionLink(Model.Tag.Name, "DetailsById", "Tag", new RouteValueDictionary { { "id", Model.Tag.Id }, { "slug", Model.Tag.UrlSlug } }, new Dictionary { { "title", Model.Tag.AdditionalNames }, { "data-bind", Model.Tooltip ? "tagToolTip: " + Model.Tag.Id : null } }) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Tag/_TagList.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Tag/_TagList.cshtml new file mode 100644 index 0000000000..7a3c0ab003 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Tag/_TagList.cshtml @@ -0,0 +1,5 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Tag +@model TagListViewModel + +@Html.LinkListHtml(Model.TagNames, t => Html.Partial("Partials/Tag/_TagLink", new TagLinkViewModel(t, Model.Tooltip))) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Tag/_TagUsagesManageTable.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Tag/_TagUsagesManageTable.cshtml new file mode 100644 index 0000000000..1f9ddbea64 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Tag/_TagUsagesManageTable.cshtml @@ -0,0 +1,58 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Tag +@using VocaDb.Web.Models.Shared.Partials.User +@model TagUsagesManageTableViewModel +@inject Login Login + +
    + You can use this to deassociate tags that are no longer valid for this entry. Be careful, this cannot be undone. +
    + +@if (Model.TagUsages.Any()) +{ + + + + + + @if (Login.CanManageUsers) + { + + } + + + + + + @foreach (var usage in Model.TagUsages) + { + + + + @if (Login.CanManageUsers) + { + + } + + + + } + +
    TagCountVotesDate
    @Html.Partial("Partials/Tag/_TagLink", new TagLinkViewModel(usage.Tag))@usage.Count + @foreach (var user in usage.Votes) + { + @Html.Partial("Partials/User/_UserIconLink_UserContract", new UserIconLink_UserContractViewModel(user))
    + } +
    + @usage.Date.ToShortDateString() + + @if (Model.CanRemove) + { + @Html.ActionLink("Remove tag usage", "RemoveTagUsage", Model.ControllerName ?? Model.EntryType.ToString(), new { id = usage.Id }, new { @class = "removeLink textLink", @onclick = "javascript: return confirm('Really delete?');" }) + } +
    +} +else +{ + @:No tags +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/User/_IconAndLinkKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/User/_IconAndLinkKnockout.cshtml new file mode 100644 index 0000000000..c51bbfb28d --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/User/_IconAndLinkKnockout.cshtml @@ -0,0 +1,8 @@ +@using VocaDb.Web.Models.Shared.Partials.User +@model IconAndLinkKnockoutViewModel + +@* User icon inside anchor (no name) *@ + + + @Html.Partial("Partials/User/_ProfileIconKnockout_ImageSize", new ProfileIconKnockout_ImageSizeViewModel(VocaDb.Model.Domain.Images.ImageSize.Thumb, binding: Model.UserBinding, size: 70)) + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/User/_IconAndNameKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/User/_IconAndNameKnockout.cshtml new file mode 100644 index 0000000000..8f420a6bc6 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/User/_IconAndNameKnockout.cshtml @@ -0,0 +1,5 @@ +@using VocaDb.Web.Models.Shared.Partials.User +@model IconAndNameKnockoutViewModel + +@Html.Partial("Partials/User/_ProfileIconKnockout", new ProfileIconKnockoutViewModel(Model.IconBinding, Model.Size)) + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/User/_IconAndNameLinkKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/User/_IconAndNameLinkKnockout.cshtml new file mode 100644 index 0000000000..2161973066 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/User/_IconAndNameLinkKnockout.cshtml @@ -0,0 +1,5 @@ +@using VocaDb.Web.Models.Shared.Partials.User +@model IconAndNameLinkKnockoutViewModel + +@* Has to be one line because of Razor *@ +@Html.Partial("Partials/User/_ProfileIconKnockout", new ProfileIconKnockoutViewModel("$data.mainPicture != null ? $data.mainPicture.urlThumb : null", Model.IconSize)) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/User/_IconNameAndTypeLinkKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/User/_IconNameAndTypeLinkKnockout.cshtml new file mode 100644 index 0000000000..1b1aeedcaf --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/User/_IconNameAndTypeLinkKnockout.cshtml @@ -0,0 +1,7 @@ +@using VocaDb.Web.Models.Shared.Partials.User +@model IconNameAndTypeLinkKnockoutViewModel + + + @Html.Partial("Partials/User/_ProfileIconKnockout", new ProfileIconKnockoutViewModel("$data.mainPicture ? mainPicture.urlThumb : null", Model.IconSize)) + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/User/_NameLinkKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/User/_NameLinkKnockout.cshtml new file mode 100644 index 0000000000..59a2275ff8 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/User/_NameLinkKnockout.cshtml @@ -0,0 +1,5 @@ +@using VocaDb.Web.Models.Shared.Partials.User +@model NameLinkKnockoutViewModel + +@* User name inside anchor *@ + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/User/_ProfileIcon.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/User/_ProfileIcon.cshtml new file mode 100644 index 0000000000..5355072267 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/User/_ProfileIcon.cshtml @@ -0,0 +1,8 @@ +@using VocaDb.Web.Models.Shared.Partials.User +@model ProfileIconViewModel + +@if (!string.IsNullOrEmpty(Model.Url)) { +
    + Icon +
    +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/User/_ProfileIconKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/User/_ProfileIconKnockout.cshtml new file mode 100644 index 0000000000..69c328c661 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/User/_ProfileIconKnockout.cshtml @@ -0,0 +1,6 @@ +@using VocaDb.Web.Models.Shared.Partials.User +@model ProfileIconKnockoutViewModel + +
    + User avatar +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/User/_ProfileIconKnockout_ImageSize.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/User/_ProfileIconKnockout_ImageSize.cshtml new file mode 100644 index 0000000000..f816768077 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/User/_ProfileIconKnockout_ImageSize.cshtml @@ -0,0 +1,24 @@ +@using VocaDb.Model.Domain.Images +@using VocaDb.Model.Helpers +@using VocaDb.Web.Models.Shared.Partials.User +@model ProfileIconKnockout_ImageSizeViewModel + +@functions { + + static string GetImageSizeProperty(ImageSize size) + { + switch (size) + { + case ImageSize.Thumb: + return "urlThumb"; + case ImageSize.SmallThumb: + return "urlSmallThumb"; + case ImageSize.TinyThumb: + return "urlTinyThumb"; + } + return "urlThumb"; + } + +} + +@Html.Partial("Partials/User/_ProfileIconKnockout", new ProfileIconKnockoutViewModel(Model.Binding + ".mainPicture != null ? " + Model.Binding + ".mainPicture." + GetImageSizeProperty(Model.ImageSize) + " : null", Model.Size > 0 ? Model.Size : ImageHelper.GetUserImageSizePx(Model.ImageSize))) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/User/_ProfileIcon_IUserWithEmail.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/User/_ProfileIcon_IUserWithEmail.cshtml new file mode 100644 index 0000000000..bd33ca1c7e --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/User/_ProfileIcon_IUserWithEmail.cshtml @@ -0,0 +1,9 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.User +@model ProfileIcon_IUserWithEmailViewModel + +@if (Model.User != null && !string.IsNullOrEmpty(Model.User.Email)) { +
    + @Gravatar.GetHtml(Model.User.Email, Model.Size) +
    +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/User/_SongVoteRatingsRadioKnockout.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/User/_SongVoteRatingsRadioKnockout.cshtml new file mode 100644 index 0000000000..097a01018e --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/User/_SongVoteRatingsRadioKnockout.cshtml @@ -0,0 +1,13 @@ +@using Resources +@using VocaDb.Web.Models.Shared.Partials.User +@model SongVoteRatingsRadioKnockoutViewModel + + + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/User/_UserIconLinkOrName.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/User/_UserIconLinkOrName.cshtml new file mode 100644 index 0000000000..fe6e6dca6b --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/User/_UserIconLinkOrName.cshtml @@ -0,0 +1,8 @@ +@using VocaDb.Web.Models.Shared.Partials.User +@model UserIconLinkOrNameViewModel + +@if (Model.User != null) { + @Html.Partial("Partials/User/_UserIconLink_IUserWithEmail", new UserIconLink_IUserWithEmailViewModel(Model.User, Model.Size)) +} else { + @Model.Name +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/User/_UserIconLink_IUserWithEmail.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/User/_UserIconLink_IUserWithEmail.cshtml new file mode 100644 index 0000000000..3217a0aff6 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/User/_UserIconLink_IUserWithEmail.cshtml @@ -0,0 +1,5 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.User +@model UserIconLink_IUserWithEmailViewModel + +@Html.Partial("Partials/User/_ProfileIcon_IUserWithEmail", new ProfileIcon_IUserWithEmailViewModel(Model.User, Model.Size))@(Model.User != null ? Model.User.Name : "") \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/User/_UserIconLink_UserContract.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/User/_UserIconLink_UserContract.cshtml new file mode 100644 index 0000000000..68c218693b --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/User/_UserIconLink_UserContract.cshtml @@ -0,0 +1,6 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.User +@model UserIconLink_UserContractViewModel + +@Html.Partial("Partials/User/_ProfileIcon_IUserWithEmail", new ProfileIcon_IUserWithEmailViewModel(Model.User, Model.Size))@(Model.User != null ? Model.User.Name : "") \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/User/_UserIconLink_UserForApiContract.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/User/_UserIconLink_UserForApiContract.cshtml new file mode 100644 index 0000000000..1ddd834620 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/User/_UserIconLink_UserForApiContract.cshtml @@ -0,0 +1,10 @@ +@using VocaDb.Model.Domain.Images +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.User +@model UserIconLink_UserForApiContractViewModel + + + @Html.Partial("Partials/User/_ProfileIcon", new ProfileIconViewModel(Model.User != null && Model.User.MainPicture != null ? Model.User.MainPicture.GetSmallestThumb(ImageSize.TinyThumb) : null, Model.Size)) + @(Model.User != null ? Model.User.Name : "") + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/User/_UserLink.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/User/_UserLink.cshtml new file mode 100644 index 0000000000..2aa8ddd6e2 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/User/_UserLink.cshtml @@ -0,0 +1,4 @@ +@using VocaDb.Web.Models.Shared.Partials.User +@model UserLinkViewModel + +@Html.ActionLink(Model.User.Name, "Profile", "User", new { id = Model.User.Name }, null) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/User/_UserLinkOrName.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/User/_UserLinkOrName.cshtml new file mode 100644 index 0000000000..64e6f89485 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/User/_UserLinkOrName.cshtml @@ -0,0 +1,8 @@ +@using VocaDb.Web.Models.Shared.Partials.User +@model UserLinkOrNameViewModel + +@if (Model.User != null) { + @Html.Partial("Partials/User/_UserLink", new UserLinkViewModel(Model.User)) +} else { + @Model.Name +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Venue/_PrintArchivedVenueData.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Venue/_PrintArchivedVenueData.cshtml new file mode 100644 index 0000000000..96cdad9803 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Venue/_PrintArchivedVenueData.cshtml @@ -0,0 +1,21 @@ +@using System.Globalization +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@using VocaDb.Web.Models.Shared.Partials.Venue +@model PrintArchivedVenueDataViewModel + +
    +

    Content

    + + + @Html.Partial("Partials/ArchivedEntry/_DataRow", new DataRowViewModel("Id", Model.ComparedVenues.FirstData.Id)) + @Html.TranslatedNameRow(Model.ComparedVenues, d => d.TranslatedName) + @Html.DataRowList("Names", Model.ComparedVenues, d => DataFormatUtils.GenerateHtml(d.Names, name => Html.Partial("Partials/ArchivedEntry/_NameInfo", new NameInfoViewModel(name)))) + @Html.DataRow("Description", Model.ComparedVenues, d => d.Description) + @Html.DataRow("Coordinates", Model.ComparedVenues, d => ((d.Coordinates != null) && d.Coordinates.HasValue) ? d.Coordinates.Formatted : string.Empty) + @Html.DataRow("Country/Region", Model.ComparedVenues, d => !string.IsNullOrEmpty(d.AddressCountryCode) ? new RegionInfo(d.AddressCountryCode).DisplayName : string.Empty) + @Html.DataRow("Address", Model.ComparedVenues, d => d.Address) + @Html.DataRowList("External links", Model.ComparedVenues, d => DataFormatUtils.GenerateHtml(d.WebLinks, webLink => Html.Partial("Partials/ArchivedEntry/_WebLinkInfo", new WebLinkInfoViewModel(webLink)))) +
    + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/Venue/_VenueLink.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/Venue/_VenueLink.cshtml new file mode 100644 index 0000000000..ff653415c5 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/Venue/_VenueLink.cshtml @@ -0,0 +1,4 @@ +@using VocaDb.Web.Models.Shared.Partials.Venue +@model VenueLinkViewModel + +@Html.ActionLink(Model.Venue.Name, "Details", "Venue", new { id = Model.Venue.Id }, new { title = Model.Venue.AdditionalNames }) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_AlbumSearchList.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_AlbumSearchList.cshtml new file mode 100644 index 0000000000..9e94a1fc51 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_AlbumSearchList.cshtml @@ -0,0 +1,97 @@ +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using SharedRes = ViewRes.SharedStrings +@using Res = ViewRes.Search.IndexStrings + + + +@{ Html.RenderPartial("Partials/_EntryCountBox"); } + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + + + + + + + + + + + + + + + + + + + + +
    + + @SharedRes.Name + + + + @SharedRes.Tags + + + @Res.ReleaseDate + + + + + @Res.Rating + + +
    + + Cover picture + + + + @Html.Partial("Partials/Knockout/_DraftIcon", new DraftIconViewModel("status")) +
    +
    + +
    +
    + + + , + +
    +
    + + +
    + +
    +
    + + + +
    + + @Res.Times +
    + + + + + + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_ArtistRolesEditViewModel.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_ArtistRolesEditViewModel.cshtml new file mode 100644 index 0000000000..48ec756b90 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_ArtistRolesEditViewModel.cshtml @@ -0,0 +1,16 @@ + + + +
    + +
    + + + + +
    + +
    diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_Chatbox.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_Chatbox.cshtml new file mode 100644 index 0000000000..edd626fc9b --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_Chatbox.cshtml @@ -0,0 +1,12 @@ +@inherits VocaDb.Web.Code.VocaDbPage + +@* +@if (!string.IsNullOrEmpty(Config.SiteSettings.SpotImAccId) && UserContext.ShowChatbox.Value) { + + + +
    + +}*@ + + diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_CommentBody.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_CommentBody.cshtml new file mode 100644 index 0000000000..4a16be6abb --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_CommentBody.cshtml @@ -0,0 +1,3 @@ +@using VocaDb.Web.Models.Shared.Partials.Comment + +@Html.Partial("Partials/Comment/_CommentKnockout", new CommentKnockoutViewModel("message", false)) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_CustomNameEdit.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_CustomNameEdit.cshtml new file mode 100644 index 0000000000..7a963bf946 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_CustomNameEdit.cshtml @@ -0,0 +1,19 @@ + + + +
    + +
    + + {{artistLink().artist.name}} +

    +
    + +
    + + + +
    + +
    diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_EditableComments.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_EditableComments.cshtml new file mode 100644 index 0000000000..d7c561f058 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_EditableComments.cshtml @@ -0,0 +1,5 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Comment +@inject Login Login + +@Html.Partial("Partials/Comment/_EditableComments", new EditableCommentsViewModel(Login.CanManageDb)) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_EnglishTranslatedString.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_EnglishTranslatedString.cshtml new file mode 100644 index 0000000000..5b2aff6c6e --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_EnglishTranslatedString.cshtml @@ -0,0 +1,48 @@ +@using VocaDb.Web.Models.Shared.Partials.Html +@using Res = VocaDb.Web.Resources.Views.Shared.Partials.EnglishTranslatedStringStrings +@model VocaDb.Web.Models.Shared.EnglishTranslatedStringViewModel + + + +@{ + void Text(string str) { + var maxLength = Model.MaxLength; + var summaryLength = Model.SummaryLength; + if (str == null || str.Length <= maxLength) { + @Html.Partial("Partials/Html/_FormatMarkdown", new FormatMarkdownViewModel(str)) + } else { +
    + @Html.Partial("Partials/Html/_FormatMarkdown", new FormatMarkdownViewModel(str)) +
    +
    + @Html.Partial("Partials/Html/_FormatMarkdown", new FormatMarkdownViewModel(str.Substring(0, summaryLength) + "...")) +

    @Res.ShowFullDescription

    +
    + } + } +} + +@if (Model.String.HasEnglish) { +
    + +
    + @{ Text(Model.String.Original); } +
    +
    + @{ Text(Model.String.English); } +
    +
    +} else { + Text(Model.String.Original); +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_EnglishTranslatedStringEdit.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_EnglishTranslatedStringEdit.cshtml new file mode 100644 index 0000000000..3fb8b0d1f3 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_EnglishTranslatedStringEdit.cshtml @@ -0,0 +1,22 @@ +@using Res = ViewRes.EntryEditStrings + + + +@* Security note: live preview renders HTML but it's sanitized by the markdown processing library *@ + +
    +

    @Res.TranslatedStringOriginal

    + +
    +

    + + @Res.TranslatedStringAddEnglishTranslation + +

    +
    +
    +

    @Res.TranslatedStringEnglishTranslation

    + +
    +
    + diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_EntryCountBox.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_EntryCountBox.cshtml new file mode 100644 index 0000000000..0d2537f703 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_EntryCountBox.cshtml @@ -0,0 +1,16 @@ +@using Res = ViewRes.Search.IndexStrings + +@{ + var selections = new[] { 10, 20, 40, 100 }; +} + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_Footer.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_Footer.cshtml new file mode 100644 index 0000000000..849fd43f1f --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_Footer.cshtml @@ -0,0 +1,11 @@ +@*Background illustration by 火神レオ | Other credits*@ + + @Html.Raw(string.Format(ViewRes._LayoutStrings.BackgroundCredit, "みゆ")) + | + @ViewRes._LayoutStrings.OtherCredit + | + @ViewRes._LayoutStrings.License + | + @ViewRes._LayoutStrings.PrivacyPolicy + + diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_GoogleAnalytics.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_GoogleAnalytics.cshtml new file mode 100644 index 0000000000..b40a204bcd --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_GoogleAnalytics.cshtml @@ -0,0 +1,15 @@ +@using VocaDb.Model.Utils + + + diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_LayoutScripts.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_LayoutScripts.cshtml new file mode 100644 index 0000000000..f154f5b8c2 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_LayoutScripts.cshtml @@ -0,0 +1,60 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Resources.Domain +@inherits VocaDb.Web.Code.VocaDbPage +@inject LaravelMixHelper LaravelMixHelper +@inject Login Login + +@{ + var searchObjectType = PageProperties.GlobalSearchType; +} + + + + + + + +@if (PageProperties.AddMainScripts) { + +} + + diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_LeftMenu.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_LeftMenu.cshtml new file mode 100644 index 0000000000..91bb450f60 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_LeftMenu.cshtml @@ -0,0 +1,88 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Code +@inherits MenuPage +@inject Login Login + +@{ + void BannerLink(string title, string url, string img) + { + @title + } + + void Banner(string title, string url, string img) + { +
  • @{ BannerLink(title, url, img); }
  • + } + + void SmallBanner(string title, string url, string img) + { +
    @{ BannerLink(title, url, img); }
    + } + + void SocialLink(string title, string url, string img) + { + BannerLink(title, url, img); + } +} + + diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_MainNavigationItems.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_MainNavigationItems.cshtml new file mode 100644 index 0000000000..5c8d5c3fc3 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_MainNavigationItems.cshtml @@ -0,0 +1,63 @@ +@using VocaDb.Model.Domain +@using VocaDb.Model.Domain.Security +@using VocaDb.Model.Utils.Search +@using VocaDb.Web.Helpers +@inherits VocaDb.Web.Code.MenuPage +@inject Login Login + + + diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_PVRatingButtonsForIndex.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_PVRatingButtonsForIndex.cshtml new file mode 100644 index 0000000000..63ab7306e3 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_PVRatingButtonsForIndex.cshtml @@ -0,0 +1,18 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Model.Domain.Songs +@inject Login Login + + + + diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_PatreonLink.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_PatreonLink.cshtml new file mode 100644 index 0000000000..0a40facec4 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_PatreonLink.cshtml @@ -0,0 +1,12 @@ +@inherits VocaDb.Web.Code.VocaDbPage + +

    + + @BrandableStrings.Layout.PaypalDonateTitle + +

    + + + Support on Patreon + + diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_PaypalDonateButton.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_PaypalDonateButton.cshtml new file mode 100644 index 0000000000..5e681cfab7 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_PaypalDonateButton.cshtml @@ -0,0 +1,14 @@ +@inherits VocaDb.Web.Code.VocaDbPage + +

    + + @BrandableStrings.Layout.PaypalDonateTitle + +

    + +
    + + + + +
    diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_PlayList.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_PlayList.cshtml new file mode 100644 index 0000000000..d6a2d830ec --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_PlayList.cshtml @@ -0,0 +1,62 @@ +@using Res = VocaDb.Web.Resources.Views.Shared.Partials.PlayListStrings + + + +
    +
    + @* Note: using visible here to avoid regenerating the pv player wrapper element *@ +
    +

    + + +

    + +
    + @{ Html.RenderPartial("Partials/_PVRatingButtonsForIndex"); } +
    + +
    +
    + + + + + + + + + + + + @Res.Autoplay + + + + + @ViewRes.Home.IndexStrings.ViewSongInfo + +
    +
    + +
    + + + + + + + + + +
    + + + + + + + + + +
    +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_SongListsFilters.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_SongListsFilters.cshtml new file mode 100644 index 0000000000..aa41c70f52 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_SongListsFilters.cshtml @@ -0,0 +1,47 @@ + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_SongSearchList.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_SongSearchList.cshtml new file mode 100644 index 0000000000..5f3b4ed76a --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_SongSearchList.cshtml @@ -0,0 +1,99 @@ +@using VocaDb.Model.Domain.Songs +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using VocaDb.Web.Models.Shared.Partials.Song +@using SharedRes = ViewRes.SharedStrings +@using Res = ViewRes.Search.IndexStrings + + + + +@{ Html.RenderPartial("Partials/_EntryCountBox"); } + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + + + + + + + + + + + + + + + + + +
    + + @SharedRes.Name + + + + @SharedRes.Tags + + + @Res.Rating + + +
    + + + + + + + + + + + + + + + + + @Html.Partial("Partials/Knockout/_DraftIcon", new DraftIconViewModel("status")) + + + +
    + + +
    + @Html.Partial("Partials/Song/_PVPreviewKnockout", new PVPreviewKnockoutViewModel("$parents[2].getPVServiceIcons")) +
    +
    +
    + + + , + +
    +
    + + @Res.TotalScore +
    + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + + + +
    + @{ Html.RenderPartial("Partials/_PlayList"); } +
    + + diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_TagFilters.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_TagFilters.cshtml new file mode 100644 index 0000000000..1c5ec2e70c --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_TagFilters.cshtml @@ -0,0 +1,16 @@ + + + + +
    +
    + + + +
    + + +
    +
    +
    + diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_TagList.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_TagList.cshtml new file mode 100644 index 0000000000..44ff1ec34f --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_TagList.cshtml @@ -0,0 +1,15 @@ + + + + + + - + + @ViewRes.EntryDetailsStrings.ShowAllTags + () + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_TagsEdit.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_TagsEdit.cshtml new file mode 100644 index 0000000000..80dc4fab58 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_TagsEdit.cshtml @@ -0,0 +1,51 @@ +@using VocaDb.Web.Models.Shared.Partials.Shared +@inherits VocaDb.Web.Code.VocaDbPage +@using Res = ViewRes.TagSelectionsStrings + + + +
    + +
    + +

    @Res.Info

    + +
    + + @* id is needed by jqButtonset. The binding also doesn't like ' in the id. *@ + + + +
    +
    + +
    +

    @Res.Suggestions

    +
    + @Html.Partial("Partials/Shared/_AjaxLoader", new AjaxLoaderViewModel()) +
    +
    + @Res.NoSuggestions +
    +
    + + + +
    +
    +
    + +
    + @Res.AddTag: +
    + + +
    +
    + +
    + +
    diff --git a/VocaDbWeb.Core/Views/Shared/Partials/_TwitterTimeline.cshtml b/VocaDbWeb.Core/Views/Shared/Partials/_TwitterTimeline.cshtml new file mode 100644 index 0000000000..fc0ee2e501 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/Partials/_TwitterTimeline.cshtml @@ -0,0 +1,4 @@ +@inherits VocaDb.Web.Code.VocaDbPage + + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/SongPopupContent.cshtml b/VocaDbWeb.Core/Views/Shared/SongPopupContent.cshtml new file mode 100644 index 0000000000..84f9d80e89 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/SongPopupContent.cshtml @@ -0,0 +1,37 @@ +@using VocaDb.Web.Helpers +@model VocaDb.Model.DataContracts.Songs.SongContract + + + +@if (!string.IsNullOrEmpty(Model.ThumbUrl)) { +
    + Thumb +
    +} + +@Model.Name + +@if (!string.IsNullOrEmpty(Model.AdditionalNames)) { +

    + @Model.AdditionalNames +

    +} +

    + @Model.ArtistString
    + @Translate.SongTypeNames[Model.SongType]
    +

    + +@if (Model.PublishDate.HasValue) { +

    + @ViewRes.EntryDetailsStrings.PublishDate @Model.PublishDate.Value.ToShortDateString() +

    +} + +@if (Model.LengthSeconds > 0) { +

    + @DateTimeUtils.FormatFromSeconds(Model.LengthSeconds) +

    +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/_EventPopupContent.cshtml b/VocaDbWeb.Core/Views/Shared/_EventPopupContent.cshtml new file mode 100644 index 0000000000..d6e2db98c7 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/_EventPopupContent.cshtml @@ -0,0 +1,48 @@ +@using VocaDb.Model.Helpers +@using VocaDb.Web.Helpers +@model VocaDb.Model.DataContracts.ReleaseEvents.ReleaseEventForApiContract + + + +@{ + var cat = Model.Series != null ? Model.Series.Category : Model.Category; +} + +@if (Model.MainPicture != null && Model.MainPicture.UrlSmallThumb != null) { +
    + Thumb +
    +} + +@Model.Name + +@if (!string.IsNullOrEmpty(Model.AdditionalNames)) { +

    + @Model.AdditionalNames +

    +} + +@if (cat != VocaDb.Model.Domain.ReleaseEvents.EventCategory.Unspecified && cat != VocaDb.Model.Domain.ReleaseEvents.EventCategory.Other) { +

    + @Translate.ReleaseEventCategoryNames[cat] +

    +} + +@if (!string.IsNullOrEmpty(Model.Description)) { +

    + @Html.StripMarkdown(Model.Description).TruncateWithEllipsis(100) +

    +} + +@if (Model.Date.HasValue) { +

    + @ViewRes.Event.DetailsStrings.OccurrenceDate: @Model.Date.Value.ToShortDateString() + @if (Model.EndDate.HasValue) { + @:- @Model.EndDate.Value.ToShortDateString() + } +

    +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/_Layout.cshtml b/VocaDbWeb.Core/Views/Shared/_Layout.cshtml new file mode 100644 index 0000000000..dc89e5329a --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/_Layout.cshtml @@ -0,0 +1,158 @@ +@using System.Globalization +@using Microsoft.AspNetCore.Html +@using VocaDb.Model.Domain.Globalization +@using VocaDb.Model.Helpers +@using VocaDb.Web.Helpers +@using VocaDb.Model.Utils +@using VocaDb.Web.Code +@using VocaDb.Web.Models.Shared.Partials.Shared +@model object +@inject LaravelMixHelper LaravelMixHelper +@inject Login Login + +@{ + var stylesheet = Login.IsLoggedIn && !string.IsNullOrEmpty(Login.User.Stylesheet) ? Login.User.Stylesheet : Config.SiteSettings.DefaultStylesheet; +} + + + + + @(!string.IsNullOrEmpty(PageProperties.PageTitle) ? PageProperties.PageTitle + " - " : "")@BrandableStrings.SiteTitle + + + + + @* Default encoding in ASP.NET is UTF-8: https://msdn.microsoft.com/en-us/library/system.web.configuration.globalizationsection.responseencoding.aspx *@ + + @if (!string.IsNullOrEmpty(PageProperties.Robots)) { + + } + + @{ Html.RenderPartial("LayoutPartials/_TwitterCard", Model); } + + + + @if (!string.IsNullOrEmpty(stylesheet)) { + + } + + + + + + + + + + @RenderSection("Head", false) + + @{ Html.RenderPartial("Partials/_GoogleAnalytics"); } + + + + + + + +
    +
    + @{ Html.RenderPartial("Partials/_LeftMenu"); } + +
    + + @if (ViewBag.Parents != null) { + var arr = (IHtmlContent[])ViewBag.Parents; + + } + + @if (!string.IsNullOrEmpty(PageProperties.Title)) { +

    @PageProperties.Title + @if (!string.IsNullOrEmpty(ViewBag.Subtitle)) { +  @ViewBag.Subtitle + } +

    + } + + @if (Login.Manager.LockdownEnabled) { +
    + @AppConfig.LockdownMessage +
    + } + + @if (!string.IsNullOrEmpty(Config.SiteSettings.SitewideAnnouncement)) { +
    + @Html.Raw(Config.SiteSettings.SitewideAnnouncement) +
    + } + + @if (IsSectionDefined("Toolbar")) { +

    + @RenderSection("Toolbar") +

    + } + + +
    +
    + + + + + + + +
    +
    + + @if (!string.IsNullOrEmpty(TempData.StatusMessage())) { + @Html.Partial("Partials/Shared/_NotificationPanel", new NotificationPanelViewModel(TempData.StatusMessage())) + } + + @RenderBody() + + + +
    +
    +
    + + + @{ Html.RenderPartial("Partials/_Footer"); } + + @{ Html.RenderPartial("Partials/_LayoutScripts"); } + + + @RenderSection("BodyScripts", false) + + diff --git a/VocaDbWeb.Core/Views/Shared/_SongWithVotePopupContent.cshtml b/VocaDbWeb.Core/Views/Shared/_SongWithVotePopupContent.cshtml new file mode 100644 index 0000000000..06a13fc7b9 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/_SongWithVotePopupContent.cshtml @@ -0,0 +1,8 @@ +@using VocaDb.Web.Helpers +@model VocaDb.Model.DataContracts.Songs.SongWithPVAndVoteContract + +@{ Html.RenderPartial("SongPopupContent", Model); } + +@if (Model.Vote != VocaDb.Model.Domain.Songs.SongVoteRating.Nothing) { +

    @Translate.SongVoteRatingNames[Model.Vote]

    +} diff --git a/VocaDbWeb.Core/Views/Shared/_TagPopupContent.cshtml b/VocaDbWeb.Core/Views/Shared/_TagPopupContent.cshtml new file mode 100644 index 0000000000..cc2da44a77 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/_TagPopupContent.cshtml @@ -0,0 +1,35 @@ +@using VocaDb.Model.Helpers +@using VocaDb.Web.Helpers +@model VocaDb.Model.DataContracts.Tags.TagForApiContract + + + +@if (Model.MainPicture != null && Model.MainPicture.UrlSmallThumb != null) { +
    + Thumb +
    +} + +@Model.Name + +@if (!string.IsNullOrEmpty(Model.AdditionalNames)) { +

    + @Model.AdditionalNames +

    +} + +@if (!string.IsNullOrEmpty(Model.CategoryName)) { +

    + @Model.CategoryName +

    +} + +@if (!string.IsNullOrEmpty(Model.Description)) { +

    + @Html.StripMarkdown(Model.Description).TruncateWithEllipsis(100) +

    +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Shared/_UserPopupContent.cshtml b/VocaDbWeb.Core/Views/Shared/_UserPopupContent.cshtml new file mode 100644 index 0000000000..3695b8c038 --- /dev/null +++ b/VocaDbWeb.Core/Views/Shared/_UserPopupContent.cshtml @@ -0,0 +1,29 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.User +@model VocaDb.Model.DataContracts.Users.UserContract + + + +@if (!string.IsNullOrEmpty(Model.Email)) { +
    + @Html.Partial("Partials/User/_ProfileIcon_IUserWithEmail", new ProfileIcon_IUserWithEmailViewModel(Model)) +
    +} + +@Model.Name + +

    + @Translate.UserGroups[Model.GroupId] +

    + +@if (Model.VerifiedArtist) { +

    + @ViewRes.User.DetailsStrings.VerifiedAccount +

    +} + +

    + Joined @Model.CreateDate.ToShortDateString() +

    diff --git a/VocaDbWeb.Core/Views/Song/Details.cshtml b/VocaDbWeb.Core/Views/Song/Details.cshtml new file mode 100644 index 0000000000..8ad717e45d --- /dev/null +++ b/VocaDbWeb.Core/Views/Song/Details.cshtml @@ -0,0 +1,622 @@ +@model SongDetails +@using System.Globalization +@using Microsoft.AspNetCore.Html +@using VocaDb.Model.DataContracts.Albums +@using VocaDb.Model.DataContracts.Songs +@using VocaDb.Model.Domain.Globalization +@using VocaDb.Model.Domain.PVs +@using VocaDb.Model.Domain.Songs +@using VocaDb.Web.Models +@using VocaDb.Web.Helpers +@using VocaDb.Model.Domain.Security +@using VocaDb.Model.Service.VideoServices +@using VocaDb.Web.Code +@using VocaDb.Web.Models.Shared +@using VocaDb.Web.Models.Shared.Partials.Album +@using VocaDb.Web.Models.Shared.Partials.Artist +@using VocaDb.Web.Models.Shared.Partials.Comment +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@using VocaDb.Web.Models.Shared.Partials.Html +@using VocaDb.Web.Models.Shared.Partials.PV +@using VocaDb.Web.Models.Shared.Partials.Shared +@using VocaDb.Web.Models.Shared.Partials.Song +@using R = ViewRes.Song +@using Res = ViewRes.Song.DetailsStrings +@using EntryRes = ViewRes.EntryDetailsStrings +@inject LaravelMixHelper LaravelMixHelper +@inject Login Login + +@{ + + ViewBag.Parents = new[] { Html.ActionLink(ViewRes.SharedStrings.Songs, "Index", "Search", UrlMapper.Search.Songs(), null) }; + + var showAlternateVersions = 3; + + var lyricsLanguageNames = Model.Lyrics + .OrderBy(l => l.TranslationType) + .Where(l => !string.IsNullOrEmpty(l.CultureCode) || l.TranslationType == TranslationType.Romanized) + .Select(l => l.TranslationType != TranslationType.Romanized ? CultureInfo.GetCultureInfo(l.CultureCode).NativeName : VocaDb.Web.Resources.Domain.Globalization.TranslationTypeNames.Romanized) + .Take(3) + .ToArray(); + + var additionalLyrics = lyricsLanguageNames.Any() ? Model.Lyrics.Length - lyricsLanguageNames.Length : 0; + + var lyricsLanguages = lyricsLanguageNames.Any() ? "(" + string.Join(", ", lyricsLanguageNames) + (additionalLyrics > 0 ? " " + string.Format(Res.LyricsPlusOthers, additionalLyrics) : "") + ")" : ""; + + var url = PageProperties.CanonicalUrl; + var eeLoveIt = Model.Name.Equals("love it!", StringComparison.InvariantCultureIgnoreCase); + var favoriteText = eeLoveIt ? "Love It!" : Res.AddToFavorites; + + PageProperties.Robots = Model.Deleted ? PagePropertiesData.Robots_Noindex_Follow : string.Empty; + +} + +@{ + @* REVIEW *@ + Func AlbumLink = @ + @Html.Partial("Partials/Album/_AlbumLink", new AlbumLinkViewModel(item)) + @if (!item.ReleaseDate.IsEmpty) { (@item.ReleaseDate.Year)} + ; + + void AlternateVersions(IEnumerable songs) { + foreach (var alternateVersion in songs) { + @Html.Partial("Partials/Song/_SongLink", new SongLinkViewModel(alternateVersion, tooltip: true)) + if (alternateVersion.LengthSeconds > 0) { + @:(@VocaDb.Model.Helpers.DateTimeHelper.FormatMinSec(alternateVersion.LengthSeconds)) + } + @Html.Partial("Partials/Song/_SongTypeLabel", new SongTypeLabelViewModel(alternateVersion.SongType)) +
    + @alternateVersion.ArtistString
    + } + } + + void SongAlbumLink(SongContract song, string icon, int albumId) { + + + @song.Name + + } +} + +@section Head { + + + + @Html.Partial("Partials/Html/_OpenGraphMetaTags", new OpenGraphMetaTagsViewModel(PageProperties)) + +} + +@section Toolbar { +@if (Model.OriginalPVs.Any() || Model.OtherPVs.Any()) { +
    + @if (Model.PrimaryPV != null) { + @Html.Partial("Partials/PV/_EmbedPV", new EmbedPVViewModel(Model.PrimaryPV)) + } +
    +} + +@if (Login.Manager.IsLoggedIn) { + + + + + @ViewRes.Song.DetailsStrings.RemoveFromFavorites + + @ViewRes.Song.DetailsStrings.AddToCustomList +} else { + + @ViewRes.Song.DetailsStrings.Like + @ViewRes.Song.DetailsStrings.AddToFavorites + + @ViewRes.Song.DetailsStrings.AddToCustomList +} + +@ViewRes.SharedStrings.Edit + +@Html.ActionLink(ViewRes.EntryDetailsStrings.ViewModifications, "Versions", new { id = Model.Id }, new { id = "viewVersions" }) + +@ViewRes.EntryDetailsStrings.ReportAnError + +@if (UserContext.HasPermission(PermissionToken.AccessManageMenu)) { + Maintenance actions +} + +@Html.Partial("Partials/Shared/_EntryStatusMessage", new EntryStatusMessageViewModel(Model.Status)) + +@Html.Partial("Partials/Shared/_AjaxLoader", new AjaxLoaderViewModel("pvLoader")) +} + +@if (Model.Deleted) { + @Html.Partial("Partials/EntryDetails/_DeletedBanner", new DeletedBannerViewModel(Model.MergedTo)) +} + +@if (Model.Draft && !Model.Deleted) { + @Html.Partial("Partials/Shared/_DraftMessage", new DraftMessageViewModel("glsongs")) +} + +
    + + + +
    + + + + + + @if (Model.Performers.Any()) { + + + + + } + + @if (Model.Subject != null && Model.Subject.Any()) { + + + + + } + @if (Model.Producers.Any()) { + + + + + } + @if (Model.Illustrators != null && Model.Illustrators.Any()) { + + + + + } + + @if (Model.Bands.Any()) { + + + + + } + + + @if (Model.Animators.Any()) { + + + + + } + + @if (Model.OtherArtists.Any()) { + + + + + } + + + + + + + @if (Model.Length > 0) { + + + + + } + + @if (Model.Albums.Any()) { + + + + + } + + + + + + + @if (Model.ListCount > 0) { + + + + + } + + @if (Model.OriginalPVs.Any()) { + + + + + } + + @if (Model.OtherPVs.Any()) { + + + + + } + + @Html.Partial("Partials/EntryDetails/_ExternalLinksRows", new ExternalLinksRowsViewModel(Model.WebLinks)) + + + + + + + @if (Model.AlternateVersions.Any()) { + + + + + } + + @if (!Model.Notes.IsEmpty) { + + + + + } + + + + + + + @if (Model.ReleaseEvent != null) { + + + + + } + + @if (Model.PublishDate.HasValue) { + + + + + } + + + + + + + @if (Model.Contract.AlbumSong != null) { + + + + + + } + +
    @ViewRes.SharedStrings.Name + @Model.Name
    + @Model.AdditionalNames +
    + @if (Model.SongType != SongType.Illustration) { + @ViewRes.Song.DetailsStrings.Vocalists + } else { + @:Subject + } + + @Html.Partial("Partials/Artist/_ArtistList", new ArtistListViewModel(Model.Performers, showRoles: ShowRolesMode.IfNotVocalist, showType: true)) +
    @EntryRes.Subject + @Html.Partial("Partials/Artist/_ArtistList", new ArtistListViewModel(Model.Subject)) +
    @ViewRes.Song.DetailsStrings.Producers + @Html.Partial("Partials/Artist/_ArtistList", new ArtistListViewModel(Model.Producers, showRoles: (Model.Producers.Length > 1) ? ShowRolesMode.IfNotDefault : ShowRolesMode.Never)) +
    Illustrators + @Html.Partial("Partials/Artist/_ArtistList", new ArtistListViewModel(Model.Illustrators)) +
    @Res.Band + @Html.Partial("Partials/Artist/_ArtistList", new ArtistListViewModel(Model.Bands)) +
    @ViewRes.Song.DetailsStrings.Animators + @Html.Partial("Partials/Artist/_ArtistList", new ArtistListViewModel(Model.Animators)) +
    @ViewRes.Song.DetailsStrings.OtherArtists + @Html.Partial("Partials/Artist/_ArtistList", new ArtistListViewModel(Model.OtherArtists, showRoles: ShowRolesMode.IfNotDefault)) +
    @ViewRes.SharedStrings.Type + @Html.Partial("Partials/Song/_SongTypeLabel", new SongTypeLabelViewModel(Model.SongType)) + @if (Model.SongTypeTag != null) { + @Translate.SongTypeNames[Model.SongType] + } else { + @Translate.SongTypeNames[Model.SongType] + } +
    @R.DetailsStrings.Duration + @DateTimeUtils.FormatFromSeconds(Model.Length) +
    @ViewRes.SharedStrings.Albums + @Html.LinkListHtml(Model.Albums, AlbumLink) +
    @ViewRes.SharedStrings.Tags + + @ViewRes.EntryDetailsStrings.EditTags + @if (Model.CanRemoveTagUsages) { + @Html.ActionLink(ViewRes.EntryDetailsStrings.ManageTags, "ManageTagUsages", new { id = Model.Id }, new { id = "manageTags" }) + } +
    @Res.PoolsAndLists + @Html.LinkList(Model.Pools, pool => Html.ActionLink(pool.Name, "Details", "SongList", new { id = pool.Id }, null)) + @if (Model.Pools.Any()) { + @:- + } + + @string.Format(Res.ViewAllLists, Model.ListCount) + +
    @Res.OriginalMedia + @{ PVList(Model.OriginalPVs, false); } +
    @Res.OtherMedia + @{ PVList(Model.OtherPVs, true); } +
    @Res.OriginalVersion + @Html.Partial("Partials/Song/_SongLinkKnockout", new SongLinkKnockoutViewModel("originalVersion().entry", "originalVersion().url", tooltip: true, toolTipDomainBinding: "originalVersion().domain")) + +
    + +
    @Res.AlternateVersions + @{ AlternateVersions(Model.AlternateVersions.Take(showAlternateVersions)); } + @if (Model.AlternateVersions.Length > showAlternateVersions) { + @Res.ShowAllVersions (@Model.AlternateVersions.Length) +
    + @{ AlternateVersions(Model.AlternateVersions.Skip(showAlternateVersions)); } +
    + } +
    @Res.Notes + @{ Html.RenderPartial("Partials/_EnglishTranslatedString", new EnglishTranslatedStringViewModel(Model.Notes)); } +
    @ViewRes.EntryDetailsStrings.Stats + + @string.Format(Res.Favorites, Model.FavoritedTimes), + @string.Format(Res.Likes, Model.LikedTimes), + + @string.Format(ViewRes.EntryDetailsStrings.Hits, Model.Hits), + @string.Format(Res.TotalScore, Model.RatingScore). +
    @Res.ReleaseEvent + @Model.ReleaseEvent.Name +
    @ViewRes.EntryDetailsStrings.PublishDate + @Model.PublishDate.Value.ToShortDateString() +
    @ViewRes.EntryDetailsStrings.AdditionDate + @Html.Partial("Partials/Shared/_UniversalTimeLabel", new UniversalTimeLabelViewModel(Model.CreateDate)) +
    @Res.Navigation +

    + @if (Model.Contract.AlbumSong.DiscNumber > 1) { + @Html.Raw(string.Format(Res.TrackOnAlbumDisc, Model.Contract.AlbumSong.TrackNumber, Model.Contract.AlbumSong.DiscNumber, Html.Partial("Partials/Album/_AlbumLink", new AlbumLinkViewModel(Model.Contract.Album)))) + } else { + @Html.Raw(string.Format(Res.TrackOnAlbum, Model.Contract.AlbumSong.TrackNumber, Html.Partial("Partials/Album/_AlbumLink", new AlbumLinkViewModel(Model.Contract.Album)))) + } +

    + @if (Model.Contract.PreviousSong != null) { + SongAlbumLink(Model.Contract.PreviousSong.Song, "icon-fast-backward", Model.Contract.Album.Id); + } + + @if (Model.Contract.NextSong != null) { + SongAlbumLink(Model.Contract.NextSong.Song, "icon-fast-forward", Model.Contract.Album.Id); + } +
    + + @if (Model.Suggestions.Any()) { +

    @Res.SuggestionsTitle

    + @Html.Partial("Partials/Song/_SongGrid", new SongGridViewModel(Model.Suggestions, 2, true, true)) +

    + @Res.SeeRelatedSongs +

    + } + + @if (Model.CanEditPersonalDescription || !string.IsNullOrEmpty(Model.PersonalDescriptionText)) { +

    + @EntryRes.PersonalDescription +

    +
    + + Thumb + + +
    + @if (Model.CanEditPersonalDescription) { + + } +

    + +

    +
    +

    +
    +
    + +
    + +
    + + +
    +
    +
    + } + + @Html.Partial("Partials/Comment/_LatestCommentsKnockout") + +

    + @ViewRes.EntryDetailsStrings.ViewAllComments +

    + +
    + + @if (Model.Lyrics.Any()) { + + } + +
    + @Html.Partial("Partials/Comment/_EditableComments", new EditableCommentsViewModel(UserContext.HasPermission(PermissionToken.CreateComments), well: false)) +
    + +
    +
    + +
    +
    + @R.DetailsStrings.Link
    + +
    +
    + Markdown
    + +
    +
    + @R.DetailsStrings.Embed
    + +
    +
    + +
    + +
    + @{ Html.RenderPartial("Partials/_TagsEdit"); } +
    + +@{ Html.RenderPartial("Partials/_AddToListDialog"); } + +
    + +
    + @{Html.RenderPartial("Partials/_UsersWithSongRating"); } +
    + +
    + +@{ Html.RenderPartial("Partials/_SongInListsDialog"); } + +@Html.Partial("Partials/EntryDetails/_ReportEntryPopupKnockout", new ReportEntryPopupKnockoutViewModel()) + + + +@section BodyScripts { + + +} + +@{ + void PVList(IEnumerable pvs, bool showPVType) { + + foreach (var pv in pvs) { + + @pv.Service + @(!string.IsNullOrEmpty(pv.Name) ? pv.Name : pv.Service.ToString()) + @if (showPVType) { + @("(" + Translate.PVTypeNames[pv.PVType] + ")") + } + + if (VideoServiceHelper.ShowExternalSiteLink(pv.Service)) { + @Html.Raw(" ") + + + @Res.ViewExternal + + } + if (pv.Service == PVService.NicoNicoDouga) { + + + @Res.ViewInfo + + } +
    + } + + } +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Song/ManageTagUsages.cshtml b/VocaDbWeb.Core/Views/Song/ManageTagUsages.cshtml new file mode 100644 index 0000000000..e4b51781c5 --- /dev/null +++ b/VocaDbWeb.Core/Views/Song/ManageTagUsages.cshtml @@ -0,0 +1,14 @@ +@using VocaDb.Model.Domain +@using VocaDb.Web.Models.Shared.Partials.Tag +@model VocaDb.Model.DataContracts.UseCases.EntryWithTagUsagesContract + +@{ + PageProperties.Title = "Manage tag usages - " + Model.DefaultName; + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.Songs, "Index", "Search", UrlMapper.Search.Songs(), null), + Html.ActionLink(Model.DefaultName, "Details", new { id = Model.Id }) + }; +} + + +@Html.Partial("Partials/Tag/_TagUsagesManageTable", new TagUsagesManageTableViewModel(EntryType.Song, Model.TagUsages, Model.CanRemoveTagUsages)) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Song/Merge.cshtml b/VocaDbWeb.Core/Views/Song/Merge.cshtml new file mode 100644 index 0000000000..427943020a --- /dev/null +++ b/VocaDbWeb.Core/Views/Song/Merge.cshtml @@ -0,0 +1,66 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Knockout +@model VocaDb.Model.DataContracts.Songs.SongContract +@inject LaravelMixHelper LaravelMixHelper + +@{ + PageProperties.Title = "Merge song - " + Model.Name; + ViewBag.Parents = new[] { + Html.ActionLink("Songs", "Index", "Search", UrlMapper.Search.Songs(), null), + Html.ActionLink(Model.Name, "Details", new { id = Model.Id }), + Html.ActionLink("Edit", "Edit", new { id = Model.Id }), + }; + +} + +@using (Html.BeginForm()) +{ + + @Html.Partial("Partials/Shared/_MergeEntryInfo") + + + +
    +
    +
    + @Html.Partial("Partials/Knockout/_LockingAutoComplete", new LockingAutoCompleteViewModel("songAutoComplete", "targetSearchParams", "target.name", "target.id")) +
    +
    + + + +
    +
    + +
    + + + +} + +@section BodyScripts { + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Song/Partials/_AddToListDialog.cshtml b/VocaDbWeb.Core/Views/Song/Partials/_AddToListDialog.cshtml new file mode 100644 index 0000000000..4eaaf4d952 --- /dev/null +++ b/VocaDbWeb.Core/Views/Song/Partials/_AddToListDialog.cshtml @@ -0,0 +1,33 @@ +@using Res = ViewRes.Song.DetailsStrings + + diff --git a/VocaDbWeb.Core/Views/Song/Partials/_ArtistForSongEdit.cshtml b/VocaDbWeb.Core/Views/Song/Partials/_ArtistForSongEdit.cshtml new file mode 100644 index 0000000000..329f04666f --- /dev/null +++ b/VocaDbWeb.Core/Views/Song/Partials/_ArtistForSongEdit.cshtml @@ -0,0 +1,38 @@ +@* Binding context: ArtistForAlbumEditViewModel *@ + + + +
    + +
    + +
    +
    + +
    + @if (VocaDb.Model.Utils.AppConfig.AllowCustomArtistName) { + @ViewRes.Album.ArtistForAlbumEditRowStrings.Customize + } + + + + + +
    +
    + +
    +
    +
    + + + + + @ViewRes.SharedStrings.Remove + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Song/Partials/_LyricsForSongEdit.cshtml b/VocaDbWeb.Core/Views/Song/Partials/_LyricsForSongEdit.cshtml new file mode 100644 index 0000000000..2fb701e90f --- /dev/null +++ b/VocaDbWeb.Core/Views/Song/Partials/_LyricsForSongEdit.cshtml @@ -0,0 +1,49 @@ +@using VocaDb.Model.Domain.Globalization +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using VocaDb.Web.Models.Shared.Partials.Shared + + + +
    + +
    +
    +
    +

    + @Html.Partial("Partials/Shared/_HelpLabel", new HelpLabelViewModel(ViewRes.Song.EditStrings.LyLanguage, "If multiple languages match, select the one that best represents the lyrics. If none of the options match, select 'Other/Unknown'.")) + @Html.Partial("Partials/Knockout/_DropdownList", new DropdownListViewModel(InterfaceLanguage.UserLanguageCultures.ToDictionaryFull(VocaDb.Web.Resources.Domain.Globalization.InterfaceLanguageStrings.Other), "cultureCode")) +

    +
    + @Html.Partial("Partials/Shared/_HelpLabel", new HelpLabelViewModel("Source", "You can fill either label, URL or both")) +
    + + + + +
    +
    + + + + +
    +
    +
    + + +
    + + @ViewRes.SharedStrings.Delete + + Change to original + Change to translation +
    +
    +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Song/Partials/_SongInListsDialog.cshtml b/VocaDbWeb.Core/Views/Song/Partials/_SongInListsDialog.cshtml new file mode 100644 index 0000000000..ab9fde9542 --- /dev/null +++ b/VocaDbWeb.Core/Views/Song/Partials/_SongInListsDialog.cshtml @@ -0,0 +1,11 @@ + +
    +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Song/Partials/_SongInListsDialogContent.cshtml b/VocaDbWeb.Core/Views/Song/Partials/_SongInListsDialogContent.cshtml new file mode 100644 index 0000000000..9140cef9c9 --- /dev/null +++ b/VocaDbWeb.Core/Views/Song/Partials/_SongInListsDialogContent.cshtml @@ -0,0 +1,36 @@ +@using VocaDb.Model.Domain.Songs +@using VocaDb.Web.Models.Shared.Partials.User +@model VocaDb.Model.DataContracts.Songs.SongListContract[] + +@{ + + var byCategory = Model.Where(l => l.FeaturedCategory != SongListFeaturedCategory.Nothing).GroupBy(l => l.FeaturedCategory); + //var categories = byCategory.Select(c => new {CategoryName = VocaDb.Web.Helpers.Translate.SongListFeaturedCategoryNames[c.Key], Lists = c.ToArray()}); + var customLists = Model.Where(l => l.FeaturedCategory == SongListFeaturedCategory.Nothing).ToArray(); + +} + +@foreach (var category in byCategory) { + +

    @VocaDb.Web.Helpers.Translate.SongListFeaturedCategoryNames[category.Key]

    + + foreach (var list in category) { + @list.Name +
    + } + +
    + +} + +@if (customLists.Any()) { + +

    @ViewRes.Song.DetailsStrings.CustomLists

    + + foreach (var list in customLists) { + @list.Name + @("(")@Html.Partial("Partials/User/_UserIconLink_IUserWithEmail", new UserIconLink_IUserWithEmailViewModel(list.Author))@(")") +
    + } + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Song/Partials/_SongInListsDialogKnockout.cshtml b/VocaDbWeb.Core/Views/Song/Partials/_SongInListsDialogKnockout.cshtml new file mode 100644 index 0000000000..e4a01e599e --- /dev/null +++ b/VocaDbWeb.Core/Views/Song/Partials/_SongInListsDialogKnockout.cshtml @@ -0,0 +1,37 @@ + +
    + +
    + +

    + + + + + +
    +
    + + + +
    + +
    + + + +

    @ViewRes.Song.DetailsStrings.CustomLists

    + + + + + + () +
    +
    + + + + + +
    diff --git a/VocaDbWeb.Core/Views/Song/Partials/_SongListsDialog.cshtml b/VocaDbWeb.Core/Views/Song/Partials/_SongListsDialog.cshtml new file mode 100644 index 0000000000..34057dfbfa --- /dev/null +++ b/VocaDbWeb.Core/Views/Song/Partials/_SongListsDialog.cshtml @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Song/Partials/_UsersWithSongRating.cshtml b/VocaDbWeb.Core/Views/Song/Partials/_UsersWithSongRating.cshtml new file mode 100644 index 0000000000..ff3e04f0fd --- /dev/null +++ b/VocaDbWeb.Core/Views/Song/Partials/_UsersWithSongRating.cshtml @@ -0,0 +1,29 @@ +@using VocaDb.Web.Models.Shared.Partials.User + + + +
    +

    + @ViewRes.Song.DetailsStrings.FavoritedByTitle + +

    + +
    + +
    +

    + @ViewRes.Song.DetailsStrings.LikedByTitle + +

    + +
    + +

    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Song/Rankings.cshtml b/VocaDbWeb.Core/Views/Song/Rankings.cshtml new file mode 100644 index 0000000000..cc9b77808e --- /dev/null +++ b/VocaDbWeb.Core/Views/Song/Rankings.cshtml @@ -0,0 +1,132 @@ +@using VocaDb.Model.Domain.Songs +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Song +@inherits VocaDb.Web.Code.VocaDbPage +@using Res = ViewRes.Song.RankingsStrings +@inject LaravelMixHelper LaravelMixHelper + +@{ + PageProperties.Title = BrandableStrings.Song.RankingsTitle; + ViewBag.Parents = new[] { Html.ActionLink(ViewRes.SharedStrings.Songs, "Index", "Search", UrlMapper.Search.Songs(), null) }; +} + + + + + + + +
    + @Res.NoSongs +
    + + + + + + + + + + + + + + + + + + + + + +
    + @Res.ColName + + @Res.ColPublished + + @Res.ColTags + + @Res.ColRating +
    +

    {{ $index() + 1 }}

    +
    + + + + + + + + + + + + + + + + +
    + + +
    + @Html.Partial("Partials/Song/_PVPreviewKnockout", new PVPreviewKnockoutViewModel("$parents[2].getPVServiceIcons")) +
    +
    + {{ $data.publishDate | formatDate:'l' }} + +
    + + + , + +
    +
    + + @Res.TotalScore +
    + +@section BodyScripts { + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Song/RelatedSongs.cshtml b/VocaDbWeb.Core/Views/Song/RelatedSongs.cshtml new file mode 100644 index 0000000000..10293ec90e --- /dev/null +++ b/VocaDbWeb.Core/Views/Song/RelatedSongs.cshtml @@ -0,0 +1,24 @@ +@using VocaDb.Web.Models.Shared.Partials.Song +@model VocaDb.Model.DataContracts.Songs.RelatedSongsContract +@using R = ViewRes.Song.DetailsStrings + +@if (!Model.Any) { + @ViewRes.EntryDetailsStrings.NoMatches +} + +@if (Model.ArtistMatches.Any()) { +

    @R.MatchingArtists

    + @Html.Partial("Partials/Song/_SongGrid", new SongGridViewModel(Model.ArtistMatches, 2, true)) +
    +} + +@if (Model.LikeMatches.Any()) { +

    @R.MatchingLikes

    + @Html.Partial("Partials/Song/_SongGrid", new SongGridViewModel(Model.LikeMatches, 2, true)) +
    +} + +@if (Model.TagMatches.Any()) { +

    @R.MatchingTags

    + @Html.Partial("Partials/Song/_SongGrid", new SongGridViewModel(Model.TagMatches, 2, true)) +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Song/SongItem.cshtml b/VocaDbWeb.Core/Views/Song/SongItem.cshtml new file mode 100644 index 0000000000..d30ca81786 --- /dev/null +++ b/VocaDbWeb.Core/Views/Song/SongItem.cshtml @@ -0,0 +1,9 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Song +@model VocaDb.Model.DataContracts.Songs.SongContract + +
    + @Html.Partial("Partials/Song/_SongIconLink", new SongIconLinkViewModel(Model)) +
    +
    +@Model.ArtistString (@Translate.SongTypeNames[Model.SongType]) diff --git a/VocaDbWeb.Core/Views/Song/ViewVersion.cshtml b/VocaDbWeb.Core/Views/Song/ViewVersion.cshtml new file mode 100644 index 0000000000..123552b2a7 --- /dev/null +++ b/VocaDbWeb.Core/Views/Song/ViewVersion.cshtml @@ -0,0 +1,81 @@ +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@using VocaDb.Web.Models.Shared.Partials.Song +@using VocaDb.Web.Models.Song +@model VocaDb.Model.DataContracts.Songs.ArchivedSongVersionDetailsContract +@inject Login Login + +@{ + + PageProperties.Title = "Revision " + Model.ArchivedVersion.Version + " for " + Model.Name; + PageProperties.Robots = PagePropertiesData.Robots_Noindex_Nofollow; + + if (Model.Song != null) { + ViewBag.Parents = new[] { + Html.ActionLink("Song", "Index", "Search", UrlMapper.Search.Songs(), null), + Html.ActionLink(Model.Song.Name, "Details", new { id = Model.Song.Id }), + Html.ActionLink("Revisions", "Versions", new { id = Model.Song.Id }) + }; + } else { + ViewBag.Parents = new[] { + Html.ActionLink("Song", "Index", "Search", UrlMapper.Search.Songs(), null) + }; + } + +} + +@section Toolbar { +@if (Login.CanRevertEntryVersions && Model.CanBeReverted) { + @Html.ActionLink(ViewRes.ViewVersionStrings.RevertToVersion, "RevertToVersion", new { archivedSongVersionId = Model.ArchivedVersion.Id }, new { id = "revertLink", onclick = string.Format("return confirm(\"{0}\");", ViewRes.ViewVersionStrings.ConfirmRevertToVersion) }) +} +  +Download XML +@ViewRes.EntryDetailsStrings.ReportAnError +@if (Login.CanViewHiddenRevisions) { + if (Model.ArchivedVersion.Hidden) { + @Html.ActionLink(ViewRes.ViewVersionStrings.UnhideVersion, "UpdateVersionVisibility", new { archivedVersionId = Model.ArchivedVersion.Id, hidden = false }, new { id = "showLink", onclick = string.Format("return confirm(\"{0}\");", ViewRes.ViewVersionStrings.ConfirmUnhide) }) + } else { + @Html.ActionLink(ViewRes.ViewVersionStrings.HideVersion, "UpdateVersionVisibility", new { archivedVersionId = Model.ArchivedVersion.Id, hidden = true }, new { id = "hideLink", onclick = string.Format("return confirm(\"{0}\");", ViewRes.ViewVersionStrings.ConfirmHide) }) + } +} +} + +@if (Model.ArchivedVersion.Hidden) { + @Html.Partial("Partials/EntryDetails/_HiddenBanner") +} + +@if (Model.ComparableVersions.Any()) { + using (Html.BeginForm("ViewVersion", "Song", FormMethod.Post, new { @class = "form form-inline" })) { + @:Compare to: @Html.DropDownListFor(m => m.ComparedVersionId, ViewHelper.CreateSelectList(Model.ComparableVersions, i => i.Id, i => i.Version + " (" + Versions.GetChangeString(i.ChangedFields) + " by " + i.AgentName + ")", Model.ComparedVersionId), new { @class = "input-xlarge", onchange = "$(this).closest('form').submit();" }) + + } +} + +@Html.Partial("Partials/ArchivedEntry/_ArchivedObjectVersionProperties", new ArchivedObjectVersionPropertiesViewModel(Versions.CreateForSong(Model.ArchivedVersion), Model.ComparedVersion != null ? Versions.CreateForSong(Model.ComparedVersion) : null)) + +@Html.Partial("Partials/Song/_PrintArchivedSongData", new PrintArchivedSongDataViewModel(Model.Versions)) + +@Html.Partial("Partials/EntryDetails/_ReportEntryVersionPopupKnockout", new ReportEntryVersionPopupKnockoutViewModel()) + +@section BodyScripts { + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/SongList/Details.cshtml b/VocaDbWeb.Core/Views/SongList/Details.cshtml new file mode 100644 index 0000000000..3167b02400 --- /dev/null +++ b/VocaDbWeb.Core/Views/SongList/Details.cshtml @@ -0,0 +1,313 @@ +@using VocaDb.Model.Domain +@using VocaDb.Model.Domain.Security +@using VocaDb.Model.Service.Search +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Web.Resources.Domain +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@using VocaDb.Web.Models.Shared.Partials.Event +@using VocaDb.Web.Models.Shared.Partials.Html +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using VocaDb.Web.Models.Shared.Partials.Search +@using VocaDb.Web.Models.Shared.Partials.Shared +@using VocaDb.Web.Models.Shared.Partials.Song +@using KnockoutDraftIconViewModel = VocaDb.Web.Models.Shared.Partials.Knockout.DraftIconViewModel +@using Res = ViewRes.SongList.DetailsStrings +@using SearchRes = ViewRes.Search.IndexStrings +@model VocaDb.Web.Models.SongLists.SongListDetailsViewModel +@inject LaravelMixHelper LaravelMixHelper +@inject Login Login + +@{ + + if (!Model.IsFeatured) { + ViewBag.Parents = new[] { + Html.ActionLink(Model.SongList.Author.Name, "Details", "User", new {id = Model.SongList.Author.Id}, null) + }; + } else { + var categoryName = Translate.SongListFeaturedCategoryNames[Model.SongList.FeaturedCategory]; + ViewBag.Parents = new[] { + Html.ActionLink(categoryName, "Featured", "SongList", null, null, Model.SongList.FeaturedCategory.ToString(), null, null) + }; + } + + var descriptionHtml = MarkdownParser.GetHtml(Model.SongList.Description); + var descriptionStripped = MarkdownParser.GetPlainText(Model.SongList.Description); + + PageProperties.Description = descriptionStripped; + PageProperties.Robots = Model.SongList.Deleted ? PagePropertiesData.Robots_Noindex_Follow : string.Empty; + + var url = PageProperties.CanonicalUrl; + +} + +@section Head { + @Html.Partial("Partials/Html/_OpenGraphMetaTags", new OpenGraphMetaTagsViewModel(PageProperties)) + +} + +@section Toolbar { + @if (Model.CanEdit) { + @Html.ActionLink(ViewRes.SharedStrings.Edit, "Edit", new { id = Model.SongList.Id }, new { id = "editListLink" }) + @: + @Html.ActionLink(ViewRes.EntryDetailsStrings.ViewModifications, "Versions", new { id = Model.SongList.Id }, new { id = "viewVersions" }) + @: +} + @Html.ActionLink(Res.Export, "Export", new { id = Model.SongList.Id }, new { id = "export" }) + @if (Model.IsFeatured) { + @Html.Partial("Partials/Shared/_EntryStatusMessage", new EntryStatusMessageViewModel(Model.SongList.Status)) + } +} + +@if (Model.IsFeatured && Model.SongList.Status == EntryStatus.Draft && !Model.SongList.Deleted) { + @Html.Partial("Partials/Shared/_DraftMessage", new DraftMessageViewModel(string.Empty)) +} + +@if (Model.SongList.Deleted) { + @Html.Partial("Partials/EntryDetails/_DeletedBanner", new DeletedBannerViewModel(null)) +} + +
    + @if (!string.IsNullOrEmpty(Model.SmallThumbUrl)) { + + Thumb + + } +
    + @if (Model.SongList.EventDate.HasValue) { +

    @string.Format(Res.Date, Model.SongList.EventDate.Value.ToShortDateString())

    + } + @if (!string.IsNullOrWhiteSpace(Model.SongList.Description)) { + @Html.Raw(descriptionHtml) + } + +

    +

    @ViewRes.SharedStrings.Tags:
    + + @ViewRes.EntryDetailsStrings.EditTags +

    + + @if (Model.SongList.Events.Any()) { +

    +

    @Res.Events

    +
      + @foreach (var ev in Model.SongList.Events) { +
    • + + @ev.Name + + @if (ev.Date.HasValue) { + if (ev.HasVenueOrVenueName) { + (@ev.Date.Value.ToString("d"), @Html.Partial("Partials/Event/_VenueLinkOrVenueName", new VenueLinkOrVenueNameViewModel(ev))) + } else { + (@ev.Date.Value.ToString("d")) + } + } else { + if (ev.HasVenueOrVenueName) { + (@Html.Partial("Partials/Event/_VenueLinkOrVenueName", new VenueLinkOrVenueNameViewModel(ev))) + } + } +
    • + } +
    +

    + } +
    +
    + +
    + + + + + +
    + @ViewRes.EntryIndexStrings.SortBy + @Html.Partial("Partials/Knockout/_Dropdown", new DropdownViewModel(Model.SortRules, "sort", "sortName")) +
    + +
    + + +
    +   + + + +
    + +
    + + + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + + + + + + + + + + + +
    + + + + + + + + . + + + + () + + + + + + + + @Html.Partial("Partials/Knockout/_DraftIcon", new KnockoutDraftIconViewModel("status")) + +
    + + +
    + @Html.Partial("Partials/Song/_PVPreviewKnockout", new PVPreviewKnockoutViewModel("$parents[2].pvServiceIcons.getIconUrls")) +
    +
    +
    + + + , + +
    +
    + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + +
    + +
    +
    + @{ Html.RenderPartial("Partials/_PlayList"); } +
    +
    + +@Html.Partial("Partials/Comment/_LatestCommentsKnockout") + +
    + @{ Html.RenderPartial("Partials/_TagsEdit"); } +
    + +@section BodyScripts { + + + + +} diff --git a/VocaDbWeb.Core/Views/SongList/Featured.cshtml b/VocaDbWeb.Core/Views/SongList/Featured.cshtml new file mode 100644 index 0000000000..418fa7f1a5 --- /dev/null +++ b/VocaDbWeb.Core/Views/SongList/Featured.cshtml @@ -0,0 +1,82 @@ +@using VocaDb.Model.Domain.Security +@using VocaDb.Model.Domain.Songs +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Song +@using VocaDb.Web.Models.SongLists +@using Res = ViewRes.SongList.FeaturedStrings +@model FeaturedViewModel +@inject LaravelMixHelper LaravelMixHelper +@inject Login Login + +@{ + PageProperties.Title = ViewRes.SharedStrings.FeaturedSongLists; + var categories = Translate.SongListFeaturedCategoryNames.AllFields.Where(f => f.Id != SongListFeaturedCategory.Nothing).ToArray(); +} + +@section Toolbar { + @if (Login.Manager.HasPermission(PermissionToken.EditFeaturedLists)) { + @Html.ActionLink(ViewRes.User.DetailsStrings.CreateNewList, "Edit", null, new { id = "createLink" }) + } + @if (Login.Manager.HasPermission(PermissionToken.EditProfile)) { + @Html.ActionLink(Res.Import, "Import", null, new { id = "importLink" }) + } +} + + + +@foreach (var cat in categories) { +
    + + @{ Html.RenderPartial("Partials/_SongListsFilters"); } + + @Html.Partial("Partials/Song/_SongListsKnockout", new SongListsKnockoutViewModel("items", groupByYear: true)) + +

    @ViewRes.SharedStrings.ShowMore

    + +
    +} + +@section BodyScripts { + + +} + +@section Head { + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/SongList/Import.cshtml b/VocaDbWeb.Core/Views/SongList/Import.cshtml new file mode 100644 index 0000000000..23eb28d5d8 --- /dev/null +++ b/VocaDbWeb.Core/Views/SongList/Import.cshtml @@ -0,0 +1,126 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Shared +@using Res = VocaDb.Web.Resources.Views.SongList.ImportStrings +@inherits VocaDb.Web.Code.VocaDbPage +@inject LaravelMixHelper LaravelMixHelper + +@{ + PageProperties.Title = Res.Title; +} + +
    +
    +
    + @Html.Partial("Partials/Shared/_HelpLabel", new HelpLabelViewModel(Res.UrlLabel, Res.UrlDescription)) +
    +
    + +
    +
    +
    +
    + +
    +
    + +
    +
    + +
    +
    +
    + +
    + +
    +

    @Res.MylistDescription

    +
    +
    @Res.ListName
    +
    + +
    +
    +
    +
    @Res.ListDescription
    +
    + +
    +
    +
    +
    @Res.TotalSongs
    +
    + {{ totalSongs }} + @Res.LoadMoreHelp +
    +
    +
    +
    Rankings number
    +
    {{ wvrNumber }}
    +
    +
    + +
    + @Res.SongsMissingError +
    + +

    @Res.SongsInList

    + + + + + + + + + + + + + + + +
    @Res.Order@Res.NicoPV@Res.VocaDbEntry
    {{ sortIndex }} + {{ name }} + + {{#if: $data.matchedSong}} + {{ matchedSong.name }} + {{/if}} + {{#ifnot: $data.matchedSong}} + @Res.SongMissing + (@Res.Submit) + {{/ifnot}} +
    + +

    + + @ViewRes.SharedStrings.ShowMore + +   + ({{ items().length }} / {{ totalSongs }} @Res.ItemsLoaded) +

    + +
    + + +
    + +@section BodyScripts { + + + +} diff --git a/VocaDbWeb.Core/Views/SongList/Versions.cshtml b/VocaDbWeb.Core/Views/SongList/Versions.cshtml new file mode 100644 index 0000000000..449ca693fa --- /dev/null +++ b/VocaDbWeb.Core/Views/SongList/Versions.cshtml @@ -0,0 +1,19 @@ +@using VocaDb.Model.Domain +@using VocaDb.Model.Domain.Songs +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@model VocaDb.Model.DataContracts.Songs.SongListWithArchivedVersionsContract + +@{ + PageProperties.Title = ViewRes.EntryDetailsStrings.Revisions + " - " + Model.Name; + PageProperties.Robots = PagePropertiesData.Robots_Noindex_Nofollow; + ViewBag.Parents = new[] { + Html.ActionLink(Model.Name, "Details", new { id = Model.Id }) + }; +} + +@Html.Partial("Partials/ArchivedEntry/_CurrentVersionMessage", new CurrentVersionMessageViewModel(Model.Version, EntryStatus.Finished)) + +@Html.Partial("Partials/ArchivedEntry/_ArchivedObjectVersions", new ArchivedObjectVersionsViewModel(Model.ArchivedVersions.Select(a => new ArchivedObjectVersion(a, Translate.SongListEditableFieldNames.GetAllNameNames(a.ChangedFields, SongListEditableFields.Nothing))))) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Stats/Index.cshtml b/VocaDbWeb.Core/Views/Stats/Index.cshtml new file mode 100644 index 0000000000..aa1d48af08 --- /dev/null +++ b/VocaDbWeb.Core/Views/Stats/Index.cshtml @@ -0,0 +1,40 @@ +@inherits VocaDb.Web.Code.VocaDbPage + +@{ + PageProperties.Title = "Statistics / Reports"; +} + + + + + +
    + +@section BodyScripts { + + + + + +} diff --git a/VocaDbWeb.Core/Views/Tag/Details.cshtml b/VocaDbWeb.Core/Views/Tag/Details.cshtml new file mode 100644 index 0000000000..42840ac99b --- /dev/null +++ b/VocaDbWeb.Core/Views/Tag/Details.cshtml @@ -0,0 +1,305 @@ +@using Microsoft.AspNetCore.Html +@using VocaDb.Model +@using VocaDb.Model.DataContracts.Tags +@using VocaDb.Model.Domain +@using Res = ViewRes.Tag.DetailsStrings +@using VocaDb.Model.Domain.Images +@using VocaDb.Model.Domain.Security +@using VocaDb.Model.Domain.Tags +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared +@using VocaDb.Web.Models.Shared.Partials.Album +@using VocaDb.Web.Models.Shared.Partials.Artist +@using VocaDb.Web.Models.Shared.Partials.Comment +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@using VocaDb.Web.Models.Shared.Partials.Html +@using VocaDb.Web.Models.Shared.Partials.Shared +@using VocaDb.Web.Models.Shared.Partials.Song +@using VocaDb.Web.Models.Shared.Partials.Tag +@using SharedRes = ViewRes.SharedStrings +@model TagDetailsContract +@inject LaravelMixHelper LaravelMixHelper +@inject Login Login + +@{ + + ViewBag.Parents = new[] { Html.ActionLink(SharedRes.Tags, "Index") }; + + var smallThumbUrl = Url.ImageThumb(Model.Thumb, ImageSize.SmallThumb, useUnknownImage: false); + var thumbUrl = Url.ImageThumb(Model.Thumb, ImageSize.Original, useUnknownImage: false); + + var url = PageProperties.CanonicalUrl; + + var related = new List>(); + + if (Model.Parent != null) { + related.Add(Tuple.Create(Model.Parent, "parent")); + } + + related.AddRange(Model.Children.Select(t => Tuple.Create(t, "child"))); + related.AddRange(Model.Siblings.Select(t => Tuple.Create(t, "sibling"))); + + PageProperties.Robots = Model.Deleted ? PagePropertiesData.Robots_Noindex_Follow : string.Empty; + +} + +@section Head { + @Html.Partial("Partials/Html/_OpenGraphMetaTags", new OpenGraphMetaTagsViewModel(PageProperties)) + +} + +@section BodyScripts { + + + +} + +@section Toolbar { + @Res.FollowTag + @Res.UnfollowTag + + @Html.ActionLink(ViewRes.SharedStrings.Edit, "Edit", new { id = Model.Id }, new { id = "editTagLink", @class = (Login.CanEdit(Model) ? "" : "disabled") }) + @Html.ActionLink(ViewRes.EntryDetailsStrings.ViewModifications, "Versions", new { id = Model.Id }, new { id = "viewVersions" }) + @ViewRes.EntryDetailsStrings.ReportAnError + @Html.Partial("Partials/Shared/_EntryStatusMessage", new EntryStatusMessageViewModel(Model.Status)) +} + +@{ + @* REVIEW *@ + Func NicoTagLink = @@item; +} + +@if (Model.Deleted) { + @Html.Partial("Partials/EntryDetails/_DeletedBanner", new DeletedBannerViewModel(null)) +} + +
    + +
    + @if (!string.IsNullOrEmpty(smallThumbUrl)) { + + Thumb + + } + +
    + @if (!Model.Description.IsEmpty) { +
    + @{ Html.RenderPartial("Partials/_EnglishTranslatedString", new EnglishTranslatedStringViewModel(Model.Description, 2100, 2000)); } +
    + } + + @if (Model.RelatedTags.Any()) { +

    @Res.RelatedTags: @Html.Partial("Partials/Tag/_TagList", new TagListViewModel(Model.RelatedTags, tooltip: true))

    + } + + @if (Model.WebLinks.Any()) { + @Html.Partial("Partials/EntryDetails/_ExternalLinksList", new ExternalLinksListViewModel(Model.WebLinks, false)) +
    + } + + @if (Model.MappedNicoTags.Any()) { +

    @Res.MappedTags: @Html.LinkListHtml(Model.MappedNicoTags, NicoTagLink)

    + } + + @if (Model.CategoryName != string.Empty) { +

    @Res.Category: @Model.CategoryName

    + } + + @if (!string.IsNullOrEmpty(Model.Translations)) { +

    @Res.Translations: @Model.Translations

    + } + + @if (!string.IsNullOrEmpty(Model.AdditionalNames)) { +

    + @Res.Aliases: @Model.AdditionalNames +

    + } + + @if (related.Any()) { +
    +
    + } + + @if (Model.Targets != TagTargetTypes.Nothing && Model.Targets != TagTargetTypes.All) { +

    + @Res.ValidFor: @string.Join(", ", EnumVal.Values.Where(e => e != EntryType.Undefined && Model.Targets.HasFlag((TagTargetTypes)e))) +

    + } + + @if (Model.RelatedEntryType.HasValue) { + var text = Translate.EntryTypeNames[Model.RelatedEntryType.EntryType] + (Model.RelatedEntryType.HasSubType ? " (" + Translate.EntrySubTypeName(Model.RelatedEntryType) + ")" : ""); +

    + @Res.AssociatedEntryType: @text +

    + } + +

    @string.Format(Res.FollowCount, Model.FollowerCount)

    + +

    @ViewRes.EntryDetailsStrings.AdditionDate: @Html.Partial("Partials/Shared/_UniversalTimeLabel", new UniversalTimeLabelViewModel(Model.CreateDate))

    + +
    + +
    + @Html.ActionLink(Res.AllEntries + " (" + Model.AllUsageCount + ")", "Index", "Search", UrlMapper.Search.Entries(tagId: Model.Id), null) +
    + + @if (Model.Stats.ArtistCount > 0) { +
    + @Html.ActionLink(Res.AllArtists + " (" + Model.Stats.ArtistCount + ")", "Index", "Search", UrlMapper.Search.Artists(tagId: Model.Id), null) +
    + } + + @if (Model.Stats.AlbumCount > 0) { +
    + @Html.ActionLink(Res.AllAlbums + " (" + Model.Stats.AlbumCount + ")", "Index", "Search", UrlMapper.Search.Albums(tagId: Model.Id), null) +
    + } + + @if (Model.Stats.SongCount > 0) { +
    + @Html.ActionLink(Res.AllSongs + " (" + Model.Stats.SongCount + ")", "Index", "Search", UrlMapper.Search.Songs(tagId: Model.Id), null) +
    + } + + @if (Model.Stats.EventCount > 0) { +
    + @Html.ActionLink(Res.AllEvents + " (" + Model.Stats.EventCount + ")", "Index", "Search", UrlMapper.Search.Events(tagId: Model.Id), null) +
    + } + + @if (Model.Stats.SongListCount > 0) { +
    + @Html.ActionLink(Res.AllSongLists + " (" + Model.Stats.SongListCount + ")", "Featured", "SongList", new { tagId = Model.Id }, null) +
    + } +
    +
    +
    + +@if (Model.Stats.Artists.Any()) { +
    + @Html.Partial("Partials/Shared/_ShowMore", new ShowMoreViewModel(href: Url.Action("Index", "Search", UrlMapper.Search.Artists(tagId: Model.Id)))) +

    + + @Res.TopArtists + (@Model.Stats.ArtistCount @SharedRes.Total) + +

    + @Html.Partial("Partials/Artist/_ArtistGrid", new ArtistGridViewModel(Model.Stats.Artists.Select(a => a), 2, true)) +
    +} + +@if (Model.Stats.Albums.Any()) { +
    + @Html.Partial("Partials/Shared/_ShowMore", new ShowMoreViewModel(href: Url.Action("Index", "Search", UrlMapper.Search.Albums(tagId: Model.Id)))) +

    + + @Res.TopAlbums + (@Model.Stats.AlbumCount @SharedRes.Total) + +

    + @Html.Partial("Partials/Album/_AlbumGrid", new AlbumGridViewModel(Model.Stats.Albums.Select(a => a), 2, false, false, true)) +
    +} + +@if (Model.Stats.Songs.Any()) { +
    + @Html.Partial("Partials/Shared/_ShowMore", new ShowMoreViewModel(href: Url.Action("Index", "Search", UrlMapper.Search.Songs(tagId: Model.Id)))) +

    + + @Res.TopSongs + (@Model.Stats.SongCount @SharedRes.Total) + +

    + @Html.Partial("Partials/Song/_SongGrid", new SongGridViewModel(Model.Stats.Songs.Select(s => s), 2, true)) +
    +} + + +@if (Model.Stats.EventSeries.Any()) { +
    + @Html.Partial("Partials/Shared/_ShowMore", new ShowMoreViewModel(href: Url.Action("Index", "Search", UrlMapper.Search.Events(tagId: Model.Id)))) +

    + + @Res.EventSeries + +

    + @Html.Partial("Partials/Shared/_EventSeriesThumbs", new EventSeriesThumbsViewModel(Model.Stats.EventSeries)) +
    +} + +@if (Model.Stats.Events.Any()) { +
    + @Html.Partial("Partials/Shared/_ShowMore", new ShowMoreViewModel(href: Url.Action("Index", "Search", UrlMapper.Search.Events(tagId: Model.Id)))) +

    + + @Res.TopEvents + (@Model.Stats.EventCount @SharedRes.Total) + +

    + @Html.Partial("Partials/Shared/_EventThumbs", new EventThumbsViewModel(Model.Stats.Events, ImageSize.TinyThumb)) +
    +} + +@if (related.Any()) { +
    + @if (Model.Siblings.Any()) { +

    @Res.Siblings: @Html.Partial("Partials/Tag/_TagList", new TagListViewModel(Model.Siblings))

    + } + @if (Model.Children.Any()) { +

    @Res.Children: @Html.Partial("Partials/Tag/_TagList", new TagListViewModel(Model.Children))

    + } +
    +} + +
    + + + +
    + @Html.Partial("Partials/Comment/_EditableComments", new EditableCommentsViewModel(UserContext.HasPermission(PermissionToken.CreateComments), well: false, commentsBinding: "topComments", newCommentRows: 3, pagination: false)) + +

    @ViewRes.EntryDetailsStrings.NoComments

    + +

    + @ViewRes.EntryDetailsStrings.ViewAllComments +

    +
    + +
    + @Html.Partial("Partials/Comment/_EditableComments", new EditableCommentsViewModel(UserContext.HasPermission(PermissionToken.CreateComments), well: false)) +
    + +
    + +@Html.Partial("Partials/EntryDetails/_ReportEntryPopupKnockout", new ReportEntryPopupKnockoutViewModel()) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Tag/Index.cshtml b/VocaDbWeb.Core/Views/Tag/Index.cshtml new file mode 100644 index 0000000000..105aa21519 --- /dev/null +++ b/VocaDbWeb.Core/Views/Tag/Index.cshtml @@ -0,0 +1,77 @@ +@using VocaDb.Web.Helpers +@model VocaDb.Model.DataContracts.Tags.TagCategoryContract[] +@inject LaravelMixHelper LaravelMixHelper +@inject Login Login + +@{ + PageProperties.Title = ViewRes.SharedStrings.Tags; + var tagCount = Model.Sum(m => m.Tags.Length); + var avgUsageCount = Model.Sum(m => m.Tags.Sum(t => t.UsageCount)) / tagCount; +} + +@functions { + int GetFontSizePercent(int usageCount, int avgUsage) { + return Math.Min(Math.Max(usageCount * 80 / avgUsage, 60), 100); + } +} + +@section Toolbar { + @if (Login.CanManageDb) { + Create new + } +} + +@foreach (var category in Model) { + +
    + @if (category.Name != string.Empty) { +

    @category.Name

    + } else { +

    @ViewRes.SharedStrings.Unsorted

    + } + + @foreach (var tag in category.Tags) { + + @Html.ActionLink(tag.Name, "DetailsById", new { id = tag.Id, slug = tag.UrlSlug }, new { title = tag.AdditionalNames, style = "font-size: " }) + + } +
    + +} + +
    + + + +
    + Tag name must be unique +
    + +
    + +@section BodyScripts { + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Tag/Merge.cshtml b/VocaDbWeb.Core/Views/Tag/Merge.cshtml new file mode 100644 index 0000000000..8267b7a839 --- /dev/null +++ b/VocaDbWeb.Core/Views/Tag/Merge.cshtml @@ -0,0 +1,66 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Knockout +@model VocaDb.Model.DataContracts.Tags.TagBaseContract +@inject LaravelMixHelper LaravelMixHelper + +@{ + PageProperties.Title = "Merge tag - " + Model.Name; + ViewBag.Parents = new[] { + Html.ActionLink("Tags", "Index", "Tag"), + Html.ActionLink(Model.Name, "DetailsById", new { id = Model.Id, slug = Model.UrlSlug }), + Html.ActionLink("Edit", "Edit", new { id = Model.Id }), + }; + +} + +@using (Html.BeginForm()) +{ + + @Html.Partial("Partials/Shared/_MergeEntryInfo") + + + +
    +
    +
    + @Html.Partial("Partials/Knockout/_LockingAutoComplete", new LockingAutoCompleteViewModel("tagAutoComplete", "target.entry", "target.name", "target.id", extraBindings: "tagFilter: tagFilter")) +
    +
    + + + +
    +
    + +
    + + + +} + +@section BodyScripts { + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Tag/Versions.cshtml b/VocaDbWeb.Core/Views/Tag/Versions.cshtml new file mode 100644 index 0000000000..d7139b6a44 --- /dev/null +++ b/VocaDbWeb.Core/Views/Tag/Versions.cshtml @@ -0,0 +1,16 @@ +@using VocaDb.Web.Code +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@model VocaDb.Web.Models.Tag.Versions + +@{ + PageProperties.Title = ViewRes.EntryDetailsStrings.Revisions + " - " + Model.Entry.Name; + PageProperties.Robots = PagePropertiesData.Robots_Noindex_Nofollow; + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.Tags, "Index"), + Html.ActionLink(Model.Entry.Name, "DetailsById", new { id = Model.Entry.Id, slug = Model.Entry.UrlSlug }) + }; +} + +@Html.Partial("Partials/ArchivedEntry/_CurrentVersionMessage", new CurrentVersionMessageViewModel(Model.Entry.Version, Model.Entry.Status)) + +@Html.Partial("Partials/ArchivedEntry/_ArchivedObjectVersions", new ArchivedObjectVersionsViewModel(Model.ArchivedVersions, id => Url.Action("ViewVersion", new { id }))) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Tag/ViewVersion.cshtml b/VocaDbWeb.Core/Views/Tag/ViewVersion.cshtml new file mode 100644 index 0000000000..990c1ce7df --- /dev/null +++ b/VocaDbWeb.Core/Views/Tag/ViewVersion.cshtml @@ -0,0 +1,74 @@ +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@using VocaDb.Web.Models.Shared.Partials.Tag +@model VocaDb.Web.Models.Shared.ViewVersion +@inject Login Login + +@{ + + PageProperties.Title = "Revision " + Model.Entry.ArchivedVersion.Version + " for " + Model.Entry.Name; + PageProperties.Robots = PagePropertiesData.Robots_Noindex_Nofollow; + + if (Model.Entry.Tag != null) { + ViewBag.Parents = new[] { + Html.ActionLink("Tags", "Index"), + Html.ActionLink(Model.Entry.Tag.Name, "DetailsById", new { id = Model.Entry.Tag.Id, slug = Model.Entry.Tag.UrlSlug }), + Html.ActionLink("Revisions", "Versions", new { id = Model.Entry.Tag.Id }) + }; + } else { + ViewBag.Parents = new[] { + Html.ActionLink("Tags", "Index"), + }; + } + +} + +@section Toolbar { + Download XML + @ViewRes.EntryDetailsStrings.ReportAnError + @if (Login.CanViewHiddenRevisions) { + if (Model.Entry.ArchivedVersion.Hidden) { + @Html.ActionLink(ViewRes.ViewVersionStrings.UnhideVersion, "UpdateVersionVisibility", new { archivedVersionId = Model.Entry.ArchivedVersion.Id, hidden = false }, new { id = "showLink", onclick = string.Format("return confirm(\"{0}\");", ViewRes.ViewVersionStrings.ConfirmUnhide) }) + } else { + @Html.ActionLink(ViewRes.ViewVersionStrings.HideVersion, "UpdateVersionVisibility", new { archivedVersionId = Model.Entry.ArchivedVersion.Id, hidden = true }, new { id = "hideLink", onclick = string.Format("return confirm(\"{0}\");", ViewRes.ViewVersionStrings.ConfirmHide) }) + } + } +} + +@if (Model.Entry.ArchivedVersion.Hidden) { + @Html.Partial("Partials/EntryDetails/_HiddenBanner") +} + +@if (Model.Entry.ComparableVersions.Any()) { + using (Html.BeginForm("ViewVersion", "Tag", FormMethod.Post, new { @class = "form form-inline" })) { + @:Compare to: @Html.DropDownListFor(m => m.ComparedVersionId, ViewHelper.CreateSelectList(Model.Entry.ComparableVersions, i => i.Id, i => i.Version + " (" + i.TranslateChangedFields(Model.EnumTranslations) + " by " + i.AgentName + ")", Model.Entry.ComparedVersionId), new { @class = "input-xlarge", onchange = "$(this).closest('form').submit();" }) + + } +} + +@Html.Partial("Partials/ArchivedEntry/_ArchivedObjectVersionProperties", new ArchivedObjectVersionPropertiesViewModel(Model.Version(Model.Entry.ArchivedVersion), Model.Version(Model.Entry.ComparedVersion))) + +@Html.Partial("Partials/Tag/_PrintArchivedTagData", new PrintArchivedTagDataViewModel(Model.Entry.Versions)) + +@Html.Partial("Partials/EntryDetails/_ReportEntryVersionPopupKnockout", new ReportEntryVersionPopupKnockoutViewModel()) + +@section BodyScripts { + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Tests/Index.cshtml b/VocaDbWeb.Core/Views/Tests/Index.cshtml new file mode 100644 index 0000000000..15f839bba5 --- /dev/null +++ b/VocaDbWeb.Core/Views/Tests/Index.cshtml @@ -0,0 +1,26 @@ +@using VocaDb.Web.Helpers +@inject LaravelMixHelper LaravelMixHelper +@{ + Layout = null; +} + + + + + + + QUnit Tests for VocaDB + + + +
    +
    + + + + + + + + + diff --git a/VocaDbWeb.Core/Views/User/AlbumCollection.cshtml b/VocaDbWeb.Core/Views/User/AlbumCollection.cshtml new file mode 100644 index 0000000000..c21647cb65 --- /dev/null +++ b/VocaDbWeb.Core/Views/User/AlbumCollection.cshtml @@ -0,0 +1,43 @@ +@using VocaDb.Web.Helpers +@using SharedRes = ViewRes.SharedStrings +@model VocaDb.Web.Models.User.AlbumCollection +@inject LaravelMixHelper LaravelMixHelper +@inject Login Login + +@{ + PageProperties.Title = "Album collection for " + Model.User.Name; + ViewBag.Parents = new[] { + Html.ActionLink(Model.User.Name, "Details", "User", new {id = Model.User.Id}, null) + }; +} + +@{ Html.RenderPartial("Partials/_AlbumCollection"); } + +@section BodyScripts { + + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/User/ComposeMessage.cshtml b/VocaDbWeb.Core/Views/User/ComposeMessage.cshtml new file mode 100644 index 0000000000..ffc57d00ca --- /dev/null +++ b/VocaDbWeb.Core/Views/User/ComposeMessage.cshtml @@ -0,0 +1,47 @@ +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using Res = ViewRes.User.MessagesStrings + + +
    + +
    + @Res.To +
    +
    + @Html.Partial("Partials/Knockout/_BasicEntryLinkLockingAutoComplete", new BasicEntryLinkLockingAutoCompleteViewModel("userAutocomplete", "receiver")) +
    + Receiver must be selected. +
    +
    + +
    + @Res.Subject +
    +
    +
    + + +
    + +
    + @Res.Body + @Html.Partial("Partials/Shared/_MarkdownNotice") +
    +
    + +
    + + Live preview +
    +
    +
    + + + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/User/Details.cshtml b/VocaDbWeb.Core/Views/User/Details.cshtml new file mode 100644 index 0000000000..24e09f8db3 --- /dev/null +++ b/VocaDbWeb.Core/Views/User/Details.cshtml @@ -0,0 +1,505 @@ +@using System.Globalization +@using VocaDb.Model.DataContracts.Users +@using VocaDb.Model.Domain.Users +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Model.Domain.Security +@using VocaDb.Model.Utils +@using VocaDb.Web.Models.Shared.Partials.Album +@using VocaDb.Web.Models.Shared.Partials.Artist +@using VocaDb.Web.Models.Shared.Partials.Comment +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@using VocaDb.Web.Models.Shared.Partials.Html +@using VocaDb.Web.Models.Shared.Partials.Shared +@using VocaDb.Web.Models.Shared.Partials.Song +@using VocaDb.Web.Models.Shared.Partials.Tag +@using VocaDb.Web.Models.Shared.Partials.User +@using VocaDb.Web.Resources.Domain.Globalization +@using R = ViewRes.User.DetailsStrings +@using Res = ViewRes.User.DetailsStrings +@model UserDetailsContract +@inject LaravelMixHelper LaravelMixHelper +@inject Login Login + +@{ + PageProperties.Title = Model.Name; + PageProperties.Subtitle = Translate.UserGroups[Model.GroupId]; + PageProperties.Robots = !Model.Active ? PagePropertiesData.Robots_Noindex_Follow : string.Empty; + + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.Users, "Index"), + }; + + var ownProfile = (Login.Manager.IsLoggedIn && Login.User.Id == Model.Id && Login.User.Active); + var canSeeDetailedStats = (ownProfile || !Model.AnonymousActivity || Login.CanModerateUsers); + + var url = VocaUriBuilder.CreateAbsolute(Url.Action("Profile", new { id = Model.Name })).ToString(); + +} + +@functions { + public string GetLanguageName(UserKnownLanguageContract lang) { + if (!string.IsNullOrEmpty(lang.CultureCode)) { + var culture = CultureInfo.GetCultureInfo(lang.CultureCode); + return culture.NativeName + " - " + culture.EnglishName; + } else { + return InterfaceLanguageStrings.Other; + } + } +} + +@{ + void AvatarImg() { + if (!string.IsNullOrEmpty(Model.Email)) { + @Html.Partial("Partials/User/_ProfileIcon_IUserWithEmail", new ProfileIcon_IUserWithEmailViewModel(Model, 120)) + } else { + Avatar + } + } + + void Avatar() { + if (UserContext.IsLoggedIn && UserContext.LoggedUserId == Model.Id && Login.User.Active) { + + @{ AvatarImg(); } + + } else { + AvatarImg(); + } + } +} + +@section Toolbar { + @if (ownProfile) { + @Html.ActionLink(R.MySettings, "MySettings", null, new { id = "mySettingsLink" }); + @Html.Raw(" ") + @Html.ActionLink(R.Messages, "Messages", null, new { id = "messagesLink" }); + } + + @if (UserContext.IsLoggedIn && UserContext.LoggedUserId != Model.Id && Login.User.Active && !Model.Standalone) { + @Html.ActionLink(R.ComposeMessage, "Messages", null, null, null, "composeTab", new { receiverName = Model.Name }, new { id = "composeMessageLink" }); + } + + @if (Login.CanManageUsers && EntryPermissionManager.CanEditUser(UserContext, Model.GroupId)) { + @Html.Raw(" ") + @Html.ActionLink(ViewRes.SharedStrings.Edit, "Edit", new { id = Model.Id }, new { id = "editUserLink" }); + } + @if (Login.Manager.HasPermission(PermissionToken.ReportUser) && UserContext.LoggedUserId != Model.Id && Model.Active) { + @Html.Raw(" ") + @Res.ReportSpamming + } + @if (Login.Manager.HasPermission(PermissionToken.RemoveEditPermission) && UserContext.LoggedUserId != Model.Id && Model.GroupId != UserGroupId.Limited && Model.Active && EntryPermissionManager.CanEditUser(Login.Manager, Model.GroupId)) { + @Html.Raw(" ") + @ViewRes.SharedStrings.SetToLimited + } + @if (Login.Manager.HasPermission(PermissionToken.DisableUsers) && UserContext.LoggedUserId != Model.Id && Model.Active) { + @Html.Raw(" ") + @Html.ActionLink(R.Disable, "Disable", new { id = Model.Id }, new { id = "disableUserLink" }); + } +} + +@if (ownProfile && Login.User.GroupId == UserGroupId.Limited) { +
    +

    Why is my user group "Limited user"?

    +

    + In order to prevent spammers and abusers we use automated tools to check users' IP address. + If you have a dynamic IP, it's possible that someone with the same IP as you was participating in these activities + and your account was mistakenly reduced, preventing you from editing the database. +

    + If this is the case, please @Html.ActionLink("contact us", "Index", "Help") and we'll take care of it. Thank you. +

    +
    +} +@if (ownProfile && Model.PossibleProducerAccount) { +
    + @Html.Raw(string.Format(Res.PossibleProducerMessage, Html.ActionLink(Res.ArtistVerificationPage, "RequestVerification"))) +
    +} + +@if (ownProfile && !Model.KnownLanguages.Any()) { +
    + @Html.Raw(string.Format(Res.KnownLanguagesMessage, "/User/MySettings#profile")) +
    +} + + + +
    +
    +
    +

    @Model.Name

    + @{ Avatar(); } +
    +

    + @Translate.UserGroups[Model.GroupId] + @if (Model.EffectivePermissions.Contains(PermissionToken.DesignatedStaff)) + { + @R.Staff + } + @if (Model.VerifiedArtist) + { + @R.VerifiedAccount + } + @if (Model.IsVeteran) { + @string.Format(R.Veteran, BrandableStrings.Layout.SiteName) + } + @if (!string.IsNullOrEmpty(Model.CustomTitle)) { + @Model.CustomTitle + } + @if (!Model.Active) + { + @R.AccountDisabled + } + @if (Model.EmailVerified) + { + + } +

    + +

    @R.StatsTab

    + + @{ var submitText = R.Submissions + ": " + Model.SubmitCount; } + @if (canSeeDetailedStats) + { + @Html.ActionLink(submitText, "EntryEdits", new { id = Model.Id, onlySubmissions = true }) + } + else + { + @submitText + } +
    + + @{ var editText = R.TotalEdits + ": " + Model.EditCount; } + @if (canSeeDetailedStats) + { + @Html.ActionLink(editText, "EntryEdits", new { id = Model.Id, onlySubmissions = false }) + } + else + { + @editText + } +
    + @R.CommentsMade: @Model.CommentCount
    + @R.TagVotes: @Model.TagVotes
    + @R.Power: @Model.Power (@R.Level @Model.Level) + +

    @R.MemberSince

    + @Model.CreateDate.ToShortDateString() + + @if (Model.OldUsernames.Any()) { +

    @R.OldUsernames

    + foreach (var oldName in Model.OldUsernames) { + @oldName.OldName@: + @(string.Format(R.OldNameUntil, oldName.Date.ToShortDateString()) + (oldName != Model.OldUsernames.Last() ? "," : ""))@: + } + } + + @if (Model.Supporter) { +
    + +
    + @string.Format(Res.SupporterTitle, BrandableStrings.Layout.SiteName) +
    +
    + } +
    + +
    + + @if (!string.IsNullOrEmpty(Model.AboutMe)) + { +

    @R.AboutMe

    + @Html.Partial("Partials/Html/_FormatMarkdown", new FormatMarkdownViewModel(Model.AboutMe)) +
    + } + + @if (Model.KnownLanguages.Any()) { +

    @Res.LanguagesIKnow

    +
      + @foreach (var lang in Model.KnownLanguages) { +
    • + @Html.Partial("Partials/Html/_LanguageFlag", new LanguageFlagViewModel(lang.CultureCode)) + @GetLanguageName(lang) + @if (lang.Proficiency != UserLanguageProficiency.Nothing) { + @:(@Translate.UserLanguageProficiencyNames[lang.Proficiency]) + } +
    • + } +
    + } + + @if (Model.OwnedArtistEntries.Any()) + { +

    @R.VerifiedOwner

    + @Html.Partial("Partials/Artist/_ArtistGrid", new ArtistGridViewModel(Model.OwnedArtistEntries.Select(a => a.Artist), 3, true)) +
    + } + + @if (Model.WebLinks.Any()) + { +

    @ViewRes.EntryDetailsStrings.ExternalLinks

    + @Html.Partial("Partials/EntryDetails/_ExternalLinksList", new ExternalLinksListViewModel(Model.WebLinks)) +
    + } + + @if (!string.IsNullOrEmpty(Model.TwitterName)) + { +

    @R.TwitterAccount

    + @Model.TwitterName +

    + } + + @if (!string.IsNullOrEmpty(Model.Location)) + { +

    @R.Location

    + @Model.Location +

    + } + + @if (Model.FavoriteTags.Any()) + { +

    @R.FavoriteTags

    + @Html.Partial("Partials/Tag/_TagList", new TagListViewModel(Model.FavoriteTags)) +
    +
    +
    + } + + @if (ownProfile && !Login.CanManageUsers) + { + if (Model.AdditionalPermissions.Any()) + { +

    @R.AdditionalPermissions

    + @Translate.AllPermissionTokenNames(Model.AdditionalPermissions) + } + } + else if (Login.CanManageUsers) + { +

    @R.LastLogin

    + @Html.Partial("Partials/Shared/_UniversalTimeLabel", new UniversalTimeLabelViewModel(Model.LastLogin)) + if (Login.CanManageUsers) { + @:(@Model.LastLoginAddress) + GeoIpTool + StopForumSpam + Add to banned IPs + } + + if (Model.AdditionalPermissions.Any()) { +

    @R.AdditionalPermissions

    + @Translate.AllPermissionTokenNames(Model.AdditionalPermissions) + } + + if (!string.IsNullOrEmpty(Model.Email)) + { +

    @R.Email

    + @Model.Email + if (Model.EmailVerified) + { + + } + } + +

    @R.EffectivePermissions

    + @Translate.AllPermissionTokenNames(Model.EffectivePermissions) + } + +
    +
    + +
    + + @if (Model.FavoriteAlbums.Any()) + { +
    + +

    + + @R.SomeAlbumsILike + (@string.Format(R.Total, Model.AlbumCollectionCount)) + +

    + @Html.Partial("Partials/Album/_AlbumThumbs", new AlbumThumbsViewModel(Model.FavoriteAlbums)) +
    + } + + @if (Model.LatestRatedSongs.Any()) + { +
    + +

    + + @R.SomeSongsILike + (@string.Format(R.Total, Model.FavoriteSongCount)) + +

    + @Html.Partial("Partials/Song/_SongGrid", new SongGridViewModel(Model.LatestRatedSongs, 3)) +
    + } + + @if (Model.FollowedArtists.Any()) + { +
    + +

    + + @R.SomeArtistsIFollow + (@string.Format(R.Total, Model.ArtistCount)) + +

    + @Html.Partial("Partials/Artist/_ArtistGrid", new ArtistGridViewModel(Model.FollowedArtists, 3)) +
    + } + +
    + +

    + + @ViewRes.EntryDetailsStrings.LatestComments + +

    +
    + @Html.Partial("Partials/Comment/_EditableComments", new EditableCommentsViewModel(!Model.Standalone && UserContext.HasPermission(PermissionToken.CreateComments), well: false, commentsBinding: "topComments", newCommentRows: 3)) + +

    @ViewRes.EntryDetailsStrings.NoComments

    + +
    + +
    +
    + +
    + @{ Html.RenderPartial("Partials/_FollowedArtists"); } +
    + +
    + @{ Html.RenderPartial("Partials/_AlbumCollection"); } +
    + +
    + @{ Html.RenderPartial("Partials/_RatedSongs"); } +
    + +
    + @{ Html.RenderPartial("Partials/_SongLists"); } +
    + +
    +
    + @Html.Partial("Partials/Comment/_EditableComments", new EditableCommentsViewModel(!Model.Standalone && UserContext.HasPermission(PermissionToken.CreateComments), well: false)) +
    +
    + +
    + @{ Html.RenderPartial("Partials/_Events"); } +
    + +@if (Login.CanManageUsers) { +
    +} + +@Html.Partial("Partials/EntryDetails/_EntryDeletePopupBase", new EntryDeletePopupBaseViewModel("Please confirm that you wish to remove user's editing permissions. You may provide additional explanation below (optional).", "limitedUserViewModel", "setToLimitedLink", "Remove editing permissions", "Confirm")) +@Html.Partial("Partials/EntryDetails/_EntryDeletePopupBase", new EntryDeletePopupBaseViewModel("Please confirm that you wish to report user. Please provide additional explanation below.", "reportUserViewModel", "reportUserLink", "Report user", "Confirm")) + +@section Head { + + +} + +@section BodyScripts { + + + + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/User/EntryEdits.cshtml b/VocaDbWeb.Core/Views/User/EntryEdits.cshtml new file mode 100644 index 0000000000..ea0786c507 --- /dev/null +++ b/VocaDbWeb.Core/Views/User/EntryEdits.cshtml @@ -0,0 +1,73 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Activityfeed +@using VocaDb.Web.Models.Shared.Partials.Knockout +@model VocaDb.Model.DataContracts.Users.UserBaseContract +@inject LaravelMixHelper LaravelMixHelper + +@{ + PageProperties.Title = "Entry edits - " + Model.Name; + ViewBag.Parents = new[] { + Html.ActionLink("Users", "Index"), + Html.ActionLink(Model.Name, "Details", new { id = Model.Id }) + }; +} + + + +
    + +
    +
    + @ViewRes.EntryIndexStrings.SortBy + @Html.Partial("Partials/Knockout/_Dropdown", new DropdownViewModel(Translate.ActivityEntrySortRuleNames.ValuesAndNamesStrings, "sort", "sortName")) +
    +
    + +
    +
    @ViewRes.User.EntryEditsStrings.EntryType
    +
    + @Html.Partial("Partials/Knockout/_DropdownList", new DropdownListViewModel(Translate.ActivityEntryTargetTypeNames.ValuesAndNamesStrings, "entryType")) +
    +
    + +
    +
    + +
    +
    + +
    + +
    + @Html.Partial("Partials/Activityfeed/_ActivityEntryKnockout", new ActivityEntryKnockoutViewModel("$parent.getEntryTypeName", "$parent.getActivityFeedEventName", "$parents[1].getChangedFieldNames", true)) +
    + +
    +

    + View more +

    + +@section BodyScripts { + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/User/FavoriteSongs.cshtml b/VocaDbWeb.Core/Views/User/FavoriteSongs.cshtml new file mode 100644 index 0000000000..68fb932a09 --- /dev/null +++ b/VocaDbWeb.Core/Views/User/FavoriteSongs.cshtml @@ -0,0 +1,49 @@ +@using VocaDb.Web.Helpers +@using Res = ViewRes.User.FavoriteSongsStrings +@using SharedRes = ViewRes.SharedStrings +@model VocaDb.Web.Models.User.FavoriteSongs +@inject LaravelMixHelper LaravelMixHelper + +@{ + PageProperties.Title = "Songs rated by " + Model.User.Name; + ViewBag.Parents = new[] { + Html.ActionLink(Model.User.Name, "Details", "User", new {id = Model.User.Id}, null) + }; +} + +@{ Html.RenderPartial("Partials/_RatedSongs"); } + +@section BodyScripts { + + + + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/User/ForgotPassword.cshtml b/VocaDbWeb.Core/Views/User/ForgotPassword.cshtml new file mode 100644 index 0000000000..d1c2f999ba --- /dev/null +++ b/VocaDbWeb.Core/Views/User/ForgotPassword.cshtml @@ -0,0 +1,47 @@ +@using VocaDb.Web.Models.Shared.Partials.Shared +@using Res = ViewRes.User.ForgotPasswordStrings +@model VocaDb.Web.Models.User.ForgotPassword + +@{ + PageProperties.Title = Res.RequestPasswordReset; +} + +@section Head { + +} + +@Html.Partial("Partials/Shared/_ValidationSummaryPanel", new ValidationSummaryPanelViewModel(Res.UnableToProcessRequest)) + +@using (Html.BeginForm()) { + +
    + +
    +
    + @Html.TextBoxFor(m => m.Username)
    + @Html.ValidationMessageFor(m => m.Username) +
    + +
    + +
    +
    + @Html.TextBoxFor(m => m.Email)
    + @Html.ValidationMessageFor(m => m.Email) +
    + +
    + CAPTCHA +
    +
    + @Html.Partial("Partials/Shared/_ReCaptcha2") + + @Html.ValidationMessage("CAPTCHA") +
    + +
    +

    + +

    + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/User/Index.cshtml b/VocaDbWeb.Core/Views/User/Index.cshtml new file mode 100644 index 0000000000..8e0a4ce016 --- /dev/null +++ b/VocaDbWeb.Core/Views/User/Index.cshtml @@ -0,0 +1,35 @@ +@using VocaDb.Web.Helpers +@model VocaDb.Web.Models.User.Index +@inject LaravelMixHelper LaravelMixHelper + +@{ + PageProperties.Title = ViewRes.SharedStrings.Users; +} + +@{ Html.RenderPartial("Partials/_ListUsers"); } + +@section BodyScripts { + + + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/User/LoginTwitterComplete.cshtml b/VocaDbWeb.Core/Views/User/LoginTwitterComplete.cshtml new file mode 100644 index 0000000000..76ac795b92 --- /dev/null +++ b/VocaDbWeb.Core/Views/User/LoginTwitterComplete.cshtml @@ -0,0 +1,38 @@ +@using VocaDb.Web.Models.Shared.Partials.Shared +@model VocaDb.Web.Models.User.RegisterOpenAuthModel + +@{ + PageProperties.Title = ViewRes.User.LoginUsingAuthStrings.LoginUsingTwitter; +} + +@Html.Partial("Partials/Shared/_ValidationSummaryPanel", new ValidationSummaryPanelViewModel(ViewRes.User.LoginStrings.UnableToLogin)) + +

    @ViewRes.User.LoginUsingAuthStrings.AlmostDone

    + +@using (Html.BeginForm()) { + + @Html.AntiForgeryToken() + @Html.HiddenFor(m => m.AccessToken) + @Html.HiddenFor(m => m.TwitterId) + @Html.HiddenFor(m => m.TwitterName) + +
    + @Html.LabelFor(m => m.UserName) +
    +
    + @Html.TextBoxFor(m => m.UserName, new { size = 40, maxlength = 100 }) + @Html.ValidationMessageFor(m => m.UserName) +
    + +
    + @Html.LabelFor(m => m.Email) +
    +
    + @Html.TextBoxFor(m => m.Email, new { size = 40, maxlength = 50 }) + @Html.ValidationMessageFor(m => m.Email) +
    + +

    + +

    +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/User/Messages.cshtml b/VocaDbWeb.Core/Views/User/Messages.cshtml new file mode 100644 index 0000000000..0d44479221 --- /dev/null +++ b/VocaDbWeb.Core/Views/User/Messages.cshtml @@ -0,0 +1,146 @@ +@using VocaDb.Model.Domain.Users +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using VocaDb.Web.Models.Shared.Partials.Shared +@using VocaDb.Web.Models.Shared.Partials.User +@using Res = ViewRes.User.MessagesStrings +@model VocaDb.Web.Models.User.Messages +@inject LaravelMixHelper LaravelMixHelper + +@{ + PageProperties.Title = Res.Messages; + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.Users, "Index"), + Html.ActionLink(Model.User.Name, "Details", new { id = Model.User.Id }) + }; +} + +@{ + void MessageFolderTab(string id, string binding, bool colFrom, bool colTo, bool showUnread, bool allowDelete) { + +
    +
    +
    + + +
    + @Html.Partial("Partials/Knockout/_BasicEntryLinkLockingAutoComplete", new BasicEntryLinkLockingAutoCompleteViewModel("userAutocomplete", "anotherUser")) +
    +
    +
    + + + + + + + + @if (colFrom) { + + } + @if (colTo) { + + } + + + + + + + + + @if (colFrom) { + + } + @if (colTo) { + + } + + + + +
    + @if (allowDelete) { + + } + @Res.Date@Res.From@Res.To@Res.Subject
    + + @Html.Partial("Partials/User/_IconAndNameKnockout", new IconAndNameKnockoutViewModel("sender.iconUrl", "sender.name"))@Html.Partial("Partials/User/_IconAndNameKnockout", new IconAndNameKnockoutViewModel("receiver.iconUrl", "receiver.name")) + + @Html.Partial("Partials/Shared/_NotificationIcon") +   + + @if (showUnread) { + + unread + + } + + @ViewRes.SharedStrings.Delete
    + +

    @ViewRes.SharedStrings.ShowMore

    +
    + } +} + +@Html.Partial("Partials/Shared/_ValidationSummaryPanel", new ValidationSummaryPanelViewModel(Res.MessageSendError)) + + + + + +@section BodyScripts { + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/User/OwnedArtistForUserEditRow.cshtml b/VocaDbWeb.Core/Views/User/OwnedArtistForUserEditRow.cshtml new file mode 100644 index 0000000000..0fac13c3ff --- /dev/null +++ b/VocaDbWeb.Core/Views/User/OwnedArtistForUserEditRow.cshtml @@ -0,0 +1,17 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Artist +@model VocaDb.Model.DataContracts.Users.ArtistForUserContract + + + + @using (Html.BeginCollectionItem("OwnedArtists")) { + @Html.HiddenFor(m => m.Id) + @Html.HiddenFor(m => m.Artist.Id) + @Html.HiddenFor(m => m.Artist.Name) + @Html.Partial("Partials/Artist/_ArtistLink", new ArtistLinkViewModel(Model.Artist)) + } + + + @ViewRes.SharedStrings.Delete + + \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/User/Partials/_AlbumCollection.cshtml b/VocaDbWeb.Core/Views/User/Partials/_AlbumCollection.cshtml new file mode 100644 index 0000000000..9dbb6d37b9 --- /dev/null +++ b/VocaDbWeb.Core/Views/User/Partials/_AlbumCollection.cshtml @@ -0,0 +1,198 @@ +@using VocaDb.Web.Helpers +@using Resources +@using VocaDb.Model.Service.Search +@using VocaDb.Web.Models.Shared.Partials.Album +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using VocaDb.Web.Models.Shared.Partials.Search +@using VocaDb.Web.Resources.Domain +@using SharedRes = ViewRes.SharedStrings +@using Res = ViewRes.User.AlbumCollectionStrings + + + + + +
    + + + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + + + + + + + + + + + + + + + + + + + + + + +
    + + @Res.Album + + + + + @Res.ReleaseDate + + + + @Res.Status + + @Res.MediaType + + @Res.Rating +
    + + + + +
    + @Html.Partial("Partials/Knockout/_DraftIcon", new DraftIconViewModel("status")) +
    + +
    + + +
    + +
    +
    + + + + + + + +
    + + + + + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + +
    + diff --git a/VocaDbWeb.Core/Views/User/Partials/_Events.cshtml b/VocaDbWeb.Core/Views/User/Partials/_Events.cshtml new file mode 100644 index 0000000000..93b60a7681 --- /dev/null +++ b/VocaDbWeb.Core/Views/User/Partials/_Events.cshtml @@ -0,0 +1,35 @@ +@using VocaDb.Model.Domain +@using VocaDb.Model.Domain.Users +@using Res = ViewRes.User.DetailsStrings + + + +
    + + + + + + + + +
    + + Picture + + + {{name}} + +
    + {{$data.date | formatDate: 'l'}} +
    +
    + +
    diff --git a/VocaDbWeb.Core/Views/User/Partials/_FollowedArtists.cshtml b/VocaDbWeb.Core/Views/User/Partials/_FollowedArtists.cshtml new file mode 100644 index 0000000000..4fcbb3c680 --- /dev/null +++ b/VocaDbWeb.Core/Views/User/Partials/_FollowedArtists.cshtml @@ -0,0 +1,70 @@ +@using VocaDb.Web.Models.Shared.Partials.Artist +@using VocaDb.Web.Models.Shared.Partials.Knockout + + + +
    + + + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + + + + + + + + + + + + + +
    + @ViewRes.SharedStrings.ArtistName +
    + + + + + +   + + @Html.Partial("Partials/Knockout/_DraftIcon", new DraftIconViewModel("status")) +
    + +
    +
    + +
    + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + +
    + diff --git a/VocaDbWeb.Core/Views/User/Partials/_ListUsers.cshtml b/VocaDbWeb.Core/Views/User/Partials/_ListUsers.cshtml new file mode 100644 index 0000000000..4ea1f618cb --- /dev/null +++ b/VocaDbWeb.Core/Views/User/Partials/_ListUsers.cshtml @@ -0,0 +1,109 @@ +@using VocaDb.Model.Domain.Globalization +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using SharedRes = ViewRes.SharedStrings +@using Res = ViewRes.User.IndexStrings + + + + + +
    + + + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + + + + + + + + + + + + + + + + + +
    + + @ViewRes.User.DetailsStrings.UserName + + + + + @ViewRes.User.DetailsStrings.MemberSince + + + + + @ViewRes.User.DetailsStrings.UserGroup + + +
    + + + + + + + + + +
    + +
    + @Html.Partial("Partials/Knockout/_ServerSidePaging") +
    + +
    diff --git a/VocaDbWeb.Core/Views/User/Partials/_RatedSongs.cshtml b/VocaDbWeb.Core/Views/User/Partials/_RatedSongs.cshtml new file mode 100644 index 0000000000..2f2bcc73de --- /dev/null +++ b/VocaDbWeb.Core/Views/User/Partials/_RatedSongs.cshtml @@ -0,0 +1,101 @@ +@using VocaDb.Web.Helpers +@using VocaDb.Model.Service.Search +@using VocaDb.Web.Models.Shared.Partials.Knockout +@using VocaDb.Web.Models.Shared.Partials.Search +@using VocaDb.Web.Models.Shared.Partials.User +@using VocaDb.Web.Resources.Domain +@using SharedRes = ViewRes.SharedStrings +@using SearchRes = ViewRes.Search.IndexStrings +@using Res = ViewRes.User.RatedSongsStrings + + + + + +
    + + @{ Html.RenderPartial("Partials/_SongSearchList"); } + +
    + diff --git a/VocaDbWeb.Core/Views/User/Partials/_SongLists.cshtml b/VocaDbWeb.Core/Views/User/Partials/_SongLists.cshtml new file mode 100644 index 0000000000..daf0cef27f --- /dev/null +++ b/VocaDbWeb.Core/Views/User/Partials/_SongLists.cshtml @@ -0,0 +1,18 @@ +@using VocaDb.Web.Models.Shared.Partials.Song +@using R = ViewRes.User.DetailsStrings +@model VocaDb.Model.DataContracts.Users.UserDetailsContract + + + +@{ var ownProfile = UserContext.IsLoggedIn && UserContext.LoggedUserId == Model.Id && UserContext.LoggedUser.Active; } + +@{ Html.RenderPartial("Partials/_SongListsFilters"); } + +@Html.Partial("Partials/Song/_SongListsKnockout", new SongListsKnockoutViewModel("items", true)) + +

    @ViewRes.SharedStrings.ShowMore

    + +@if (ownProfile) { + @R.CreateNewList + @R.ImportSongList +} diff --git a/VocaDbWeb.Core/Views/User/Partials/_UserMessageKnockout.cshtml b/VocaDbWeb.Core/Views/User/Partials/_UserMessageKnockout.cshtml new file mode 100644 index 0000000000..b9e69eb5f3 --- /dev/null +++ b/VocaDbWeb.Core/Views/User/Partials/_UserMessageKnockout.cshtml @@ -0,0 +1,30 @@ +@using VocaDb.Web.Models.Shared.Partials.User + + + +
    + +
    +
    + +
    + + + @ViewRes.User.MessagesStrings.From + + @Html.Partial("Partials/User/_IconAndNameLinkKnockout", new IconAndNameLinkKnockoutViewModel()) + . + + + @ViewRes.User.MessagesStrings.To + + @Html.Partial("Partials/User/_IconAndNameLinkKnockout", new IconAndNameLinkKnockoutViewModel()) + . + +
    +
    +

    + + @ViewRes.User.MessagesStrings.Reply + +
    \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/User/RequestVerification.cshtml b/VocaDbWeb.Core/Views/User/RequestVerification.cshtml new file mode 100644 index 0000000000..371aa39533 --- /dev/null +++ b/VocaDbWeb.Core/Views/User/RequestVerification.cshtml @@ -0,0 +1,87 @@ +@using VocaDb.Web.Helpers +@using Res = ViewRes.User.RequestVerificationStrings +@model string +@inject LaravelMixHelper LaravelMixHelper +@inject Login Login + +@{ + PageProperties.Title = Res.PageTitle; +} + +
    + + @if (!Login.Manager.IsLoggedIn) { +
    + @Html.Raw(string.Format(Res.NotLoggedInMessage, Html.ActionLink(Res.Login, "Login"), Html.ActionLink(Res.Signup, "Create"))) +
    + } else { + +
    + + + +
    + + +
    +
    + +
    + +
    + + + +
    + +
    + + + + +
    +
    + +
    + + + +
    + + +
    + + } + +
    + @Html.Raw(BrandableStrings.User.RequestVerificationInfo) +
    + +
    + +@section BodyScripts { + + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/User/ResetPassword.cshtml b/VocaDbWeb.Core/Views/User/ResetPassword.cshtml new file mode 100644 index 0000000000..77d44e92cf --- /dev/null +++ b/VocaDbWeb.Core/Views/User/ResetPassword.cshtml @@ -0,0 +1,35 @@ +@using VocaDb.Web.Models.Shared.Partials.Shared +@model VocaDb.Web.Models.User.ResetPassword +@{ + PageProperties.Title = "Reset password"; +} + +@Html.Partial("Partials/Shared/_ValidationSummaryPanel", new ValidationSummaryPanelViewModel("Unable to process request")) + +@using (Html.BeginForm()) { + + @Html.HiddenFor(m => m.RequestId) + +
    + @Html.LabelFor(m => m.NewPass) +
    +
    + @Html.PasswordFor(m => m.NewPass)
    + @Html.ValidationMessageFor(m => m.NewPass) +
    + +
    + @Html.LabelFor(m => m.NewPassAgain) +
    +
    + @Html.PasswordFor(m => m.NewPassAgain)
    + @Html.ValidationMessageFor(m => m.NewPassAgain) +
    +
    + +

    + +

    + + +} diff --git a/VocaDbWeb.Core/Views/User/Stats.cshtml b/VocaDbWeb.Core/Views/User/Stats.cshtml new file mode 100644 index 0000000000..8e1f84005f --- /dev/null +++ b/VocaDbWeb.Core/Views/User/Stats.cshtml @@ -0,0 +1,31 @@ +@model VocaDb.Model.DataContracts.Users.UserContract + +@{ + PageProperties.Title = "Stats for " + Model.Name; +} + +
    + +@section BodyScripts { + + + + +} + + diff --git a/VocaDbWeb.Core/Views/Venue/Details.cshtml b/VocaDbWeb.Core/Views/Venue/Details.cshtml new file mode 100644 index 0000000000..e27ddc002a --- /dev/null +++ b/VocaDbWeb.Core/Views/Venue/Details.cshtml @@ -0,0 +1,110 @@ +@using System.Globalization +@using VocaDb.Model.Domain.Venues +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@using VocaDb.Web.Models.Shared.Partials.Shared +@model VocaDb.Model.DataContracts.Venues.VenueForApiContract +@inject LaravelMixHelper LaravelMixHelper +@inject Login Login + +@{ + + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.Venues, "EventsByVenue", "Event") + }; + + var descriptionHtml = MarkdownParser.GetHtml(Model.Description); + var descriptionStripped = MarkdownParser.GetPlainText(Model.Description); + + PageProperties.Description = descriptionStripped; + PageProperties.Robots = Model.Deleted ? PagePropertiesData.Robots_Noindex_Follow : string.Empty; + +} + +@section Toolbar { + @Html.ActionLink(ViewRes.SharedStrings.Edit, "Edit", new { id = Model.Id }, new { id = "editVenueLink", @class = (Login.CanEdit(Model) ? "" : "disabled") }) + @Html.ActionLink(ViewRes.EntryDetailsStrings.ViewModifications, "Versions", new { id = Model.Id }, new { id = "viewVersions" }) + @if (Login.CanManageDb) { + @ViewRes.Event.EventsBySeriesStrings.CreateEvent + } + @ViewRes.EntryDetailsStrings.ReportAnError + @Html.Partial("Partials/Shared/_EntryStatusMessage", new EntryStatusMessageViewModel(Model.Status)) +} + +@if (Model.Deleted) { + @Html.Partial("Partials/EntryDetails/_DeletedBanner", new DeletedBannerViewModel(null)) +} + +@if (Model.Coordinates.HasValue) { + @Html.Partial("Partials/Shared/_EmbedGoogleMaps", new EmbedGoogleMapsViewModel(Model.Coordinates)) +} + +
    +
    + @if (!string.IsNullOrEmpty(Model.Description)) { +

    @Html.Raw(descriptionHtml)

    + } +
    + + @if (Model.WebLinks.Any()) { + @Html.Partial("Partials/EntryDetails/_ExternalLinksList", new ExternalLinksListViewModel(Model.WebLinks, false)) +
    + } + + @if (!string.IsNullOrEmpty(Model.AdditionalNames)) { +

    + @ViewRes.Venue.DetailsStrings.Aliases: @Model.AdditionalNames +

    + } + + @if (!string.IsNullOrEmpty(Model.AddressCountryCode)) { +

    + @ViewRes.Venue.DetailsStrings.Country: @(new RegionInfo(Model.AddressCountryCode).DisplayName) +

    + } + + @if (!string.IsNullOrEmpty(Model.Address)) { +

    + @ViewRes.Venue.DetailsStrings.Address: @Model.Address +

    + } +
    + +

    @ViewRes.SharedStrings.ReleaseEvents

    +
      + @foreach (var ev in Model.Events) { +
    • + @Html.ActionLink(ev.Name, "Details", "Event", new { id = ev.Id }, null) + @if (ev.Date != null) { + (@ev.Date.Value.ToString("d")) + } +
    • + } +
    + +@Html.Partial("Partials/EntryDetails/_ReportEntryPopupKnockout", new ReportEntryPopupKnockoutViewModel()) + +@section BodyScripts { + + +} diff --git a/VocaDbWeb.Core/Views/Venue/Versions.cshtml b/VocaDbWeb.Core/Views/Venue/Versions.cshtml new file mode 100644 index 0000000000..bc80c2d398 --- /dev/null +++ b/VocaDbWeb.Core/Views/Venue/Versions.cshtml @@ -0,0 +1,16 @@ +@using VocaDb.Web.Code +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@model VocaDb.Web.Models.Venue.Versions + +@{ + PageProperties.Title = ViewRes.EntryDetailsStrings.Revisions + " - " + Model.Entry.Name; + PageProperties.Robots = PagePropertiesData.Robots_Noindex_Nofollow; + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.Venues, "EventsByVenue", "Event"), + Html.ActionLink(Model.Entry.Name, "Details", new { id = Model.Entry.Id }) + }; +} + +@Html.Partial("Partials/ArchivedEntry/_CurrentVersionMessage", new CurrentVersionMessageViewModel(Model.Entry.Version, Model.Entry.Status)) + +@Html.Partial("Partials/ArchivedEntry/_ArchivedObjectVersions", new ArchivedObjectVersionsViewModel(Model.ArchivedVersions, id => Url.Action("ViewVersion", new { id }))) \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/Venue/ViewVersion.cshtml b/VocaDbWeb.Core/Views/Venue/ViewVersion.cshtml new file mode 100644 index 0000000000..5e3cead94b --- /dev/null +++ b/VocaDbWeb.Core/Views/Venue/ViewVersion.cshtml @@ -0,0 +1,75 @@ +@using VocaDb.Web.Code +@using VocaDb.Web.Helpers +@using VocaDb.Web.Models.Shared.Partials.ArchivedEntry +@using VocaDb.Web.Models.Shared.Partials.EntryDetails +@using VocaDb.Web.Models.Shared.Partials.Venue +@model VocaDb.Web.Models.Shared.ViewVersion +@inject Login Login + +@{ + + PageProperties.Title = "Revision " + Model.Entry.ArchivedVersion.Version + " for " + Model.Entry.Name; + PageProperties.Robots = PagePropertiesData.Robots_Noindex_Nofollow; + + if (Model.Entry.Venue != null) { + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.Venues, "EventsByVenue", "Event"), + Html.ActionLink(Model.Entry.Venue.Name, "Details", new { id = Model.Entry.Venue.Id }), + Html.ActionLink("Revisions", "Versions", new { id = Model.Entry.Venue.Id }) + }; + } else { + ViewBag.Parents = new[] { + Html.ActionLink(ViewRes.SharedStrings.Venues, "EventsByVenue", "Event"), + }; + } + +} + +@section Toolbar { + @ViewRes.EntryDetailsStrings.ReportAnError + @if (Login.CanViewHiddenRevisions) { + if (Model.Entry.ArchivedVersion.Hidden) { + @Html.ActionLink(ViewRes.ViewVersionStrings.UnhideVersion, "UpdateVersionVisibility", new { archivedVersionId = Model.Entry.ArchivedVersion.Id, hidden = false }, new { id = "showLink", onclick = string.Format("return confirm(\"{0}\");", ViewRes.ViewVersionStrings.ConfirmUnhide) }) + } else { + @Html.ActionLink(ViewRes.ViewVersionStrings.HideVersion, "UpdateVersionVisibility", new { archivedVersionId = Model.Entry.ArchivedVersion.Id, hidden = true }, new { id = "hideLink", onclick = string.Format("return confirm(\"{0}\");", ViewRes.ViewVersionStrings.ConfirmHide) }) + } + } +} + +@if (Model.Entry.ArchivedVersion.Hidden) { + @Html.Partial("Partials/EntryDetails/_HiddenBanner") +} + +@if (Model.Entry.ComparableVersions.Any()) { + using (Html.BeginForm("ViewVersion", "Venue", FormMethod.Post, new { @class = "form form-inline" })) { + @:Compare to: @Html.DropDownListFor(m => m.ComparedVersionId, ViewHelper.CreateSelectList(Model.Entry.ComparableVersions, i => i.Id, i => i.Version + " (" + i.TranslateChangedFields(Model.EnumTranslations) + " by " + i.AgentName + ")", Model.Entry.ComparedVersionId), new { @class = "input-xlarge", onchange = "$(this).closest('form').submit();" }) + + } +} + +@Html.Partial("Partials/ArchivedEntry/_ArchivedObjectVersionProperties", new ArchivedObjectVersionPropertiesViewModel(Model.Version(Model.Entry.ArchivedVersion), Model.Version(Model.Entry.ComparedVersion))) + +@Html.Partial("Partials/Venue/_PrintArchivedVenueData", new PrintArchivedVenueDataViewModel(Model.Entry.Versions)) + +@Html.Partial("Partials/EntryDetails/_ReportEntryVersionPopupKnockout", new ReportEntryVersionPopupKnockoutViewModel()) + +@section BodyScripts { + + + +} \ No newline at end of file diff --git a/VocaDbWeb.Core/Views/_ViewImports.cshtml b/VocaDbWeb.Core/Views/_ViewImports.cshtml new file mode 100644 index 0000000000..c5b9a67fb5 --- /dev/null +++ b/VocaDbWeb.Core/Views/_ViewImports.cshtml @@ -0,0 +1,5 @@ +@using VocaDb.Web +@using VocaDb.Web.Code +@using VocaDb.Web.Models +@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers +@inherits VocaDbPage diff --git a/VocaDbWeb.Core/Views/_ViewStart.cshtml b/VocaDbWeb.Core/Views/_ViewStart.cshtml new file mode 100644 index 0000000000..1c81a2c431 --- /dev/null +++ b/VocaDbWeb.Core/Views/_ViewStart.cshtml @@ -0,0 +1,5 @@ +@using VocaDb.Model.Utils + +@{ + Layout = "~/Views/Shared/_Layout.cshtml"; +} \ No newline at end of file