mirror of https://github.com/abpframework/abp.git
93 changed files with 6097 additions and 75 deletions
@ -0,0 +1,28 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Authorization.Permissions; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Permissions |
||||
|
{ |
||||
|
public class FakePermissionStore : IPermissionStore, ITransientDependency |
||||
|
{ |
||||
|
public Task<bool> IsGrantedAsync(string name, string providerName, string providerKey) |
||||
|
{ |
||||
|
var result = (name.Contains("MyComponent1") || name.Contains("MyComponent3")); |
||||
|
return Task.FromResult(result); |
||||
|
} |
||||
|
|
||||
|
public Task<MultiplePermissionGrantResult> IsGrantedAsync(string[] names, string providerName, string providerKey) |
||||
|
{ |
||||
|
var result = new MultiplePermissionGrantResult(); |
||||
|
foreach (var name in names) |
||||
|
{ |
||||
|
result.Result.Add(name, (name.Contains("MyComponent1") || name.Contains("MyComponent3")) |
||||
|
? PermissionGrantResult.Granted |
||||
|
: PermissionGrantResult.Prohibited); |
||||
|
} |
||||
|
|
||||
|
return Task.FromResult(result); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
using Volo.Abp.Authorization.Permissions; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Permissions |
||||
|
{ |
||||
|
public class TestPermissionDefinitionProvider : PermissionDefinitionProvider |
||||
|
{ |
||||
|
public override void Define(IPermissionDefinitionContext context) |
||||
|
{ |
||||
|
var group = context.AddGroup("TestGroup"); |
||||
|
|
||||
|
group.AddPermission("MyComponent1"); |
||||
|
group.AddPermission("MyComponent2"); |
||||
|
group.AddPermission("MyComponent3"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,129 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using System.Security.Claims; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.DependencyInjection.Extensions; |
||||
|
using Microsoft.Extensions.Hosting; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using NSubstitute; |
||||
|
using Shouldly; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Theming; |
||||
|
using Volo.Abp.Security.Claims; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Tests.Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Toolbars |
||||
|
{ |
||||
|
public class Toolbar_Tests : AbpAspNetCoreMvcUiThemeSharedTestBase |
||||
|
{ |
||||
|
protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services) |
||||
|
{ |
||||
|
services.Configure<AbpToolbarOptions>(options => |
||||
|
{ |
||||
|
options.Contributors.Add(new MyToolbarContributor()); |
||||
|
options.Contributors.Add(new MyToolbarContributor2()); |
||||
|
}); |
||||
|
|
||||
|
var claims = new List<Claim>() { |
||||
|
new Claim(AbpClaimTypes.UserId, "1fcf46b2-28c3-48d0-8bac-fa53268a2775"), |
||||
|
}; |
||||
|
|
||||
|
var identity = new ClaimsIdentity(claims); |
||||
|
var claimsPrincipal = new ClaimsPrincipal(identity); |
||||
|
var principalAccessor = Substitute.For<ICurrentPrincipalAccessor>(); |
||||
|
principalAccessor.Principal.Returns(ci => claimsPrincipal); |
||||
|
Thread.CurrentPrincipal = claimsPrincipal; |
||||
|
|
||||
|
var themeManager = Substitute.For<IThemeManager>(); |
||||
|
themeManager.CurrentTheme.Returns(x => null); |
||||
|
services.Replace(ServiceDescriptor.Singleton<IThemeManager>(themeManager)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void AbpToolbarOptions_Should_Contain_Contributors() |
||||
|
{ |
||||
|
var options = GetRequiredService<IOptions<AbpToolbarOptions>>().Value; |
||||
|
options.Contributors.Count.ShouldBe(2); |
||||
|
options.Contributors.ShouldContain(x => x.GetType() == typeof(MyToolbarContributor)); |
||||
|
options.Contributors.ShouldContain(x => x.GetType() == typeof(MyToolbarContributor2)); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task ToolbarManager_Should_Return_Toolbar() |
||||
|
{ |
||||
|
var toolbarManager = GetRequiredService<ToolbarManager>(); |
||||
|
var toolbar = await toolbarManager.GetAsync(StandardToolbars.Main); |
||||
|
|
||||
|
toolbar.Items.Count.ShouldBe(3); |
||||
|
toolbar.Items[0].ComponentType.ShouldBe(typeof(MyComponent1)); |
||||
|
toolbar.Items[1].ComponentType.ShouldBe(typeof(MyComponent3)); |
||||
|
toolbar.Items[2].ComponentType.ShouldBe(typeof(MyComponent4)); |
||||
|
} |
||||
|
|
||||
|
public class MyToolbarContributor : IToolbarContributor |
||||
|
{ |
||||
|
public Task ConfigureToolbarAsync(IToolbarConfigurationContext context) |
||||
|
{ |
||||
|
if (context.Toolbar.Name != StandardToolbars.Main) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
context.Toolbar.Items.Add(new ToolbarItem(typeof(MyComponent1), requiredPermissionName: "MyComponent1")); |
||||
|
context.Toolbar.Items.Add(new ToolbarItem(typeof(MyComponent2), requiredPermissionName: "MyComponent2")); |
||||
|
|
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class MyToolbarContributor2 : IToolbarContributor |
||||
|
{ |
||||
|
public Task ConfigureToolbarAsync(IToolbarConfigurationContext context) |
||||
|
{ |
||||
|
if (context.Toolbar.Name != StandardToolbars.Main) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
context.Toolbar.Items.Add(new ToolbarItem(typeof(MyComponent3), requiredPermissionName: "MyComponent3")); |
||||
|
context.Toolbar.Items.Add(new ToolbarItem(typeof(MyComponent4))); |
||||
|
|
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class MyComponent1 : AbpViewComponent |
||||
|
{ |
||||
|
public IViewComponentResult InvokeAsync() |
||||
|
{ |
||||
|
return Content("MyComponent1"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class MyComponent2 : AbpViewComponent |
||||
|
{ |
||||
|
public IViewComponentResult InvokeAsync() |
||||
|
{ |
||||
|
return Content("MyComponent2"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class MyComponent3 : AbpViewComponent |
||||
|
{ |
||||
|
public IViewComponentResult InvokeAsync() |
||||
|
{ |
||||
|
return Content("MyComponent3"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class MyComponent4 : AbpViewComponent |
||||
|
{ |
||||
|
public IViewComponentResult InvokeAsync() |
||||
|
{ |
||||
|
return Content("MyComponent4"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
using Volo.Abp.Authorization; |
||||
|
using Volo.Abp.Autofac; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace Volo.Abp.UI.Navigation |
||||
|
{ |
||||
|
[DependsOn(typeof(AbpUiNavigationModule))] |
||||
|
[DependsOn(typeof(AbpAuthorizationModule))] |
||||
|
[DependsOn(typeof(AbpAutofacModule))] |
||||
|
public class AbpUiNavigationTestModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpNavigationOptions>(options => |
||||
|
{ |
||||
|
options.MenuContributors.Add(new MenuManager_Tests.TestMenuContributor1()); |
||||
|
options.MenuContributors.Add(new MenuManager_Tests.TestMenuContributor2()); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Authorization.Permissions; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.UI.Navigation |
||||
|
{ |
||||
|
public class FakePermissionStore : IPermissionStore, ITransientDependency |
||||
|
{ |
||||
|
public Task<bool> IsGrantedAsync(string name, string providerName, string providerKey) |
||||
|
{ |
||||
|
var result = (name.Contains("Administration") || name.Contains("Dashboard")) && !name.Contains("SubMenu1"); |
||||
|
return Task.FromResult(result); |
||||
|
} |
||||
|
|
||||
|
public Task<MultiplePermissionGrantResult> IsGrantedAsync(string[] names, string providerName, string providerKey) |
||||
|
{ |
||||
|
var result = new MultiplePermissionGrantResult(); |
||||
|
foreach (var name in names) |
||||
|
{ |
||||
|
result.Result.Add(name, (name.Contains("Administration") || name.Contains("Dashboard")) && !name.Contains("SubMenu1") |
||||
|
? PermissionGrantResult.Granted |
||||
|
: PermissionGrantResult.Prohibited); |
||||
|
} |
||||
|
|
||||
|
return Task.FromResult(result); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
using Volo.Abp.Authorization.Permissions; |
||||
|
|
||||
|
namespace Volo.Abp.UI.Navigation |
||||
|
{ |
||||
|
public class TestPermissionDefinitionProvider : PermissionDefinitionProvider |
||||
|
{ |
||||
|
public override void Define(IPermissionDefinitionContext context) |
||||
|
{ |
||||
|
var group = context.AddGroup("TestGroup"); |
||||
|
|
||||
|
group.AddPermission("Dashboard"); |
||||
|
|
||||
|
group.AddPermission("Administration"); |
||||
|
group.AddPermission("Administration.UserManagement"); |
||||
|
group.AddPermission("Administration.RoleManagement"); |
||||
|
|
||||
|
group.AddPermission("Administration.DashboardSettings"); |
||||
|
|
||||
|
group.AddPermission("Administration.SubMenu1"); |
||||
|
group.AddPermission("Administration.SubMenu1.1"); |
||||
|
group.AddPermission("Administration.SubMenu1.2"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
@ -0,0 +1,109 @@ |
|||||
|
using System; |
||||
|
using Microsoft.EntityFrameworkCore.Migrations; |
||||
|
|
||||
|
namespace Volo.CmsKit.Migrations |
||||
|
{ |
||||
|
public partial class BlogPost_Author : Migration |
||||
|
{ |
||||
|
protected override void Up(MigrationBuilder migrationBuilder) |
||||
|
{ |
||||
|
migrationBuilder.DropForeignKey( |
||||
|
name: "FK_CmsBlogPosts_CmsUsers_CreatorId", |
||||
|
table: "CmsBlogPosts"); |
||||
|
|
||||
|
migrationBuilder.DropForeignKey( |
||||
|
name: "FK_CmsBlogPosts_CmsUsers_DeleterId", |
||||
|
table: "CmsBlogPosts"); |
||||
|
|
||||
|
migrationBuilder.DropForeignKey( |
||||
|
name: "FK_CmsBlogPosts_CmsUsers_LastModifierId", |
||||
|
table: "CmsBlogPosts"); |
||||
|
|
||||
|
migrationBuilder.DropIndex( |
||||
|
name: "IX_CmsBlogPosts_CreatorId", |
||||
|
table: "CmsBlogPosts"); |
||||
|
|
||||
|
migrationBuilder.DropIndex( |
||||
|
name: "IX_CmsBlogPosts_DeleterId", |
||||
|
table: "CmsBlogPosts"); |
||||
|
|
||||
|
migrationBuilder.DropIndex( |
||||
|
name: "IX_CmsBlogPosts_LastModifierId", |
||||
|
table: "CmsBlogPosts"); |
||||
|
|
||||
|
migrationBuilder.AddColumn<Guid>( |
||||
|
name: "AuthorId", |
||||
|
table: "CmsBlogPosts", |
||||
|
type: "uniqueidentifier", |
||||
|
nullable: false, |
||||
|
defaultValue: new Guid("00000000-0000-0000-0000-000000000000")); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_CmsBlogPosts_AuthorId", |
||||
|
table: "CmsBlogPosts", |
||||
|
column: "AuthorId"); |
||||
|
|
||||
|
migrationBuilder.AddForeignKey( |
||||
|
name: "FK_CmsBlogPosts_CmsUsers_AuthorId", |
||||
|
table: "CmsBlogPosts", |
||||
|
column: "AuthorId", |
||||
|
principalTable: "CmsUsers", |
||||
|
principalColumn: "Id", |
||||
|
onDelete: ReferentialAction.Cascade); |
||||
|
} |
||||
|
|
||||
|
protected override void Down(MigrationBuilder migrationBuilder) |
||||
|
{ |
||||
|
migrationBuilder.DropForeignKey( |
||||
|
name: "FK_CmsBlogPosts_CmsUsers_AuthorId", |
||||
|
table: "CmsBlogPosts"); |
||||
|
|
||||
|
migrationBuilder.DropIndex( |
||||
|
name: "IX_CmsBlogPosts_AuthorId", |
||||
|
table: "CmsBlogPosts"); |
||||
|
|
||||
|
migrationBuilder.DropColumn( |
||||
|
name: "AuthorId", |
||||
|
table: "CmsBlogPosts"); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_CmsBlogPosts_CreatorId", |
||||
|
table: "CmsBlogPosts", |
||||
|
column: "CreatorId"); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_CmsBlogPosts_DeleterId", |
||||
|
table: "CmsBlogPosts", |
||||
|
column: "DeleterId"); |
||||
|
|
||||
|
migrationBuilder.CreateIndex( |
||||
|
name: "IX_CmsBlogPosts_LastModifierId", |
||||
|
table: "CmsBlogPosts", |
||||
|
column: "LastModifierId"); |
||||
|
|
||||
|
migrationBuilder.AddForeignKey( |
||||
|
name: "FK_CmsBlogPosts_CmsUsers_CreatorId", |
||||
|
table: "CmsBlogPosts", |
||||
|
column: "CreatorId", |
||||
|
principalTable: "CmsUsers", |
||||
|
principalColumn: "Id", |
||||
|
onDelete: ReferentialAction.Restrict); |
||||
|
|
||||
|
migrationBuilder.AddForeignKey( |
||||
|
name: "FK_CmsBlogPosts_CmsUsers_DeleterId", |
||||
|
table: "CmsBlogPosts", |
||||
|
column: "DeleterId", |
||||
|
principalTable: "CmsUsers", |
||||
|
principalColumn: "Id", |
||||
|
onDelete: ReferentialAction.Restrict); |
||||
|
|
||||
|
migrationBuilder.AddForeignKey( |
||||
|
name: "FK_CmsBlogPosts_CmsUsers_LastModifierId", |
||||
|
table: "CmsBlogPosts", |
||||
|
column: "LastModifierId", |
||||
|
principalTable: "CmsUsers", |
||||
|
principalColumn: "Id", |
||||
|
onDelete: ReferentialAction.Restrict); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,21 @@ |
|||||
|
The MIT License (MIT) |
||||
|
|
||||
|
Copyright (c) Simeon Velichkov <simeonvelichkov@gmail.com> |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
|
SOFTWARE. |
||||
@ -0,0 +1,90 @@ |
|||||
|
|
||||
|
# slugify |
||||
|
|
||||
|
[![npm-version]][npm] [![travis-ci]][travis] [![coveralls-status]][coveralls] |
||||
|
|
||||
|
```js |
||||
|
var slugify = require('slugify') |
||||
|
|
||||
|
slugify('some string') // some-string |
||||
|
|
||||
|
// if you prefer something other than '-' as separator |
||||
|
slugify('some string', '_') // some_string |
||||
|
``` |
||||
|
|
||||
|
- Vanilla ES5 JavaScript |
||||
|
- No dependencies |
||||
|
- Coerces foreign symbols to their English equivalent (check out the [charMap][charmap] for more details) |
||||
|
- Works in the browser (window.slugify) and AMD/CommonJS-flavored module loaders |
||||
|
|
||||
|
## Options |
||||
|
|
||||
|
```js |
||||
|
slugify('some string', { |
||||
|
replacement: '-', // replace spaces with replacement character, defaults to `-` |
||||
|
remove: undefined, // remove characters that match regex, defaults to `undefined` |
||||
|
lower: false, // convert to lower case, defaults to `false` |
||||
|
strict: false, // strip special characters except replacement, defaults to `false` |
||||
|
locale: 'vi' // language code of the locale to use |
||||
|
}) |
||||
|
``` |
||||
|
|
||||
|
## Remove |
||||
|
|
||||
|
For example, to remove `*+~.()'"!:@` from the result slug, you can use `slugify('..', {remove: /[*+~.()'"!:@]/g})`. |
||||
|
|
||||
|
## Locales |
||||
|
|
||||
|
The main `charmap.json` file contains all known characters and their transliteration. All new characters should be added there first. In case you stumble upon a character already set in `charmap.json`, but not transliterated correctly according to your language, then you have to add those characters in `locales.json` to override the already existing transliteration in `charmap.json`, but for your locale only. |
||||
|
|
||||
|
You can get the correct language code of your language from [here](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes). |
||||
|
|
||||
|
## Extend |
||||
|
|
||||
|
Out of the box `slugify` comes with support for a handful of Unicode symbols. For example the `☢` (radioactive) symbol is not defined in the [`charMap`][charmap] and therefore it will be stripped by default: |
||||
|
|
||||
|
```js |
||||
|
slugify('unicode ♥ is ☢') // unicode-love-is |
||||
|
``` |
||||
|
|
||||
|
However you can extend the supported symbols, or override the existing ones with your own: |
||||
|
|
||||
|
```js |
||||
|
slugify.extend({'☢': 'radioactive'}) |
||||
|
slugify('unicode ♥ is ☢') // unicode-love-is-radioactive |
||||
|
``` |
||||
|
|
||||
|
Keep in mind that the `extend` method extends/overrides the default `charMap` for the entire process. In case you need a fresh instance of the slugify's `charMap` object you have to clean up the module cache first: |
||||
|
|
||||
|
```js |
||||
|
delete require.cache[require.resolve('slugify')] |
||||
|
var slugify = require('slugify') |
||||
|
``` |
||||
|
|
||||
|
## Contribute |
||||
|
|
||||
|
1. Add chars to `charmap.json` |
||||
|
2. Run tests `npm test` |
||||
|
3. The tests will build the charmap in `index.js` and will sort the `charmap.json` |
||||
|
4. Commit **all** modified files |
||||
|
|
||||
|
--- |
||||
|
|
||||
|
> This module was originally a vanilla javascript port of [node-slug][node-slug].<br> |
||||
|
> Note that the original [slug][slug] module has been ported to vanilla javascript too.<br> |
||||
|
> One major difference between the two modules is that `slugify` does not depend on the external [unicode][unicode] module. |
||||
|
|
||||
|
|
||||
|
[npm-version]: https://img.shields.io/npm/v/slugify.svg?style=flat-square (NPM Package Version) |
||||
|
[travis-ci]: https://img.shields.io/travis/simov/slugify/master.svg?style=flat-square (Build Status - Travis CI) |
||||
|
[coveralls-status]: https://img.shields.io/coveralls/simov/slugify.svg?style=flat-square (Test Coverage - Coveralls) |
||||
|
|
||||
|
[npm]: https://www.npmjs.com/package/slugify |
||||
|
[travis]: https://travis-ci.org/simov/slugify |
||||
|
[coveralls]: https://coveralls.io/r/simov/slugify?branch=master |
||||
|
|
||||
|
[node-slug]: https://github.com/dodo/node-slug |
||||
|
[slug]: https://www.npmjs.com/package/slug |
||||
|
[unicode]: https://www.npmjs.com/package/unicode |
||||
|
[index]: https://github.com/simov/slugify/blob/master/index.js |
||||
|
[charmap]: https://github.com/simov/slugify/blob/master/config/charmap.json |
||||
@ -0,0 +1,40 @@ |
|||||
|
{ |
||||
|
"name": "slugify", |
||||
|
"version": "1.4.6", |
||||
|
"description": "Slugifies a String", |
||||
|
"keywords": [ |
||||
|
"slugify", |
||||
|
"slug", |
||||
|
"url", |
||||
|
"urlify" |
||||
|
], |
||||
|
"license": "MIT", |
||||
|
"homepage": "https://github.com/simov/slugify", |
||||
|
"author": "Simeon Velichkov <simeonvelichkov@gmail.com> (https://simov.github.io)", |
||||
|
"repository": { |
||||
|
"type": "git", |
||||
|
"url": "https://github.com/simov/slugify.git" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"coveralls": "^3.1.0", |
||||
|
"mocha": "^7.2.0", |
||||
|
"nyc": "^15.1.0" |
||||
|
}, |
||||
|
"main": "./slugify.js", |
||||
|
"files": [ |
||||
|
"LICENSE", |
||||
|
"README.md", |
||||
|
"slugify.d.ts", |
||||
|
"slugify.js" |
||||
|
], |
||||
|
"types": "slugify.d.ts", |
||||
|
"scripts": { |
||||
|
"build": "node bin/build", |
||||
|
"test:ci": "npx mocha --recursive", |
||||
|
"test:cov": "npx nyc --reporter=lcov --reporter=text-summary mocha -- --recursive", |
||||
|
"test": "npm run build && npm run test:ci" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">=8.0.0" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
declare module slugify { |
||||
|
type ExtendArgs = { |
||||
|
[key: string]: any; |
||||
|
} |
||||
|
|
||||
|
export function extend (args: ExtendArgs): void; |
||||
|
} |
||||
|
|
||||
|
declare function slugify( |
||||
|
string: string, |
||||
|
options?: |
||||
|
| { |
||||
|
replacement?: string; |
||||
|
remove?: RegExp; |
||||
|
lower?: boolean; |
||||
|
strict?: boolean; |
||||
|
locale?: string; |
||||
|
} |
||||
|
| string, |
||||
|
|
||||
|
): string; |
||||
|
|
||||
|
export default slugify; |
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,17 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Bundles |
||||
|
{ |
||||
|
public class SlugifyScriptContributor : BundleContributor |
||||
|
{ |
||||
|
public override void ConfigureBundle(BundleConfigurationContext context) |
||||
|
{ |
||||
|
context.Files.Add("/libs/slugify/slugify.js"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,79 @@ |
|||||
|
@page |
||||
|
|
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Contents.Components.EntityContentEditor |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageToolbar |
||||
|
@using Volo.CmsKit.Admin.Web.Pages |
||||
|
@using Volo.CmsKit.Admin.Web.Menus |
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.BlogPosts |
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Tags.Components.TagEditor |
||||
|
@using Volo.CmsKit.Blogs |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.Uppy |
||||
|
@using Volo.CmsKit.Admin.Web.Bundles |
||||
|
|
||||
|
@inherits CmsKitAdminPageBase |
||||
|
|
||||
|
@model CreateModel |
||||
|
|
||||
|
@{ |
||||
|
PageLayout.Content.Title = L["BlogPosts"].Value; |
||||
|
PageLayout.Content.BreadCrumb.Add(L["Menu:CMS"].Value); |
||||
|
PageLayout.Content.MenuItemName = CmsKitAdminMenus.BlogPosts.BlogPostsMenu; |
||||
|
} |
||||
|
|
||||
|
@section scripts { |
||||
|
<abp-script-bundle> |
||||
|
<abp-script type="typeof(UppyScriptContributor)" /> |
||||
|
<abp-script src="/Pages/CmsKit/BlogPosts/create.js" /> |
||||
|
<abp-script type="typeof(SlugifyScriptContributor)" /> |
||||
|
</abp-script-bundle> |
||||
|
} |
||||
|
|
||||
|
@section content_toolbar { |
||||
|
@await Component.InvokeAsync(typeof(AbpPageToolbarViewComponent), new { pageName = typeof(CreateModel).FullName }) |
||||
|
} |
||||
|
|
||||
|
<abp-card> |
||||
|
<abp-card-header title="@L["New"].Value"></abp-card-header> |
||||
|
<abp-card-body> |
||||
|
<div class="form-group"> |
||||
|
<label>@L["CoverImage"]</label> |
||||
|
<input type="file" id="BlogPostCoverImage" class="form-control" /> |
||||
|
</div> |
||||
|
|
||||
|
<abp-dynamic-form abp-model="ViewModel" asp-page="/CmsKit/BlogPosts/Create" id="form-blog-post-create"> |
||||
|
<div class="form-group"> |
||||
|
<label asp-for="ViewModel.BlogId"></label> |
||||
|
<select asp-for="ViewModel.BlogId" id="BlogSelectionSelect" class="form-control"></select> |
||||
|
<span asp-validation-for="ViewModel.BlogId"></span> |
||||
|
</div> |
||||
|
|
||||
|
<abp-input asp-for="ViewModel.Title" /> |
||||
|
|
||||
|
<abp-input asp-for="ViewModel.Slug" /> |
||||
|
|
||||
|
@await Component.InvokeAsync(typeof(EntityContentEditorViewComponent), new |
||||
|
{ |
||||
|
entityType = BlogPostConsts.EntityType, |
||||
|
displaySubmitButton = false |
||||
|
}) |
||||
|
|
||||
|
<abp-form-content /> |
||||
|
|
||||
|
</abp-dynamic-form> |
||||
|
|
||||
|
<div id="blog-post-tags-wrapper"> |
||||
|
|
||||
|
<hr /> |
||||
|
|
||||
|
@await Component.InvokeAsync(typeof(TagEditorViewComponent), new |
||||
|
{ |
||||
|
entityType = BlogPostConsts.EntityType, |
||||
|
displaySubmitButton = false |
||||
|
}) |
||||
|
</div> |
||||
|
|
||||
|
</abp-card-body> |
||||
|
<abp-card-footer> |
||||
|
<abp-button button-type="Primary" type="submit" text="@L["Submit"].Value" id="button-blog-post-create" /> |
||||
|
</abp-card-footer> |
||||
|
</abp-card> |
||||
@ -0,0 +1,63 @@ |
|||||
|
using AutoMapper; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.AspNetCore.Mvc.Rendering; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; |
||||
|
using Volo.Abp.Validation; |
||||
|
using Volo.CmsKit.Admin.Blogs; |
||||
|
using Volo.CmsKit.Blogs; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.BlogPosts |
||||
|
{ |
||||
|
public class CreateModel : CmsKitAdminPageModel |
||||
|
{ |
||||
|
protected IBlogPostAdminAppService BlogPostAdminAppService { get; } |
||||
|
|
||||
|
[BindProperty] |
||||
|
public CreateBlogPostViewModel ViewModel { get; set; } |
||||
|
|
||||
|
public CreateModel( |
||||
|
IBlogPostAdminAppService blogPostAdminAppService) |
||||
|
{ |
||||
|
BlogPostAdminAppService = blogPostAdminAppService; |
||||
|
} |
||||
|
|
||||
|
public async Task<IActionResult> OnPostAsync() |
||||
|
{ |
||||
|
var dto = ObjectMapper.Map<CreateBlogPostViewModel, CreateBlogPostDto>(ViewModel); |
||||
|
|
||||
|
var created = await BlogPostAdminAppService.CreateAsync(dto); |
||||
|
|
||||
|
return new OkObjectResult(created); |
||||
|
} |
||||
|
|
||||
|
[AutoMap(typeof(CreateBlogPostDto), ReverseMap = true)] |
||||
|
public class CreateBlogPostViewModel |
||||
|
{ |
||||
|
[Required] |
||||
|
[DynamicFormIgnore] |
||||
|
public Guid BlogId { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[DynamicMaxLength(typeof(BlogPostConsts), nameof(BlogPostConsts.MaxTitleLength))] |
||||
|
[DynamicFormIgnore] |
||||
|
public string Title { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[DynamicStringLength( |
||||
|
typeof(BlogPostConsts), |
||||
|
nameof(BlogPostConsts.MaxSlugLength), |
||||
|
nameof(BlogPostConsts.MinSlugLength))] |
||||
|
[DynamicFormIgnore] |
||||
|
public string Slug { get; set; } |
||||
|
|
||||
|
[DynamicMaxLength(typeof(BlogPostConsts), nameof(BlogPostConsts.MaxShortDescriptionLength))] |
||||
|
public string ShortDescription { get; set; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
@page |
||||
|
|
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.BlogPosts |
||||
|
@using Volo.CmsKit.Admin.Web.Pages |
||||
|
@using Volo.CmsKit.Admin.Web.Menus |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageToolbar |
||||
|
|
||||
|
@inherits CmsKitAdminPageBase |
||||
|
|
||||
|
@model IndexModel |
||||
|
|
||||
|
@{ |
||||
|
PageLayout.Content.Title = L["BlogPosts"].Value; |
||||
|
PageLayout.Content.BreadCrumb.Add(L["Menu:CMS"].Value); |
||||
|
PageLayout.Content.MenuItemName = CmsKitAdminMenus.BlogPosts.BlogPostsMenu; |
||||
|
} |
||||
|
|
||||
|
@section scripts { |
||||
|
<abp-script src="/Pages/CmsKit/BlogPosts/index.js" /> |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@section content_toolbar { |
||||
|
@await Component.InvokeAsync(typeof(AbpPageToolbarViewComponent), new { pageName = typeof(IndexModel).FullName }) |
||||
|
} |
||||
|
|
||||
|
<div id="CmsKitBlogPostsWrapper"> |
||||
|
<abp-card> |
||||
|
<abp-card-body> |
||||
|
<abp-table striped-rows="true" id="BlogPostsTable" class="nowrap"></abp-table> |
||||
|
</abp-card-body> |
||||
|
</abp-card> |
||||
|
</div> |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.BlogPosts |
||||
|
{ |
||||
|
public class IndexModel : CmsKitAdminPageModel |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,77 @@ |
|||||
|
@page "{Id}" |
||||
|
|
||||
|
@using Volo.CmsKit.Blogs |
||||
|
@using Volo.CmsKit.Admin.Web.Pages |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageToolbar |
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Contents.Components.EntityContentEditor |
||||
|
@using Volo.CmsKit.Admin.Web.Menus |
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.BlogPosts |
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Tags.Components.TagEditor |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Packages.Uppy |
||||
|
@using Volo.CmsKit.Admin.Web.Bundles |
||||
|
|
||||
|
@inherits CmsKitAdminPageBase |
||||
|
|
||||
|
@model UpdateModel |
||||
|
|
||||
|
@{ |
||||
|
PageLayout.Content.Title = L["BlogPosts"].Value; |
||||
|
PageLayout.Content.BreadCrumb.Add(L["Menu:CMS"].Value); |
||||
|
PageLayout.Content.MenuItemName = CmsKitAdminMenus.BlogPosts.BlogPostsMenu; |
||||
|
} |
||||
|
|
||||
|
@section scripts { |
||||
|
<abp-script-bundle> |
||||
|
<abp-script type="typeof(UppyScriptContributor)" /> |
||||
|
<abp-script src="/Pages/CmsKit/BlogPosts/update.js" /> |
||||
|
<abp-script type="typeof(SlugifyScriptContributor)" /> |
||||
|
</abp-script-bundle> |
||||
|
} |
||||
|
|
||||
|
@section content_toolbar { |
||||
|
@await Component.InvokeAsync(typeof(AbpPageToolbarViewComponent), new { pageName = typeof(UpdateModel).FullName }) |
||||
|
} |
||||
|
|
||||
|
<abp-card> |
||||
|
<abp-card-header title="@L["Update"].Value"></abp-card-header> |
||||
|
<abp-card-body> |
||||
|
|
||||
|
<div class="form-group"> |
||||
|
<img height="120" src="/api/cms-kit-admin/blogs/blog-posts/@Model.Id/cover-image" /> |
||||
|
<label>@L["CoverImage"]</label> |
||||
|
<input type="file" id="BlogPostCoverImage" class="form-control" /> |
||||
|
</div> |
||||
|
|
||||
|
<abp-dynamic-form abp-model="ViewModel" asp-page="/CmsKit/BlogPosts/Update" id="form-blog-post-update"> |
||||
|
<abp-input asp-for="Id" /> |
||||
|
|
||||
|
<abp-input asp-for="ViewModel.Title" /> |
||||
|
|
||||
|
<abp-input asp-for="ViewModel.Slug" /> |
||||
|
|
||||
|
@await Component.InvokeAsync(typeof(EntityContentEditorViewComponent), new |
||||
|
{ |
||||
|
entityType = BlogPostConsts.EntityType, |
||||
|
entityId = Model.Id.ToString(), |
||||
|
displaySubmitButton = false |
||||
|
}) |
||||
|
|
||||
|
<abp-form-content /> |
||||
|
</abp-dynamic-form> |
||||
|
|
||||
|
@if (Model.TagsFeature?.IsEnabled == true) |
||||
|
{ |
||||
|
<hr /> |
||||
|
|
||||
|
@await Component.InvokeAsync(typeof(TagEditorViewComponent), new |
||||
|
{ |
||||
|
entityType = BlogPostConsts.EntityType, |
||||
|
entityId = Model.Id.ToString(), |
||||
|
displaySubmitButton = false |
||||
|
}) |
||||
|
} |
||||
|
</abp-card-body> |
||||
|
<abp-card-footer> |
||||
|
<abp-button button-type="Primary" type="submit" text="@L["Submit"].Value" id="button-blog-post-update" /> |
||||
|
</abp-card-footer> |
||||
|
</abp-card> |
||||
@ -0,0 +1,77 @@ |
|||||
|
using AutoMapper; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.AspNetCore.Mvc.Rendering; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; |
||||
|
using Volo.Abp.Validation; |
||||
|
using Volo.CmsKit.Admin.Blogs; |
||||
|
using Volo.CmsKit.Blogs; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.BlogPosts |
||||
|
{ |
||||
|
public class UpdateModel : CmsKitAdminPageModel |
||||
|
{ |
||||
|
protected IBlogPostAdminAppService BlogPostAdminAppService { get; } |
||||
|
protected IBlogFeatureAppService BlogFeatureAppService { get; } |
||||
|
|
||||
|
[BindProperty] |
||||
|
public virtual UpdateBlogPostViewModel ViewModel { get; set; } |
||||
|
|
||||
|
[HiddenInput] |
||||
|
[BindProperty(SupportsGet = true)] |
||||
|
public virtual Guid Id { get; set; } |
||||
|
|
||||
|
public virtual BlogFeatureDto TagsFeature { get; protected set; } |
||||
|
|
||||
|
public UpdateModel( |
||||
|
IBlogPostAdminAppService blogPostAdminAppService, |
||||
|
IBlogFeatureAppService blogFeatureAppService) |
||||
|
{ |
||||
|
BlogPostAdminAppService = blogPostAdminAppService; |
||||
|
BlogFeatureAppService = blogFeatureAppService; |
||||
|
} |
||||
|
|
||||
|
public virtual async Task OnGetAsync() |
||||
|
{ |
||||
|
var blogPost = await BlogPostAdminAppService.GetAsync(Id); |
||||
|
|
||||
|
ViewModel = ObjectMapper.Map<BlogPostDto, UpdateBlogPostViewModel>(blogPost); |
||||
|
|
||||
|
TagsFeature = await BlogFeatureAppService.GetOrDefaultAsync(blogPost.BlogId, GlobalFeatures.TagsFeature.Name); |
||||
|
} |
||||
|
|
||||
|
public virtual async Task<IActionResult> OnPostAsync() |
||||
|
{ |
||||
|
var dto = ObjectMapper.Map<UpdateBlogPostViewModel, UpdateBlogPostDto>(ViewModel); |
||||
|
|
||||
|
await BlogPostAdminAppService.UpdateAsync(Id, dto); |
||||
|
|
||||
|
return NoContent(); |
||||
|
} |
||||
|
|
||||
|
[AutoMap(typeof(BlogPostDto))] |
||||
|
[AutoMap(typeof(UpdateBlogPostDto), ReverseMap = true)] |
||||
|
public class UpdateBlogPostViewModel |
||||
|
{ |
||||
|
[DynamicMaxLength(typeof(BlogPostConsts), nameof(BlogPostConsts.MaxTitleLength))] |
||||
|
[Required] |
||||
|
[DynamicFormIgnore] |
||||
|
public string Title { get; set; } |
||||
|
|
||||
|
[DynamicStringLength(typeof(BlogPostConsts), nameof(BlogPostConsts.MaxSlugLength), nameof(BlogPostConsts.MinSlugLength))] |
||||
|
[Required] |
||||
|
[DisplayOrder(10000)] |
||||
|
[DynamicFormIgnore] |
||||
|
public string Slug { get; set; } |
||||
|
|
||||
|
[DynamicMaxLength(typeof(BlogPostConsts), nameof(BlogPostConsts.MaxShortDescriptionLength))] |
||||
|
[DisplayOrder(10001)] |
||||
|
public string ShortDescription { get; set; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,212 @@ |
|||||
|
$(function () { |
||||
|
|
||||
|
var l = abp.localization.getResource("CmsKit"); |
||||
|
|
||||
|
var $selectBlog = $('#BlogSelectionSelect'); |
||||
|
var $formCreate = $('#form-blog-post-create'); |
||||
|
var $title = $('#ViewModel_Title'); |
||||
|
var $shortDescription = $('#ViewModel_ShortDescription'); |
||||
|
var $url = $('#ViewModel_Slug'); |
||||
|
var $buttonSubmit = $('#button-blog-post-create'); |
||||
|
var $pageContentInput = $('#ViewModel_Value'); |
||||
|
var $tagsInput = $('.tag-editor-form input[name=tags]'); |
||||
|
var $fileInput = $('#BlogPostCoverImage'); |
||||
|
var $tagsWrapper = $('#blog-post-tags-wrapper'); |
||||
|
|
||||
|
var UPPY_UPLOAD_ENDPOINT = "/api/cms-kit-admin/blogs/blog-posts/{0}/cover-image"; |
||||
|
var UPPY_FILE_ID = "uppy-upload-file"; |
||||
|
|
||||
|
var isTagsEnabled = true; |
||||
|
|
||||
|
$formCreate.data('validator').settings.ignore = ":hidden, [contenteditable='true']:not([name]), .tui-popup-wrapper"; |
||||
|
|
||||
|
function initSelectBlog() { |
||||
|
$selectBlog.data('autocompleteApiUrl', '/api/cms-kit-admin/blogs'); |
||||
|
$selectBlog.data('autocompleteDisplayProperty', 'name'); |
||||
|
$selectBlog.data('autocompleteValueProperty', 'id'); |
||||
|
$selectBlog.data('autocompleteItemsProperty', 'items'); |
||||
|
$selectBlog.data('autocompleteFilterParamName', 'filter'); |
||||
|
|
||||
|
abp.dom.initializers.initializeAutocompleteSelects($selectBlog); |
||||
|
} |
||||
|
|
||||
|
initSelectBlog(); |
||||
|
|
||||
|
$formCreate.on('submit', function (e) { |
||||
|
e.preventDefault(); |
||||
|
|
||||
|
if ($formCreate.valid()) { |
||||
|
|
||||
|
abp.ui.setBusy(); |
||||
|
|
||||
|
$formCreate.ajaxSubmit({ |
||||
|
success: function (result) { |
||||
|
submitEntityContent(result.id); |
||||
|
}, |
||||
|
error: function (result) { |
||||
|
abp.notify.error(result.responseJSON.error.message); |
||||
|
abp.ui.clearBusy(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
$buttonSubmit.click(function (e) { |
||||
|
e.preventDefault(); |
||||
|
$formCreate.submit(); |
||||
|
}); |
||||
|
|
||||
|
function submitEntityContent(blogPostId) { |
||||
|
volo.cmsKit.admin.contents.contentAdmin |
||||
|
.create( |
||||
|
{ |
||||
|
entityType: 'BlogPost', |
||||
|
entityId: blogPostId, |
||||
|
value: $pageContentInput.val() |
||||
|
}) |
||||
|
.then(function (result) { |
||||
|
if (isTagsEnabled) { |
||||
|
submitEntityTags(blogPostId) |
||||
|
} |
||||
|
else { |
||||
|
submitCoverImage(blogPostId); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function submitEntityTags(blogPostId) { |
||||
|
|
||||
|
var tags = $tagsInput.val().split(","); |
||||
|
|
||||
|
volo.cmsKit.admin.tags.entityTagAdmin |
||||
|
.setEntityTags({ |
||||
|
entityType: 'BlogPost', |
||||
|
entityId: blogPostId, |
||||
|
tags: tags |
||||
|
}) |
||||
|
.then(function (result) { |
||||
|
submitCoverImage(blogPostId); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function getUppyHeaders() { |
||||
|
var headers = {}; |
||||
|
headers[abp.security.antiForgery.tokenHeaderName] = abp.security.antiForgery.getToken(); |
||||
|
|
||||
|
return headers; |
||||
|
} |
||||
|
|
||||
|
function submitCoverImage(blogPostId) { |
||||
|
var UPPY_OPTIONS = { |
||||
|
endpoint: UPPY_UPLOAD_ENDPOINT.replace("{0}", blogPostId), |
||||
|
formData: true, |
||||
|
fieldName: "file", |
||||
|
method: "post", |
||||
|
headers: getUppyHeaders() |
||||
|
}; |
||||
|
|
||||
|
var UPPY = Uppy.Core().use(Uppy.XHRUpload, UPPY_OPTIONS); |
||||
|
|
||||
|
UPPY.reset(); |
||||
|
|
||||
|
var file = $fileInput[0].files[0]; |
||||
|
|
||||
|
if (file) { |
||||
|
|
||||
|
UPPY.addFile({ |
||||
|
id: UPPY_FILE_ID, |
||||
|
name: file.name, // file name
|
||||
|
type: file.type, // file type
|
||||
|
data: file, // file
|
||||
|
}); |
||||
|
|
||||
|
UPPY.upload().then((result) => { |
||||
|
if (result.failed.length > 0) { |
||||
|
abp.message.error(l("UploadFailedMessage")); |
||||
|
} else { |
||||
|
finishSaving(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
else { |
||||
|
finishSaving(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function finishSaving() { |
||||
|
abp.notify.success(l('SuccessfullySaved')); |
||||
|
abp.ui.clearBusy(); |
||||
|
location.href = "../BlogPosts"; |
||||
|
} |
||||
|
|
||||
|
var urlEdited = false; |
||||
|
var reflectedChange = false; |
||||
|
|
||||
|
$url.on('change', function () { |
||||
|
reflectUrlChanges(); |
||||
|
}); |
||||
|
|
||||
|
$title.on('keyup paste', function () { |
||||
|
reflectUrlChanges(); |
||||
|
}); |
||||
|
|
||||
|
function reflectUrlChanges() { |
||||
|
var title = $title.val().toLocaleLowerCase(); |
||||
|
|
||||
|
if (urlEdited) { |
||||
|
title = $url.val(); |
||||
|
} |
||||
|
|
||||
|
var slugified = slugify(title, { |
||||
|
lower: true |
||||
|
}); |
||||
|
|
||||
|
if (slugified != $url.val()) { |
||||
|
reflectedChange = true; |
||||
|
|
||||
|
$url.val(slugified); |
||||
|
|
||||
|
reflectedChange = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$url.change(function () { |
||||
|
if (!reflectedChange) { |
||||
|
urlEdited = true; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
var shorDescriptionEdited = false; |
||||
|
$pageContentInput.on('change', function () { |
||||
|
if (shorDescriptionEdited) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var htmlValue = $pageContentInput.val(); |
||||
|
|
||||
|
var plainValue = jQuery('<div>').html(htmlValue).text().replace(/\n/g, ' ').substring(0, 120); |
||||
|
|
||||
|
$shortDescription.val(plainValue); |
||||
|
}); |
||||
|
|
||||
|
$shortDescription.on('change', function () { |
||||
|
shorDescriptionEdited = true; |
||||
|
}); |
||||
|
|
||||
|
$selectBlog.on('change', function () { |
||||
|
var blogId = $selectBlog.val(); |
||||
|
volo.cmsKit.blogs.blogFeature |
||||
|
.getOrDefault(blogId, 'CmsKit.Tags') |
||||
|
.then(function (result) { |
||||
|
if (result) { |
||||
|
isTagsEnabled = result.isEnabled == true |
||||
|
if (isTagsEnabled === true) { |
||||
|
$tagsWrapper.removeClass('d-none'); |
||||
|
} |
||||
|
else { |
||||
|
$tagsWrapper.addClass('d-none'); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,68 @@ |
|||||
|
|
||||
|
$(function () { |
||||
|
var l = abp.localization.getResource("CmsKit"); |
||||
|
|
||||
|
var blogsService = volo.cmsKit.admin.blogs.blogPostAdmin; |
||||
|
|
||||
|
var $blogPostWrapper = $('#CmsKitBlogPostsWrapper'); |
||||
|
|
||||
|
var dataTable = $("#BlogPostsTable").DataTable(abp.libs.datatables.normalizeConfiguration({ |
||||
|
processing: true, |
||||
|
serverSide: true, |
||||
|
paging: true, |
||||
|
searching: false, |
||||
|
autoWidth: false, |
||||
|
scrollCollapse: true, |
||||
|
scrollX: true, |
||||
|
ordering: true, |
||||
|
order: [[1, "desc"]], |
||||
|
ajax: abp.libs.datatables.createAjax(blogsService.getList), |
||||
|
columnDefs: [ |
||||
|
{ |
||||
|
title: l("Details"), |
||||
|
targets: 0, |
||||
|
rowAction: { |
||||
|
items: [ |
||||
|
{ |
||||
|
text: l('Edit'), |
||||
|
visible: abp.auth.isGranted('CmsKit.Blogs.Update'), |
||||
|
action: function (data) { |
||||
|
location.href = "/CmsKit/BlogPosts/Update/" + data.record.id |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
text: l('Delete'), |
||||
|
visible: abp.auth.isGranted('CmsKit.Blogs.Delete'), |
||||
|
confirmMessage: function (data) { |
||||
|
return l("BlogPostDeletionConfirmationMessage", data.record.title) |
||||
|
}, |
||||
|
action: function (data) { |
||||
|
blogsService |
||||
|
.delete(data.record.id) |
||||
|
.then(function () { |
||||
|
abp.notify.info(l("SuccessfullyDeleted")); |
||||
|
dataTable.ajax.reload(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
title: l("Title"), |
||||
|
orderable: true, |
||||
|
data: "title" |
||||
|
}, |
||||
|
{ |
||||
|
title: l("Slug"), |
||||
|
orderable: true, |
||||
|
data: "slug" |
||||
|
} |
||||
|
] |
||||
|
})); |
||||
|
|
||||
|
$('#AbpContentToolbar button[name=CreateBlogPost]').on('click', function (e) { |
||||
|
e.preventDefault(); |
||||
|
window.location.href = "/CmsKit/BlogPosts/Create" |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,180 @@ |
|||||
|
$(function () { |
||||
|
|
||||
|
var l = abp.localization.getResource("CmsKit"); |
||||
|
|
||||
|
var $selectBlog = $('#BlogSelectionSelect'); |
||||
|
var $formUpdate = $('#form-blog-post-update'); |
||||
|
var $title = $('#ViewModel_Title'); |
||||
|
var $titleClone = $('#title-clone'); |
||||
|
var $slug = $('#ViewModel_Slug'); |
||||
|
var $buttonSubmit = $('#button-blog-post-update'); |
||||
|
var $contentValueInput = $('#ViewModel_Value'); |
||||
|
var $blogPostIdInput = $('#Id'); |
||||
|
var $contentIdInput = $('#ViewModel_Id'); |
||||
|
var $tagsInput = $('.tag-editor-form input[name=tags]'); |
||||
|
var $fileInput = $('#BlogPostCoverImage'); |
||||
|
|
||||
|
var UPPY_UPLOAD_ENDPOINT = "/api/cms-kit-admin/blogs/blog-posts/{0}/cover-image"; |
||||
|
var UPPY_FILE_ID = "uppy-upload-file"; |
||||
|
|
||||
|
$formUpdate.data('validator').settings.ignore = ":hidden, [contenteditable='true']:not([name]), .tui-popup-wrapper"; |
||||
|
|
||||
|
function initSelectBlog() { |
||||
|
$selectBlog.data('autocompleteApiUrl', '/api/cms-kit-admin/blogs/blogs'); |
||||
|
$selectBlog.data('autocompleteDisplayProperty', 'name'); |
||||
|
$selectBlog.data('autocompleteValueProperty', 'id'); |
||||
|
$selectBlog.data('autocompleteItemsProperty', 'items'); |
||||
|
$selectBlog.data('autocompleteFilterParamName', 'filter'); |
||||
|
|
||||
|
abp.dom.initializers.initializeAutocompleteSelects($selectBlog); |
||||
|
} |
||||
|
|
||||
|
initSelectBlog(); |
||||
|
|
||||
|
$formUpdate.on('submit', function (e) { |
||||
|
e.preventDefault(); |
||||
|
|
||||
|
if ($formUpdate.valid()) { |
||||
|
|
||||
|
abp.ui.setBusy(); |
||||
|
|
||||
|
$formUpdate.ajaxSubmit({ |
||||
|
success: function (result) { |
||||
|
submitEntityContent(); |
||||
|
}, |
||||
|
error: function (result) { |
||||
|
abp.ui.clearBusy(); abp.notify.error(result.responseJSON.error.message); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
$buttonSubmit.click(function (e) { |
||||
|
e.preventDefault(); |
||||
|
$formUpdate.submit(); |
||||
|
}); |
||||
|
|
||||
|
function submitEntityContent() { |
||||
|
|
||||
|
var contentId = $contentIdInput.val(); |
||||
|
var blogPostId = $blogPostIdInput.val(); |
||||
|
var contentValue = $contentValueInput.val(); |
||||
|
|
||||
|
if (contentId) { |
||||
|
volo.cmsKit.admin.contents.contentAdmin |
||||
|
.update(contentId, |
||||
|
{ |
||||
|
value: contentValue |
||||
|
}) |
||||
|
.then(function (result) { |
||||
|
entityContentCallback(blogPostId); |
||||
|
}); |
||||
|
} |
||||
|
else { |
||||
|
volo.cmsKit.admin.contents.contentAdmin |
||||
|
.create({ |
||||
|
entityType: 'BlogPost', |
||||
|
entityId: blogPostId, |
||||
|
value: contentValue |
||||
|
}) |
||||
|
.then(function (result) { |
||||
|
entityContentCallback(blogPostId); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function entityContentCallback(blogPostId) { |
||||
|
if ($tagsInput.val()) { |
||||
|
submitEntityTags(blogPostId); |
||||
|
} |
||||
|
else { |
||||
|
submitCoverImage(blogPostId); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function submitEntityTags(blogPostId) { |
||||
|
|
||||
|
var tags = $tagsInput.val().split(","); |
||||
|
|
||||
|
volo.cmsKit.admin.tags.entityTagAdmin |
||||
|
.setEntityTags({ |
||||
|
entityType: 'BlogPost', |
||||
|
entityId: blogPostId, |
||||
|
tags: tags |
||||
|
}) |
||||
|
.then(function (result) { |
||||
|
submitCoverImage(blogPostId); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function getUppyHeaders() { |
||||
|
var headers = {}; |
||||
|
headers[abp.security.antiForgery.tokenHeaderName] = abp.security.antiForgery.getToken(); |
||||
|
|
||||
|
return headers; |
||||
|
} |
||||
|
|
||||
|
function submitCoverImage(blogPostId) { |
||||
|
var UPPY_OPTIONS = { |
||||
|
endpoint: UPPY_UPLOAD_ENDPOINT.replace("{0}", blogPostId), |
||||
|
formData: true, |
||||
|
fieldName: "file", |
||||
|
method: "post", |
||||
|
headers: getUppyHeaders() |
||||
|
}; |
||||
|
|
||||
|
var UPPY = Uppy.Core().use(Uppy.XHRUpload, UPPY_OPTIONS); |
||||
|
|
||||
|
UPPY.reset(); |
||||
|
|
||||
|
var file = $fileInput[0].files[0]; |
||||
|
|
||||
|
if (file) { |
||||
|
|
||||
|
UPPY.addFile({ |
||||
|
id: UPPY_FILE_ID, |
||||
|
name: file.name, // file name
|
||||
|
type: file.type, // file type
|
||||
|
data: file, // file
|
||||
|
}); |
||||
|
|
||||
|
UPPY.upload().then((result) => { |
||||
|
if (result.failed.length > 0) { |
||||
|
abp.message.error(l("UploadFailedMessage")); |
||||
|
} else { |
||||
|
finishSaving(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
else { |
||||
|
finishSaving(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function finishSaving(result) { |
||||
|
abp.notify.success(l('SuccessfullySaved')); |
||||
|
abp.ui.clearBusy(); |
||||
|
location.href = "../BlogPosts/"; |
||||
|
} |
||||
|
|
||||
|
$titleClone.on('change paste keyup', function () { |
||||
|
$title.val($titleClone.val()); |
||||
|
}); |
||||
|
|
||||
|
$slug.on('change', function () { |
||||
|
reflectUrlChanges(); |
||||
|
}); |
||||
|
|
||||
|
function reflectUrlChanges() { |
||||
|
|
||||
|
var title = $slug.val(); |
||||
|
|
||||
|
var slugified = slugify(title); |
||||
|
|
||||
|
if (slugified != title) { |
||||
|
$slug.val(slugified, { |
||||
|
lower: true |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
@ -0,0 +1,23 @@ |
|||||
|
@page |
||||
|
|
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Blogs |
||||
|
@using Volo.CmsKit.Admin.Web.Pages |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal |
||||
|
|
||||
|
@inherits CmsKitAdminPageBase |
||||
|
|
||||
|
@model CreateModalModel |
||||
|
|
||||
|
@{ |
||||
|
Layout = null; |
||||
|
} |
||||
|
|
||||
|
<abp-dynamic-form abp-model="ViewModel" asp-page="/CmsKit/Blogs/CreateModal"> |
||||
|
<abp-modal> |
||||
|
<abp-modal-header title="@L["New"].Value"></abp-modal-header> |
||||
|
<abp-modal-body> |
||||
|
<abp-form-content /> |
||||
|
</abp-modal-body> |
||||
|
<abp-modal-footer buttons="@(AbpModalButtons.Cancel | AbpModalButtons.Save)"></abp-modal-footer> |
||||
|
</abp-modal> |
||||
|
</abp-dynamic-form> |
||||
@ -0,0 +1,44 @@ |
|||||
|
using AutoMapper; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Validation; |
||||
|
using Volo.CmsKit.Admin.Blogs; |
||||
|
using Volo.CmsKit.Blogs; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.Blogs |
||||
|
{ |
||||
|
public class CreateModalModel : CmsKitAdminPageModel |
||||
|
{ |
||||
|
protected IBlogAdminAppService BlogAdminAppService { get; } |
||||
|
|
||||
|
[BindProperty] |
||||
|
public CreateBlogViewModel ViewModel { get; set; } |
||||
|
|
||||
|
public CreateModalModel(IBlogAdminAppService blogAdminAppService) |
||||
|
{ |
||||
|
BlogAdminAppService = blogAdminAppService; |
||||
|
} |
||||
|
|
||||
|
public async Task<IActionResult> OnPostAsync() |
||||
|
{ |
||||
|
var dto = ObjectMapper.Map<CreateBlogViewModel, CreateBlogDto>(ViewModel); |
||||
|
|
||||
|
await BlogAdminAppService.CreateAsync(dto); |
||||
|
|
||||
|
return NoContent(); |
||||
|
} |
||||
|
|
||||
|
[AutoMap(typeof(CreateBlogDto), ReverseMap = true)] |
||||
|
public class CreateBlogViewModel |
||||
|
{ |
||||
|
[Required] |
||||
|
[DynamicMaxLength(typeof(BlogConsts), nameof(BlogConsts.MaxNameLength))] |
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
[DynamicMaxLength(typeof(BlogConsts), nameof(BlogConsts.MaxSlugLength))] |
||||
|
[Required] |
||||
|
public string Slug { get; set; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,44 @@ |
|||||
|
@page |
||||
|
|
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Blogs |
||||
|
@using Volo.CmsKit.Admin.Web.Pages |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal |
||||
|
|
||||
|
@inherits CmsKitAdminPageBase |
||||
|
|
||||
|
@model FeaturesModalModel |
||||
|
|
||||
|
@{ |
||||
|
Layout = null; |
||||
|
} |
||||
|
|
||||
|
<form action="/CmsKit/Blogs/FeaturesModal"> |
||||
|
<abp-modal> |
||||
|
<abp-modal-header title="@L["Features"].Value"></abp-modal-header> |
||||
|
<abp-modal-body> |
||||
|
<abp-input asp-for="BlogId"></abp-input> |
||||
|
@for (int i = 0; i < Model.Items.Count; i++) |
||||
|
{ |
||||
|
<div class="form-group"> |
||||
|
|
||||
|
@if (Model.Items[i].IsAvailable) |
||||
|
{ |
||||
|
<abp-input asp-for="Items[i].IsEnabled" |
||||
|
label="@L[Model.Items[i].FeatureName]" /> |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
<div data-toggle="tooltip" title="@L["BlogFeatureNotAvailable"]"> |
||||
|
<abp-input asp-for="Items[i].IsEnabled" |
||||
|
label="@L[Model.Items[i].FeatureName]" |
||||
|
disabled="true" /> |
||||
|
</div> |
||||
|
} |
||||
|
<abp-input asp-for="Items[i].FeatureName" /> |
||||
|
</div> |
||||
|
} |
||||
|
|
||||
|
</abp-modal-body> |
||||
|
<abp-modal-footer buttons="@(AbpModalButtons.Close | AbpModalButtons.Save)"></abp-modal-footer> |
||||
|
</abp-modal> |
||||
|
</form> |
||||
@ -0,0 +1,77 @@ |
|||||
|
using AutoMapper; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.GlobalFeatures; |
||||
|
using Volo.CmsKit.Admin.Blogs; |
||||
|
using Volo.CmsKit.Admin.Web.Pages; |
||||
|
using Volo.CmsKit.Blogs; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.Blogs |
||||
|
{ |
||||
|
public class FeaturesModalModel : CmsKitAdminPageModel |
||||
|
{ |
||||
|
[BindProperty(SupportsGet = true)] |
||||
|
[HiddenInput] |
||||
|
public Guid BlogId { get; set; } |
||||
|
|
||||
|
[BindProperty] |
||||
|
public List<BlogFeatureViewModel> Items { get; set; } |
||||
|
|
||||
|
protected IBlogFeatureAdminAppService BlogFeatureAdminAppService { get; } |
||||
|
|
||||
|
public FeaturesModalModel(IBlogFeatureAdminAppService blogFeatureAdminAppService) |
||||
|
{ |
||||
|
BlogFeatureAdminAppService = blogFeatureAdminAppService; |
||||
|
} |
||||
|
|
||||
|
public async Task OnGetAsync() |
||||
|
{ |
||||
|
var blogFeatureDtos = await BlogFeatureAdminAppService.GetListAsync(BlogId); |
||||
|
|
||||
|
Items = ObjectMapper.Map<List<BlogFeatureDto>, List<BlogFeatureViewModel>>(blogFeatureDtos); |
||||
|
} |
||||
|
|
||||
|
public async Task<IActionResult> OnPostAsync() |
||||
|
{ |
||||
|
var dtos = ObjectMapper.Map<List<BlogFeatureViewModel>, List<BlogFeatureInputDto>>(Items); |
||||
|
|
||||
|
foreach (var item in dtos) |
||||
|
{ |
||||
|
await BlogFeatureAdminAppService.SetAsync(BlogId, item); |
||||
|
} |
||||
|
|
||||
|
return NoContent(); |
||||
|
} |
||||
|
|
||||
|
[AutoMap(typeof(BlogFeatureDto), ReverseMap = true)] |
||||
|
[AutoMap(typeof(BlogFeatureInputDto), ReverseMap = true)] |
||||
|
public class BlogFeatureViewModel |
||||
|
{ |
||||
|
private string featureName; |
||||
|
[HiddenInput] |
||||
|
public string FeatureName { get => featureName; set => SetFeatureName(value); } |
||||
|
|
||||
|
public bool IsEnabled { get; set; } |
||||
|
|
||||
|
public bool IsAvailable { get; private set; } |
||||
|
|
||||
|
private void SetFeatureName(string value) |
||||
|
{ |
||||
|
featureName = value; |
||||
|
|
||||
|
IsAvailable = GlobalFeatureManager.Instance.Modules.CmsKit().GetFeatures().Any(a => a.FeatureName == FeatureName) ? |
||||
|
GlobalFeatureManager.Instance.IsEnabled(FeatureName) : |
||||
|
true; |
||||
|
|
||||
|
if (!IsAvailable) |
||||
|
{ |
||||
|
IsEnabled = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
@page |
||||
|
|
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Blogs |
||||
|
@using Volo.CmsKit.Admin.Web.Pages |
||||
|
@using Volo.CmsKit.Admin.Web.Menus |
||||
|
@using Volo.CmsKit.Admin.Web.Bundles |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageToolbar |
||||
|
|
||||
|
@inherits CmsKitAdminPageBase |
||||
|
|
||||
|
@model IndexModel |
||||
|
|
||||
|
@{ |
||||
|
PageLayout.Content.Title = L["Blogs"].Value; |
||||
|
PageLayout.Content.BreadCrumb.Add(L["Menu:CMS"].Value); |
||||
|
PageLayout.Content.MenuItemName = CmsKitAdminMenus.Blogs.BlogsMenu; |
||||
|
} |
||||
|
|
||||
|
@section scripts { |
||||
|
<abp-script-bundle> |
||||
|
<abp-script src="/Pages/CmsKit/Blogs/index.js" /> |
||||
|
<abp-script src="/Pages/CmsKit/Blogs/createModal.js" /> |
||||
|
<abp-script src="/Pages/CmsKit/Blogs/updateModal.js" /> |
||||
|
<abp-script type="typeof(SlugifyScriptContributor)"/> |
||||
|
</abp-script-bundle> |
||||
|
} |
||||
|
|
||||
|
@section content_toolbar { |
||||
|
@await Component.InvokeAsync(typeof(AbpPageToolbarViewComponent), new { pageName = typeof(IndexModel).FullName }) |
||||
|
} |
||||
|
|
||||
|
<div id="CmsKitBlogsWrapper"> |
||||
|
<abp-card> |
||||
|
<abp-card-body> |
||||
|
<abp-table striped-rows="true" id="BlogsTable" class="nowrap"></abp-table> |
||||
|
</abp-card-body> |
||||
|
</abp-card> |
||||
|
</div> |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.Blogs |
||||
|
{ |
||||
|
public class IndexModel : CmsKitAdminPageModel |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
@page |
||||
|
@using Volo.CmsKit.Admin.Web.Pages |
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Blogs |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal |
||||
|
|
||||
|
@inherits CmsKitAdminPageBase |
||||
|
|
||||
|
@model UpdateModalModel |
||||
|
|
||||
|
@{ |
||||
|
Layout = null; |
||||
|
} |
||||
|
|
||||
|
<abp-dynamic-form abp-model="ViewModel" asp-page="/CmsKit/Blogs/UpdateModal"> |
||||
|
<abp-modal> |
||||
|
<abp-modal-header title="@L["Update"].Value"></abp-modal-header> |
||||
|
<abp-modal-body> |
||||
|
<abp-input asp-for="Id" /> |
||||
|
<abp-form-content /> |
||||
|
</abp-modal-body> |
||||
|
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer> |
||||
|
</abp-modal> |
||||
|
</abp-dynamic-form> |
||||
@ -0,0 +1,61 @@ |
|||||
|
using AutoMapper; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Linq; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Validation; |
||||
|
using Volo.CmsKit.Admin.Blogs; |
||||
|
using Volo.CmsKit.Blogs; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.Blogs |
||||
|
{ |
||||
|
public class UpdateModalModel : CmsKitAdminPageModel |
||||
|
{ |
||||
|
protected IBlogAdminAppService BlogAdminAppService { get; } |
||||
|
|
||||
|
[HiddenInput] |
||||
|
[BindProperty(SupportsGet = true)] |
||||
|
public Guid Id { get; set; } |
||||
|
|
||||
|
[BindProperty] |
||||
|
public UpdateBlogViewModel ViewModel { get; set; } |
||||
|
|
||||
|
public UpdateModalModel(IBlogAdminAppService blogAdminAppService) |
||||
|
{ |
||||
|
BlogAdminAppService = blogAdminAppService; |
||||
|
} |
||||
|
|
||||
|
public async Task OnGetAsync() |
||||
|
{ |
||||
|
var blog = await BlogAdminAppService.GetAsync(Id); |
||||
|
|
||||
|
ViewModel = ObjectMapper.Map<BlogDto, UpdateBlogViewModel>(blog); |
||||
|
} |
||||
|
|
||||
|
public async Task<IActionResult> OnPostAsync() |
||||
|
{ |
||||
|
var dto = ObjectMapper.Map<UpdateBlogViewModel, UpdateBlogDto>(ViewModel); |
||||
|
|
||||
|
await BlogAdminAppService.UpdateAsync(Id, dto); |
||||
|
|
||||
|
return NoContent(); |
||||
|
} |
||||
|
|
||||
|
[AutoMap(typeof(BlogDto))] |
||||
|
[AutoMap(typeof(UpdateBlogDto), ReverseMap = true)] |
||||
|
public class UpdateBlogViewModel |
||||
|
{ |
||||
|
[Required] |
||||
|
[DynamicMaxLength(typeof(BlogConsts), nameof(BlogConsts.MaxNameLength))] |
||||
|
public string Name { get; set; } |
||||
|
|
||||
|
[DynamicMaxLength(typeof(BlogConsts), nameof(BlogConsts.MaxSlugLength))] |
||||
|
[Required] |
||||
|
public string Slug { get; set; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
var abp = abp || {}; |
||||
|
$(function () { |
||||
|
abp.modals.createBlog = function () { |
||||
|
var initModal = function (publicApi, args) { |
||||
|
|
||||
|
var $name = $('#ViewModel_Name'); |
||||
|
var $slug = $('#ViewModel_Slug'); |
||||
|
|
||||
|
var urlEdited = false; |
||||
|
|
||||
|
$name.on('change paste keyup', function () { |
||||
|
reflectUrlChanges(); |
||||
|
}); |
||||
|
|
||||
|
$slug.on('change', function () { |
||||
|
reflectUrlChanges(); |
||||
|
}); |
||||
|
|
||||
|
function reflectUrlChanges() { |
||||
|
var title = $name.val(); |
||||
|
|
||||
|
if (urlEdited) { |
||||
|
title = $slug.val(); |
||||
|
} |
||||
|
|
||||
|
var slugified = slugify(title, { |
||||
|
lower: true |
||||
|
}); |
||||
|
|
||||
|
if (slugified != $slug.val()) { |
||||
|
reflectedChange = true; |
||||
|
|
||||
|
$slug.val(slugified); |
||||
|
|
||||
|
reflectedChange = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
$slug.change(function () { |
||||
|
urlEdited = true; |
||||
|
}); |
||||
|
} |
||||
|
return { |
||||
|
initModal: initModal |
||||
|
}; |
||||
|
}; |
||||
|
}); |
||||
@ -0,0 +1,85 @@ |
|||||
|
|
||||
|
$(function () { |
||||
|
var l = abp.localization.getResource("CmsKit"); |
||||
|
|
||||
|
var createModal = new abp.ModalManager({ viewUrl: abp.appPath + "CmsKit/Blogs/CreateModal", modalClass: 'createBlog' }); |
||||
|
var updateModal = new abp.ModalManager({ viewUrl: abp.appPath + "CmsKit/Blogs/UpdateModal", modalClass: 'updateBlog' }); |
||||
|
var featuresModal = new abp.ModalManager(abp.appPath + "CmsKit/Blogs/FeaturesModal"); |
||||
|
|
||||
|
var blogsService = volo.cmsKit.admin.blogs.blogAdmin; |
||||
|
|
||||
|
var dataTable = $("#BlogsTable").DataTable(abp.libs.datatables.normalizeConfiguration({ |
||||
|
processing: true, |
||||
|
serverSide: true, |
||||
|
paging: true, |
||||
|
searching: false, |
||||
|
autoWidth: false, |
||||
|
scrollCollapse: true, |
||||
|
scrollX: true, |
||||
|
ordering: true, |
||||
|
order: [[1, "desc"]], |
||||
|
ajax: abp.libs.datatables.createAjax(blogsService.getList), |
||||
|
columnDefs: [ |
||||
|
{ |
||||
|
title: l("Details"), |
||||
|
targets: 0, |
||||
|
rowAction: { |
||||
|
items: [ |
||||
|
{ |
||||
|
text: l('Features'), |
||||
|
visible: abp.auth.isGranted('CmsKit.Blogs.Features'), |
||||
|
action: function (data) { |
||||
|
featuresModal.open({ blogId: data.record.id }); |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
text: l('Edit'), |
||||
|
visible: abp.auth.isGranted('CmsKit.Blogs.Update'), |
||||
|
action: function (data) { |
||||
|
updateModal.open({ id: data.record.id }); |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
text: l('Delete'), |
||||
|
visible: abp.auth.isGranted('CmsKit.Blogs.Delete'), |
||||
|
confirmMessage: function (data) { |
||||
|
return l("BlogDeletionConfirmationMessage", data.record.name) |
||||
|
}, |
||||
|
action: function (data) { |
||||
|
blogsService |
||||
|
.delete(data.record.id) |
||||
|
.then(function () { |
||||
|
abp.notify.info(l("SuccessfullyDeleted")); |
||||
|
dataTable.ajax.reload(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
title: l("Name"), |
||||
|
orderable: true, |
||||
|
data: "name" |
||||
|
}, |
||||
|
{ |
||||
|
title: l("Slug"), |
||||
|
orderable: true, |
||||
|
data: "slug" |
||||
|
} |
||||
|
] |
||||
|
})); |
||||
|
|
||||
|
$('#AbpContentToolbar button[name=CreateBlog]').on('click', function (e) { |
||||
|
e.preventDefault(); |
||||
|
createModal.open(); |
||||
|
}); |
||||
|
|
||||
|
createModal.onResult(function () { |
||||
|
dataTable.ajax.reload(); |
||||
|
}); |
||||
|
|
||||
|
updateModal.onResult(function () { |
||||
|
dataTable.ajax.reload(); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,30 @@ |
|||||
|
var abp = abp || {}; |
||||
|
$(function () { |
||||
|
abp.modals.updateBlog = function () { |
||||
|
var initModal = function (publicApi, args) { |
||||
|
debugger; |
||||
|
var $slug = $('#ViewModel_Slug'); |
||||
|
|
||||
|
$slug.on('change', function () { |
||||
|
reflectUrlChanges(); |
||||
|
}); |
||||
|
|
||||
|
function reflectUrlChanges() { |
||||
|
|
||||
|
var title = $slug.val(); |
||||
|
|
||||
|
var slugified = slugify(title); |
||||
|
|
||||
|
if (slugified != title) { |
||||
|
|
||||
|
$slug.val(slugified, { |
||||
|
lower: true |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return { |
||||
|
initModal: initModal |
||||
|
}; |
||||
|
}; |
||||
|
}); |
||||
@ -0,0 +1,108 @@ |
|||||
|
@page "{id}" |
||||
|
|
||||
|
@using System.Globalization |
||||
|
@using Microsoft.AspNetCore.Mvc.Localization |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Layout |
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Comments |
||||
|
@using Volo.CmsKit.Admin.Web.Menus |
||||
|
@using Volo.CmsKit.Localization |
||||
|
|
||||
|
@inject IPageLayout PageLayout |
||||
|
@inject IHtmlLocalizer<CmsKitResource> L |
||||
|
|
||||
|
@model DetailsModel |
||||
|
|
||||
|
@{ |
||||
|
PageLayout.Content.Title = L["Details"].Value; |
||||
|
PageLayout.Content.BreadCrumb.Add(L["Menu:CMS"].Value); |
||||
|
PageLayout.Content.BreadCrumb.Add(L["Comments"].Value); |
||||
|
PageLayout.Content.MenuItemName = CmsKitAdminMenus.Comments.CommentsMenu; |
||||
|
} |
||||
|
|
||||
|
@section styles{ |
||||
|
<abp-style-bundle> |
||||
|
<abp-style src="/Pages/CmsKit/Comments/details.css" /> |
||||
|
</abp-style-bundle> |
||||
|
} |
||||
|
|
||||
|
@section scripts { |
||||
|
<abp-script-bundle> |
||||
|
<abp-script src="/Pages/CmsKit/Comments/details.js" /> |
||||
|
</abp-script-bundle> |
||||
|
} |
||||
|
|
||||
|
<div id="CmsKitCommentsWrapper"> |
||||
|
|
||||
|
<abp-card> |
||||
|
<abp-card-body> |
||||
|
<abp-row> |
||||
|
<abp-column> |
||||
|
<abp-table> |
||||
|
<tr> |
||||
|
<td width="10%"><b>@L["EntityType"]</b>:</td> |
||||
|
<td>@Model.CommentWithAuthorDto.EntityType</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td><b>@L["EntityId"]</b>:</td> |
||||
|
<td>@Model.CommentWithAuthorDto.EntityId</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td><b>@L["CreationTime"]</b>:</td> |
||||
|
<td>@Model.CommentWithAuthorDto.CreationTime.ToString(CultureInfo.CurrentUICulture)</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td><b>@L["Username"]</b>:</td> |
||||
|
<td>@Model.CommentWithAuthorDto.Author.Name</td> |
||||
|
</tr> |
||||
|
@if (Model.CommentWithAuthorDto.RepliedCommentId.HasValue) |
||||
|
{ |
||||
|
<tr> |
||||
|
<td><b>@L["ReplyTo"]</b>:</td> |
||||
|
<td><a href="/CmsKit/Comments/Details/@Model.CommentWithAuthorDto.RepliedCommentId">@Model.CommentWithAuthorDto.RepliedCommentId</a></td> |
||||
|
</tr> |
||||
|
} |
||||
|
<tr> |
||||
|
<td class="align-text-top"><b>@L["Text"]</b>:</td> |
||||
|
<td>@Model.CommentWithAuthorDto.Text</td> |
||||
|
</tr> |
||||
|
</abp-table> |
||||
|
</abp-column> |
||||
|
</abp-row> |
||||
|
</abp-card-body> |
||||
|
</abp-card> |
||||
|
|
||||
|
<abp-card> |
||||
|
<abp-card-body> |
||||
|
<abp-row> |
||||
|
<abp-column> |
||||
|
<form id="CmsKitCommentsFilterForm" method="post"> |
||||
|
<abp-row> |
||||
|
<input type="hidden" name="@nameof(Model.CommentWithAuthorDto.RepliedCommentId)" value="@Model.CommentWithAuthorDto.Id"/> |
||||
|
|
||||
|
<abp-column size-lg="_6" size-md="_12" class="input-group input-daterange"> |
||||
|
<abp-column size="_6" style="padding-left: 0"> |
||||
|
<abp-input asp-for="@Model.CreationStartDate" label="@L["StartDate"].Value" type="text"/> |
||||
|
</abp-column> |
||||
|
<abp-column size="_6" style="padding-right: 0"> |
||||
|
<abp-input asp-for="@Model.CreationEndDate" label="@L["EndDate"].Value" type="text"/> |
||||
|
</abp-column> |
||||
|
</abp-column> |
||||
|
<abp-column size-lg="_4" size-md="_12"> |
||||
|
<abp-input asp-for="@Model.Author" label="@L["Username"].Value" type="text"/> |
||||
|
</abp-column> |
||||
|
<abp-column size-lg="_2" size-md="_12"> |
||||
|
<abp-button class="mt-md-4" size="Block" button-type="Primary" type="submit"> |
||||
|
<i class="fa fa-search" aria-hidden="true"></i> |
||||
|
</abp-button> |
||||
|
</abp-column> |
||||
|
</abp-row> |
||||
|
</form> |
||||
|
</abp-column> |
||||
|
</abp-row> |
||||
|
</abp-card-body> |
||||
|
</abp-card> |
||||
|
|
||||
|
<h3>@L["RepliesToThisComment"]</h3> |
||||
|
|
||||
|
<abp-table striped-rows="true" id="CommentsTable" class="nowrap"></abp-table> |
||||
|
</div> |
||||
@ -0,0 +1,33 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Volo.CmsKit.Admin.Comments; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.Comments |
||||
|
{ |
||||
|
public class DetailsModel : CmsKitAdminPageModel |
||||
|
{ |
||||
|
[BindProperty(SupportsGet = true)] |
||||
|
public Guid Id { get; set; } |
||||
|
|
||||
|
public string Author { get; set; } |
||||
|
|
||||
|
public DateTime? CreationStartDate { get; set; } |
||||
|
|
||||
|
public DateTime? CreationEndDate { get; set; } |
||||
|
|
||||
|
public CommentWithAuthorDto CommentWithAuthorDto { get; protected set; } |
||||
|
|
||||
|
protected readonly ICommentAdminAppService CommentAdminAppService; |
||||
|
|
||||
|
public DetailsModel(ICommentAdminAppService commentAdminAppService) |
||||
|
{ |
||||
|
CommentAdminAppService = commentAdminAppService; |
||||
|
} |
||||
|
|
||||
|
public async Task OnGetAsync() |
||||
|
{ |
||||
|
CommentWithAuthorDto = await CommentAdminAppService.GetAsync(Id); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,71 @@ |
|||||
|
@page |
||||
|
|
||||
|
@using Microsoft.AspNetCore.Mvc.Localization |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Layout |
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Comments |
||||
|
@using Volo.CmsKit.Admin.Web.Menus |
||||
|
@using Volo.CmsKit.Localization |
||||
|
|
||||
|
@inject IPageLayout PageLayout |
||||
|
@inject IHtmlLocalizer<CmsKitResource> L |
||||
|
|
||||
|
@model IndexModel |
||||
|
|
||||
|
@{ |
||||
|
PageLayout.Content.Title = L["Comments"].Value; |
||||
|
PageLayout.Content.BreadCrumb.Add(L["Menu:CMS"].Value); |
||||
|
PageLayout.Content.MenuItemName = CmsKitAdminMenus.Comments.CommentsMenu; |
||||
|
|
||||
|
var defaultStartDate = DateTime.Now.AddDays(-7).Date.ToShortDateString(); |
||||
|
} |
||||
|
|
||||
|
@section styles{ |
||||
|
<abp-style-bundle> |
||||
|
<abp-style src="/Pages/CmsKit/Comments/index.css" /> |
||||
|
</abp-style-bundle> |
||||
|
} |
||||
|
|
||||
|
@section scripts { |
||||
|
<abp-script-bundle> |
||||
|
<abp-script src="/Pages/CmsKit/Comments/index.js" /> |
||||
|
</abp-script-bundle> |
||||
|
} |
||||
|
|
||||
|
<div id="CmsKitCommentsWrapper"> |
||||
|
<abp-card> |
||||
|
<abp-card-body> |
||||
|
<abp-row> |
||||
|
<abp-column> |
||||
|
<form id="CmsKitCommentsFilterForm" method="post"> |
||||
|
<abp-row> |
||||
|
<abp-column size-lg="_4" size-md="_12" class="input-group input-daterange"> |
||||
|
<abp-column size="_6" style="padding-left: 0"> |
||||
|
<abp-input asp-for="@Model.CreationStartDate" label="@L["StartDate"].Value" value="@defaultStartDate" type="text" /> |
||||
|
</abp-column> |
||||
|
<abp-column size="_6" style="padding-right: 0"> |
||||
|
<abp-input asp-for="@Model.CreationEndDate" label="@L["EndDate"].Value" type="text" /> |
||||
|
</abp-column> |
||||
|
</abp-column> |
||||
|
<abp-column size-lg="_2" size-md="_6"> |
||||
|
<abp-input asp-for="@Model.EntityType" label="@L["EntityType"].Value" type="text"/> |
||||
|
</abp-column> |
||||
|
<abp-column size-lg="_2" size-md="_6"> |
||||
|
<abp-input asp-for="@Model.EntityId" label="@L["EntityId"].Value" type="text"/> |
||||
|
</abp-column> |
||||
|
<abp-column size-lg="_2" size-md="_12"> |
||||
|
<abp-input asp-for="@Model.Author" label="@L["Username"].Value" type="text"/> |
||||
|
</abp-column> |
||||
|
<abp-column size-lg="_2" size-md="_12"> |
||||
|
<abp-button class="mt-md-4" size="Block" button-type="Primary" type="submit"> |
||||
|
<i class="fa fa-search" aria-hidden="true"></i> |
||||
|
</abp-button> |
||||
|
</abp-column> |
||||
|
</abp-row> |
||||
|
</form> |
||||
|
</abp-column> |
||||
|
</abp-row> |
||||
|
</abp-card-body> |
||||
|
</abp-card> |
||||
|
|
||||
|
<abp-table striped-rows="true" id="CommentsTable" class="nowrap"></abp-table> |
||||
|
</div> |
||||
@ -0,0 +1,17 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.Comments |
||||
|
{ |
||||
|
public class IndexModel : CmsKitAdminPageModel |
||||
|
{ |
||||
|
public string EntityType { get; set; } |
||||
|
|
||||
|
public string EntityId { get; set; } |
||||
|
|
||||
|
public string Author { get; set; } |
||||
|
|
||||
|
public DateTime? CreationStartDate { get; set; } |
||||
|
|
||||
|
public DateTime? CreationEndDate { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,3 @@ |
|||||
|
.datatableCell{ |
||||
|
cursor: pointer; |
||||
|
} |
||||
@ -0,0 +1,146 @@ |
|||||
|
$(function (){ |
||||
|
var l = abp.localization.getResource("CmsKit"); |
||||
|
|
||||
|
var commentsService = volo.cmsKit.admin.comments.commentAdmin; |
||||
|
|
||||
|
var detailsModal = new abp.ModalManager(abp.appPath + "CmsKit/Comments/DetailsModal"); |
||||
|
|
||||
|
$(".input-daterange") |
||||
|
.datepicker({ |
||||
|
todayBtn: "linked", |
||||
|
autoclose: true, |
||||
|
language: abp.localization.currentCulture.cultureName, |
||||
|
}) |
||||
|
.on("hide", function (e) { |
||||
|
e.stopPropagation(); |
||||
|
}); |
||||
|
|
||||
|
var filterForm = $('#CmsKitCommentsFilterForm'); |
||||
|
|
||||
|
var getFilter = function () { |
||||
|
var filterObj = filterForm.serializeFormToObject(); |
||||
|
|
||||
|
var startDate = luxon.DateTime.fromFormat( |
||||
|
filterObj.creationStartDate, |
||||
|
abp.localization.currentCulture.dateTimeFormat.shortDatePattern, |
||||
|
{ locale: abp.localization.currentCulture.cultureName } |
||||
|
); |
||||
|
if (!startDate.invalid) { |
||||
|
filterObj.creationStartDate = startDate.toFormat("yyyy-MM-dd"); |
||||
|
} |
||||
|
|
||||
|
var endDate = luxon.DateTime.fromFormat( |
||||
|
filterObj.creationEndDate, |
||||
|
abp.localization.currentCulture.dateTimeFormat.shortDatePattern, |
||||
|
{ locale: abp.localization.currentCulture.cultureName } |
||||
|
); |
||||
|
if (!endDate.invalid) { |
||||
|
filterObj.creationEndDate = endDate.toFormat("yyyy-MM-dd"); |
||||
|
} |
||||
|
|
||||
|
return filterObj; |
||||
|
}; |
||||
|
|
||||
|
var _dataTable = $('#CommentsTable').DataTable(abp.libs.datatables.normalizeConfiguration({ |
||||
|
processing: true, |
||||
|
serverSide: true, |
||||
|
paging: true, |
||||
|
scrollX: true, |
||||
|
searching: false, |
||||
|
scrollCollapse: true, |
||||
|
ajax: abp.libs.datatables.createAjax(commentsService.getList, getFilter), |
||||
|
columnDefs: [ |
||||
|
{ |
||||
|
width: "10%", |
||||
|
title: l("Actions"), |
||||
|
targets: 0, |
||||
|
orderable: false, |
||||
|
rowAction: { |
||||
|
items: [ |
||||
|
{ |
||||
|
text: l('Details'), |
||||
|
action: function (data) { |
||||
|
window.location = abp.appPath + 'CmsKit/Comments/Details/' + data.record.id; |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
text: l('Delete'), |
||||
|
visible: abp.auth.isGranted('CmsKit.Comments.Delete'), |
||||
|
confirmMessage: function (data) { |
||||
|
return l("CommentDeletionConfirmationMessage") |
||||
|
}, |
||||
|
action: function (data) { |
||||
|
commentsService |
||||
|
.delete(data.record.id) |
||||
|
.then(function () { |
||||
|
abp.notify.info(l("SuccessfullyDeleted")); |
||||
|
_dataTable.ajax.reload(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
width: "10%", |
||||
|
title: l("Username"), |
||||
|
orderable: false, |
||||
|
data: "author.userName", |
||||
|
render: function (data) { |
||||
|
if (data !== null) { |
||||
|
return GetFilterableDatatableContent('#Author', data); |
||||
|
} |
||||
|
return ""; |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
title: l("Text"), |
||||
|
data: "text", |
||||
|
orderable: false, |
||||
|
render: function (data) { |
||||
|
data = data || ""; |
||||
|
|
||||
|
var maxChars = 64; |
||||
|
|
||||
|
if (data.length > maxChars) { |
||||
|
return ( |
||||
|
'<span data-toggle="tooltip" title="' + |
||||
|
data + |
||||
|
'">' + |
||||
|
data.substring(0, maxChars) + |
||||
|
"..." + |
||||
|
"</span>" |
||||
|
); |
||||
|
} else { |
||||
|
return data; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
width: "15%", |
||||
|
title: l("CreationTime"), |
||||
|
data: "creationTime", |
||||
|
orderable: true, |
||||
|
dataFormat: "datetime" |
||||
|
} |
||||
|
] |
||||
|
})); |
||||
|
|
||||
|
function GetFilterableDatatableContent(filterSelector, data){ |
||||
|
return '<span class="datatableCell" data-field="'+ filterSelector +'" data-val="'+ data +'">' + data + '</span>'; |
||||
|
} |
||||
|
|
||||
|
$(document).on('click', '.datatableCell', function () { |
||||
|
var inputSelector = $(this).attr('data-field'); |
||||
|
var value = $(this).attr('data-val'); |
||||
|
|
||||
|
$(inputSelector).val(value); |
||||
|
|
||||
|
_dataTable.ajax.reload(); |
||||
|
}); |
||||
|
|
||||
|
filterForm.submit(function (e){ |
||||
|
e.preventDefault(); |
||||
|
_dataTable.ajax.reload(); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,3 @@ |
|||||
|
.datatableCell{ |
||||
|
cursor: pointer; |
||||
|
} |
||||
@ -0,0 +1,158 @@ |
|||||
|
$(function (){ |
||||
|
var l = abp.localization.getResource("CmsKit"); |
||||
|
|
||||
|
var commentsService = volo.cmsKit.admin.comments.commentAdmin; |
||||
|
|
||||
|
var detailsModal = new abp.ModalManager(abp.appPath + "CmsKit/Comments/DetailsModal"); |
||||
|
|
||||
|
$(".input-daterange") |
||||
|
.datepicker({ |
||||
|
todayBtn: "linked", |
||||
|
autoclose: true, |
||||
|
language: abp.localization.currentCulture.cultureName, |
||||
|
}) |
||||
|
.on("hide", function (e) { |
||||
|
e.stopPropagation(); |
||||
|
}); |
||||
|
|
||||
|
var filterForm = $('#CmsKitCommentsFilterForm'); |
||||
|
|
||||
|
var getFilter = function () { |
||||
|
var filterObj = filterForm.serializeFormToObject(); |
||||
|
|
||||
|
var startDate = luxon.DateTime.fromFormat( |
||||
|
filterObj.creationStartDate, |
||||
|
abp.localization.currentCulture.dateTimeFormat.shortDatePattern, |
||||
|
{ locale: abp.localization.currentCulture.cultureName } |
||||
|
); |
||||
|
if (!startDate.invalid) { |
||||
|
filterObj.creationStartDate = startDate.toFormat("yyyy-MM-dd"); |
||||
|
} |
||||
|
|
||||
|
var endDate = luxon.DateTime.fromFormat( |
||||
|
filterObj.creationEndDate, |
||||
|
abp.localization.currentCulture.dateTimeFormat.shortDatePattern, |
||||
|
{ locale: abp.localization.currentCulture.cultureName } |
||||
|
); |
||||
|
if (!endDate.invalid) { |
||||
|
filterObj.creationEndDate = endDate.toFormat("yyyy-MM-dd"); |
||||
|
} |
||||
|
|
||||
|
return filterObj; |
||||
|
}; |
||||
|
|
||||
|
var _dataTable = $('#CommentsTable').DataTable(abp.libs.datatables.normalizeConfiguration({ |
||||
|
processing: true, |
||||
|
serverSide: true, |
||||
|
paging: true, |
||||
|
scrollX: true, |
||||
|
searching: false, |
||||
|
scrollCollapse: true, |
||||
|
ajax: abp.libs.datatables.createAjax(commentsService.getList, getFilter), |
||||
|
columnDefs: [ |
||||
|
{ |
||||
|
width: "10%", |
||||
|
title: l("Actions"), |
||||
|
targets: 0, |
||||
|
orderable: false, |
||||
|
rowAction: { |
||||
|
items: [ |
||||
|
{ |
||||
|
text: l('Details'), |
||||
|
action: function (data) { |
||||
|
window.location = abp.appPath + 'CmsKit/Comments/Details/' + data.record.id; |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
text: l('Delete'), |
||||
|
visible: abp.auth.isGranted('CmsKit.Comments.Delete'), |
||||
|
confirmMessage: function (data) { |
||||
|
return l("CommentDeletionConfirmationMessage") |
||||
|
}, |
||||
|
action: function (data) { |
||||
|
commentsService |
||||
|
.delete(data.record.id) |
||||
|
.then(function () { |
||||
|
abp.notify.info(l("SuccessfullyDeleted")); |
||||
|
_dataTable.ajax.reload(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
width: "10%", |
||||
|
title: l("Username"), |
||||
|
orderable: false, |
||||
|
data: "author.userName", |
||||
|
render: function (data) { |
||||
|
if (data !== null) { |
||||
|
return GetFilterableDatatableContent('#Author', data); |
||||
|
} |
||||
|
return ""; |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
width: "15%", |
||||
|
title: l("EntityType"), |
||||
|
orderable: false, |
||||
|
data: "entityType", |
||||
|
render: function (data) { |
||||
|
if (data !== null) { |
||||
|
return GetFilterableDatatableContent('#EntityType', data); |
||||
|
} |
||||
|
return ""; |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
title: l("Text"), |
||||
|
data: "text", |
||||
|
orderable: false, |
||||
|
render: function (data) { |
||||
|
data = data || ""; |
||||
|
|
||||
|
var maxChars = 64; |
||||
|
|
||||
|
if (data.length > maxChars) { |
||||
|
return ( |
||||
|
'<span data-toggle="tooltip" title="' + |
||||
|
data + |
||||
|
'">' + |
||||
|
data.substring(0, maxChars) + |
||||
|
"..." + |
||||
|
"</span>" |
||||
|
); |
||||
|
} else { |
||||
|
return data; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
width: "15%", |
||||
|
title: l("CreationTime"), |
||||
|
data: "creationTime", |
||||
|
orderable: true, |
||||
|
dataFormat: "datetime" |
||||
|
} |
||||
|
] |
||||
|
})); |
||||
|
|
||||
|
function GetFilterableDatatableContent(filterSelector, data){ |
||||
|
return '<span class="datatableCell" data-field="'+ filterSelector +'" data-val="'+ data +'">' + data + '</span>'; |
||||
|
} |
||||
|
|
||||
|
$(document).on('click', '.datatableCell', function () { |
||||
|
var inputSelector = $(this).attr('data-field'); |
||||
|
var value = $(this).attr('data-val'); |
||||
|
|
||||
|
$(inputSelector).val(value); |
||||
|
|
||||
|
_dataTable.ajax.reload(); |
||||
|
}); |
||||
|
|
||||
|
filterForm.submit(function (e){ |
||||
|
e.preventDefault(); |
||||
|
_dataTable.ajax.reload(); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,44 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Packages.TuiEditor; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Packages.Uppy; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.Contents.Components.ContentEditor |
||||
|
{ |
||||
|
[Widget( |
||||
|
StyleTypes = new[] |
||||
|
{ |
||||
|
typeof(TuiEditorStyleContributor) |
||||
|
}, |
||||
|
ScriptTypes = new[] |
||||
|
{ |
||||
|
typeof(TuiEditorScriptContributor), |
||||
|
typeof(UppyScriptContributor) |
||||
|
}, |
||||
|
ScriptFiles = new[] |
||||
|
{ |
||||
|
"/Pages/CmsKit/Contents/Components/ContentEditor/default.js" |
||||
|
})] |
||||
|
public class ContentEditorViewComponent : AbpViewComponent |
||||
|
{ |
||||
|
public async Task<IViewComponentResult> InvokeAsync(string inputId) |
||||
|
{ |
||||
|
return View( |
||||
|
"~/Pages/CmsKit/Contents/Components/ContentEditor/Default.cshtml", |
||||
|
new ContentEditorViewModel(inputId)); |
||||
|
} |
||||
|
|
||||
|
public class ContentEditorViewModel |
||||
|
{ |
||||
|
public ContentEditorViewModel(string inputId) |
||||
|
{ |
||||
|
InputId = inputId; |
||||
|
} |
||||
|
|
||||
|
public string InputId { get; set; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
@using System.Globalization |
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Contents.Components.ContentEditor |
||||
|
|
||||
|
@model ContentEditorViewComponent.ContentEditorViewModel |
||||
|
|
||||
|
<div class="content-editor" id="@Model.InputId-container" data-input-id="@Model.InputId" data-language="@(CultureInfo.CurrentUICulture.TwoLetterISOLanguageName)"> |
||||
|
</div> |
||||
@ -0,0 +1,80 @@ |
|||||
|
$(function () { |
||||
|
var fileUploadUri = "/api/cms-kit-admin/media"; |
||||
|
var fileUriPrefix = "/api/cms-kit/media/"; |
||||
|
|
||||
|
var editorDataKey = "tuiEditor"; |
||||
|
|
||||
|
initAllEditors(); |
||||
|
|
||||
|
function initAllEditors() { |
||||
|
$('.content-editor').each(function (i, item) { |
||||
|
initEditor(item); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function initEditor(element) { |
||||
|
var $editorContainer = $(element); |
||||
|
var inputName = $editorContainer.data('input-id'); |
||||
|
var $editorInput = $('#' + inputName); |
||||
|
var initialValue = $editorInput.val(); |
||||
|
|
||||
|
var editor = $editorContainer.tuiEditor({ |
||||
|
usageStatistics: false, |
||||
|
useCommandShortcut: true, |
||||
|
initialValue: initialValue, |
||||
|
previewStyle: 'tab', |
||||
|
height: "25em", |
||||
|
minHeight: "25em", |
||||
|
initialEditType: initialValue ? 'wysiwyg' : 'markdown', |
||||
|
language: $editorContainer.data("language"), |
||||
|
hooks: { |
||||
|
addImageBlobHook: uploadFile, |
||||
|
}, |
||||
|
events: { |
||||
|
change: function (_val) { |
||||
|
$editorInput.val(editor.getHtml()); |
||||
|
$editorInput.trigger("change"); |
||||
|
} |
||||
|
} |
||||
|
}).data(editorDataKey); |
||||
|
} |
||||
|
|
||||
|
function getUppyHeaders() { |
||||
|
var headers = {}; |
||||
|
headers[abp.security.antiForgery.tokenHeaderName] = abp.security.antiForgery.getToken(); |
||||
|
|
||||
|
return headers; |
||||
|
} |
||||
|
|
||||
|
function uploadFile(blob, callback, source) { |
||||
|
var UPPY_OPTIONS = { |
||||
|
endpoint: fileUploadUri, |
||||
|
formData: true, |
||||
|
fieldName: "file", |
||||
|
method: "post", |
||||
|
headers: getUppyHeaders() |
||||
|
}; |
||||
|
|
||||
|
var UPPY = Uppy.Core().use(Uppy.XHRUpload, UPPY_OPTIONS); |
||||
|
|
||||
|
UPPY.reset(); |
||||
|
|
||||
|
UPPY.addFile({ |
||||
|
id: "content-file", |
||||
|
name: blob.name, |
||||
|
type: blob.type, |
||||
|
data: blob, |
||||
|
}); |
||||
|
|
||||
|
UPPY.upload().then((result) => { |
||||
|
if (result.failed.length > 0) { |
||||
|
abp.message.error("File upload failed"); |
||||
|
} else { |
||||
|
var mediaDto = result.successful[0].response.body; |
||||
|
var fileUrl = (fileUriPrefix + mediaDto.id); |
||||
|
|
||||
|
callback(fileUrl, mediaDto.name); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
@ -0,0 +1,28 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.Contents.Components.EntityContentEditor |
||||
|
{ |
||||
|
public class ContentViewModel |
||||
|
{ |
||||
|
public ContentViewModel(string entityType, string entityId, string value = null, Guid? id = null) |
||||
|
{ |
||||
|
EntityType = entityType; |
||||
|
EntityId = entityId; |
||||
|
Value = value; |
||||
|
Id = id; |
||||
|
} |
||||
|
|
||||
|
[HiddenInput] |
||||
|
public string EntityType { get; set; } |
||||
|
|
||||
|
[HiddenInput] |
||||
|
public string EntityId { get; set; } |
||||
|
|
||||
|
[HiddenInput] |
||||
|
public string Value { get; set; } |
||||
|
|
||||
|
[HiddenInput] |
||||
|
public Guid? Id { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Contents.Components.EntityContentEditor |
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Contents.Components.ContentEditor |
||||
|
@using Volo.CmsKit.Localization |
||||
|
@using Microsoft.Extensions.Localization |
||||
|
|
||||
|
@model EntityContentEditorViewComponent.EntityContentEditorViewModel |
||||
|
|
||||
|
@inject IStringLocalizer<CmsKitResource> L |
||||
|
|
||||
|
@if (Model.DisplaySubmitButton) |
||||
|
{ |
||||
|
<abp-dynamic-form abp-model="ViewModel" class="form-entity-content-editor"> |
||||
|
|
||||
|
<abp-form-content /> |
||||
|
|
||||
|
<abp-button button-type="Primary" type="submit" text="@L["Submit"]" /> |
||||
|
</abp-dynamic-form> |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
<abp-input asp-for="ViewModel.Id" /> |
||||
|
<abp-input asp-for="ViewModel.EntityId" /> |
||||
|
<abp-input asp-for="ViewModel.EntityType" /> |
||||
|
<abp-input asp-for="ViewModel.Value" /> |
||||
|
} |
||||
|
|
||||
|
@await Component.InvokeAsync(typeof(ContentEditorViewComponent), new { inputId = Html.IdFor(m => m.ViewModel.Value) }) |
||||
@ -0,0 +1,65 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
||||
|
using Volo.Abp.Domain.Entities; |
||||
|
using Volo.CmsKit.Admin.Contents; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.Contents.Components.EntityContentEditor |
||||
|
{ |
||||
|
[Widget( |
||||
|
ScriptFiles = new[] |
||||
|
{ |
||||
|
"/Pages/CmsKit/Contents/Components/EntityContentEditor/default.js" |
||||
|
})] |
||||
|
public class EntityContentEditorViewComponent : AbpViewComponent |
||||
|
{ |
||||
|
private readonly IContentAdminAppService contentAdminAppService; |
||||
|
|
||||
|
public EntityContentEditorViewComponent(IContentAdminAppService contentAdminAppService) |
||||
|
{ |
||||
|
this.contentAdminAppService = contentAdminAppService; |
||||
|
} |
||||
|
|
||||
|
public async Task<IViewComponentResult> InvokeAsync(string entityType, string entityId, bool displaySubmitButton = true) |
||||
|
{ |
||||
|
var viewModel = new ContentViewModel(entityType, entityId); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
if (entityId != null) |
||||
|
{ |
||||
|
var content = await contentAdminAppService.GetAsync(entityType, entityId); |
||||
|
viewModel.Value = content.Value; |
||||
|
viewModel.Id = content.Id; |
||||
|
} |
||||
|
} |
||||
|
catch (EntityNotFoundException) |
||||
|
{ |
||||
|
// Initialize editor even content doesn't exist.
|
||||
|
} |
||||
|
|
||||
|
return View( |
||||
|
"~/Pages/CmsKit/Contents/Components/EntityContentEditor/Default.cshtml", |
||||
|
new EntityContentEditorViewModel(viewModel, displaySubmitButton)); |
||||
|
} |
||||
|
|
||||
|
public class EntityContentEditorViewModel |
||||
|
{ |
||||
|
public ContentViewModel ViewModel { get; set; } |
||||
|
|
||||
|
public bool DisplaySubmitButton { get; set; } |
||||
|
|
||||
|
public EntityContentEditorViewModel(ContentViewModel viewModel, bool displaySubmitButton) |
||||
|
{ |
||||
|
ViewModel = viewModel; |
||||
|
DisplaySubmitButton = displaySubmitButton; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,34 @@ |
|||||
|
$(function () { |
||||
|
|
||||
|
$entityContentEditors = $('.form-entity-content-editor'); |
||||
|
|
||||
|
var contentAdminService = volo.cmsKit.admin.contents.contentAdmin; |
||||
|
|
||||
|
$entityContentEditors.on('submit', function (e, obj) { |
||||
|
e.preventDefault(); |
||||
|
|
||||
|
var $form = $(e.currentTarget); |
||||
|
|
||||
|
if ($form.valid()) { |
||||
|
|
||||
|
var data = form.serializeFormToObject().viewModel; |
||||
|
|
||||
|
abp.ui.setBusy(e); |
||||
|
if (data.id) { |
||||
|
contentAdminService |
||||
|
.update(data.id, data) |
||||
|
.then(function (result) { |
||||
|
abp.ui.clearBusy(); |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
else { |
||||
|
contentAdminService |
||||
|
.create(data) |
||||
|
.then(function (result) { |
||||
|
abp.ui.clearBusy(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
@ -0,0 +1,45 @@ |
|||||
|
@page |
||||
|
|
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Contents.Components.EntityContentEditor |
||||
|
@using Volo.CmsKit.Admin.Web.Pages |
||||
|
@using Volo.CmsKit.Admin.Web.Menus |
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Pages |
||||
|
@using Volo.CmsKit.Admin.Web.Bundles |
||||
|
|
||||
|
@inherits CmsKitAdminPageBase |
||||
|
|
||||
|
@model CreateModel |
||||
|
|
||||
|
@{ |
||||
|
PageLayout.Content.Title = L["Pages"].Value; |
||||
|
PageLayout.Content.BreadCrumb.Add(L["Menu:CMS"].Value); |
||||
|
PageLayout.Content.MenuItemName = CmsKitAdminMenus.Pages.PagesMenu; |
||||
|
} |
||||
|
|
||||
|
@section scripts { |
||||
|
<abp-script-bundle> |
||||
|
<abp-script src="/Pages/CmsKit/Pages/create.js" /> |
||||
|
<abp-script type="typeof(SlugifyScriptContributor)" /> |
||||
|
</abp-script-bundle> |
||||
|
} |
||||
|
|
||||
|
<abp-card> |
||||
|
<abp-card-header title="@L["New"].Value"></abp-card-header> |
||||
|
<abp-card-body> |
||||
|
<abp-dynamic-form abp-model="ViewModel" asp-page="/CmsKit/Pages/Create" id="form-page-create"> |
||||
|
<abp-form-content /> |
||||
|
|
||||
|
<abp-input asp-for="ViewModel.Slug" title="@L["PageSlugInformation"]" data-toggle="tooltip"/> |
||||
|
</abp-dynamic-form> |
||||
|
|
||||
|
@await Component.InvokeAsync(typeof(EntityContentEditorViewComponent), new |
||||
|
{ |
||||
|
entityType = "Page", |
||||
|
displaySubmitButton = false |
||||
|
}) |
||||
|
|
||||
|
</abp-card-body> |
||||
|
<abp-card-footer> |
||||
|
<abp-button button-type="Primary" type="submit" text="@L["Submit"].Value" id="button-page-create" /> |
||||
|
</abp-card-footer> |
||||
|
</abp-card> |
||||
@ -0,0 +1,47 @@ |
|||||
|
using AutoMapper; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; |
||||
|
using Volo.Abp.Validation; |
||||
|
using Volo.CmsKit.Admin.Pages; |
||||
|
using Volo.CmsKit.Admin.Web.Pages; |
||||
|
using Volo.CmsKit.Pages; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.Pages |
||||
|
{ |
||||
|
public class CreateModel : CmsKitAdminPageModel |
||||
|
{ |
||||
|
protected readonly IPageAdminAppService pageAdminAppService; |
||||
|
|
||||
|
[BindProperty] |
||||
|
public CreatePageViewModel ViewModel { get; set; } |
||||
|
|
||||
|
public CreateModel(IPageAdminAppService pageAdminAppService) |
||||
|
{ |
||||
|
this.pageAdminAppService = pageAdminAppService; |
||||
|
} |
||||
|
|
||||
|
public async Task<IActionResult> OnPostAsync() |
||||
|
{ |
||||
|
var createInput = ObjectMapper.Map<CreatePageViewModel, CreatePageInputDto>(ViewModel); |
||||
|
|
||||
|
var created = await pageAdminAppService.CreateAsync(createInput); |
||||
|
|
||||
|
return new OkObjectResult(created); |
||||
|
} |
||||
|
|
||||
|
[AutoMap(typeof(CreatePageInputDto), ReverseMap = true)] |
||||
|
public class CreatePageViewModel |
||||
|
{ |
||||
|
[DynamicMaxLength(typeof(PageConsts), nameof(PageConsts.MaxTitleLength))] |
||||
|
[Required] |
||||
|
public string Title { get; set; } |
||||
|
|
||||
|
[DynamicMaxLength(typeof(PageConsts), nameof(PageConsts.MaxSlugLength))] |
||||
|
[Required] |
||||
|
[DynamicFormIgnore] |
||||
|
public string Slug { get; set; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
@page |
||||
|
|
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Layout |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageToolbar |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageSearchBox |
||||
|
@using Volo.CmsKit.Admin.Web.Pages |
||||
|
@using Volo.CmsKit.Admin.Web.Menus |
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Pages |
||||
|
|
||||
|
@inherits CmsKitAdminPageBase |
||||
|
|
||||
|
@model IndexModel |
||||
|
|
||||
|
@{ |
||||
|
PageLayout.Content.Title = L["Pages"].Value; |
||||
|
PageLayout.Content.BreadCrumb.Add(L["Menu:CMS"].Value); |
||||
|
PageLayout.Content.MenuItemName = CmsKitAdminMenus.Pages.PagesMenu; |
||||
|
} |
||||
|
|
||||
|
@section scripts { |
||||
|
<abp-script src="/Pages/CmsKit/Pages/index.js"/> |
||||
|
} |
||||
|
|
||||
|
@section content_toolbar { |
||||
|
@await Component.InvokeAsync(typeof(AbpPageToolbarViewComponent), new {pageName = typeof(IndexModel).FullName}) |
||||
|
} |
||||
|
|
||||
|
<div id="CmsKitPagesWrapper"> |
||||
|
<abp-card> |
||||
|
<abp-card-body> |
||||
|
<abp-row> |
||||
|
<abp-column> |
||||
|
@await Component.InvokeAsync(typeof(AbpPageSearchBoxViewComponent)) |
||||
|
</abp-column> |
||||
|
</abp-row> |
||||
|
</abp-card-body> |
||||
|
</abp-card> |
||||
|
|
||||
|
<abp-table striped-rows="true" id="PagesTable" class="nowrap"></abp-table> |
||||
|
</div> |
||||
@ -0,0 +1,11 @@ |
|||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.Pages |
||||
|
{ |
||||
|
public class IndexModel : CmsKitAdminPageModel |
||||
|
{ |
||||
|
public void OnGet() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
@page "{Id}" |
||||
|
|
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Contents.Components.EntityContentEditor |
||||
|
@using Volo.CmsKit.Admin.Web.Menus |
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Pages |
||||
|
@using Volo.CmsKit.Admin.Web.Pages |
||||
|
|
||||
|
@inherits CmsKitAdminPageBase |
||||
|
|
||||
|
@model UpdateModel |
||||
|
|
||||
|
@{ |
||||
|
PageLayout.Content.Title = L["Pages"].Value; |
||||
|
PageLayout.Content.BreadCrumb.Add(L["Menu:CMS"].Value); |
||||
|
PageLayout.Content.MenuItemName = CmsKitAdminMenus.Pages.PagesMenu; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@section scripts { |
||||
|
<abp-script src="/Pages/CmsKit/Pages/update.js" /> |
||||
|
} |
||||
|
|
||||
|
<abp-card> |
||||
|
<abp-card-header title="@L["Update"].Value"></abp-card-header> |
||||
|
<abp-card-body> |
||||
|
<abp-dynamic-form abp-model="ViewModel" asp-page="/CmsKit/Pages/Update" id="form-page-update"> |
||||
|
<abp-input asp-for="Id" /> |
||||
|
|
||||
|
<abp-form-content /> |
||||
|
|
||||
|
<abp-input asp-for="ViewModel.Slug" title="@L["PageSlugInformation"]" data-toggle="tooltip" /> |
||||
|
</abp-dynamic-form> |
||||
|
|
||||
|
@await Component.InvokeAsync(typeof(EntityContentEditorViewComponent), new |
||||
|
{ |
||||
|
entityType = "Page", |
||||
|
entityId = Model.Id.ToString(), |
||||
|
displaySubmitButton = false |
||||
|
}) |
||||
|
|
||||
|
</abp-card-body> |
||||
|
<abp-card-footer> |
||||
|
<abp-button button-type="Primary" type="submit" text="@L["Submit"].Value" id="button-page-update" /> |
||||
|
</abp-card-footer> |
||||
|
</abp-card> |
||||
@ -0,0 +1,62 @@ |
|||||
|
using AutoMapper; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; |
||||
|
using Volo.Abp.Validation; |
||||
|
using Volo.CmsKit.Admin.Pages; |
||||
|
using Volo.CmsKit.Pages; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.Pages |
||||
|
{ |
||||
|
public class UpdateModel : CmsKitAdminPageModel |
||||
|
{ |
||||
|
[BindProperty(SupportsGet = true)] |
||||
|
[HiddenInput] |
||||
|
public Guid Id { get; set; } |
||||
|
|
||||
|
[BindProperty] |
||||
|
public UpdatePageViewModel ViewModel { get; set; } |
||||
|
|
||||
|
protected readonly IPageAdminAppService pageAdminAppService; |
||||
|
|
||||
|
public UpdateModel(IPageAdminAppService pageAdminAppService) |
||||
|
{ |
||||
|
this.pageAdminAppService = pageAdminAppService; |
||||
|
} |
||||
|
|
||||
|
public async Task OnGetAsync() |
||||
|
{ |
||||
|
var dto = await pageAdminAppService.GetAsync(Id); |
||||
|
|
||||
|
ViewModel = ObjectMapper.Map<PageDto, UpdatePageViewModel>(dto); |
||||
|
} |
||||
|
|
||||
|
public async Task<IActionResult> OnPostAsync() |
||||
|
{ |
||||
|
var updateInput = ObjectMapper.Map<UpdatePageViewModel, UpdatePageInputDto>(ViewModel); |
||||
|
|
||||
|
await pageAdminAppService.UpdateAsync(Id, updateInput); |
||||
|
|
||||
|
return NoContent(); |
||||
|
} |
||||
|
|
||||
|
[AutoMap(typeof(PageDto))] |
||||
|
[AutoMap(typeof(UpdatePageInputDto), ReverseMap = true)] |
||||
|
public class UpdatePageViewModel |
||||
|
{ |
||||
|
[DynamicMaxLength(typeof(PageConsts), nameof(PageConsts.MaxTitleLength))] |
||||
|
[Required] |
||||
|
public string Title { get; set; } |
||||
|
|
||||
|
[DynamicMaxLength(typeof(PageConsts), nameof(PageConsts.MaxSlugLength))] |
||||
|
[Required] |
||||
|
[DynamicFormIgnore] |
||||
|
public string Slug { get; set; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,72 @@ |
|||||
|
$(function () { |
||||
|
var l = abp.localization.getResource("CmsKit"); |
||||
|
|
||||
|
var $formCreate = $('#form-page-create'); |
||||
|
var $title = $('#ViewModel_Title'); |
||||
|
var $slug = $('#ViewModel_Slug'); |
||||
|
var $buttonSubmit = $('#button-page-create'); |
||||
|
var $pageContentInput = $('#ViewModel_Value'); |
||||
|
|
||||
|
$formCreate.on('submit', function (e) { |
||||
|
e.preventDefault(); |
||||
|
|
||||
|
if ($formCreate.valid()) { |
||||
|
|
||||
|
abp.ui.setBusy(); |
||||
|
|
||||
|
$formCreate.ajaxSubmit({ |
||||
|
success: function (result) { |
||||
|
submitEntityContent(result.id); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
$buttonSubmit.click(function (e) { |
||||
|
e.preventDefault(); |
||||
|
$formCreate.submit(); |
||||
|
}); |
||||
|
|
||||
|
function submitEntityContent(pageId) { |
||||
|
volo.cmsKit.admin.contents.contentAdmin |
||||
|
.create( |
||||
|
{ |
||||
|
entityType: 'Page', |
||||
|
entityId: pageId, |
||||
|
value: $pageContentInput.val() |
||||
|
}) |
||||
|
.then(function (result) { |
||||
|
finishSaving(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function finishSaving() { |
||||
|
abp.notify.success(l('SuccessfullySaved')); |
||||
|
abp.ui.clearBusy(); |
||||
|
location.href = "/CmsKit/Pages/"; |
||||
|
} |
||||
|
|
||||
|
var slugEdited = false; |
||||
|
|
||||
|
$title.on('change paste keyup', function () { |
||||
|
var title = $title.val(); |
||||
|
|
||||
|
if (slugEdited) { |
||||
|
title = $slug.val(); |
||||
|
} |
||||
|
|
||||
|
var slugified = slugify(title, { |
||||
|
lower: true |
||||
|
}); |
||||
|
|
||||
|
if (slugified != $slug.val()) { |
||||
|
reflectedChange = true; |
||||
|
$slug.val(slugified); |
||||
|
reflectedChange = false; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
$slug.change(function () { |
||||
|
slugEdited = true; |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,81 @@ |
|||||
|
$(function (){ |
||||
|
var l = abp.localization.getResource("CmsKit"); |
||||
|
|
||||
|
var pagesService = volo.cmsKit.admin.pages.pageAdmin; |
||||
|
|
||||
|
var getFilter = function () { |
||||
|
return { |
||||
|
filter: $('#CmsKitPagesWrapper input.page-search-filter-text').val() |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
var _dataTable = $("#PagesTable").DataTable(abp.libs.datatables.normalizeConfiguration({ |
||||
|
processing: true, |
||||
|
serverSide: true, |
||||
|
paging: true, |
||||
|
searching: false, |
||||
|
autoWidth: false, |
||||
|
scrollCollapse: true, |
||||
|
scrollX: true, |
||||
|
ordering: true, |
||||
|
order: [[3, "desc"]], |
||||
|
ajax: abp.libs.datatables.createAjax(pagesService.getList, getFilter), |
||||
|
columnDefs: [ |
||||
|
{ |
||||
|
title: l("Details"), |
||||
|
targets: 0, |
||||
|
rowAction: { |
||||
|
items: [ |
||||
|
{ |
||||
|
text: l('Edit'), |
||||
|
visible: abp.auth.isGranted('CmsKit.Pages.Update'), |
||||
|
action: function (data) { |
||||
|
location.href = '/CmsKit/Pages/Update/' + data.record.id; |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
text: l('Delete'), |
||||
|
visible: abp.auth.isGranted('CmsKit.Pages.Delete'), |
||||
|
confirmMessage: function (data) { |
||||
|
return l("PageDeletionConfirmationMessage") |
||||
|
}, |
||||
|
action: function (data) { |
||||
|
pagesService |
||||
|
.delete(data.record.id) |
||||
|
.then(function () { |
||||
|
abp.notify.info(l("SuccessfullyDeleted")); |
||||
|
_dataTable.ajax.reload(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
title: l("Title"), |
||||
|
orderable: true, |
||||
|
data: "title" |
||||
|
}, |
||||
|
{ |
||||
|
title: l("Slug"), |
||||
|
orderable: true, |
||||
|
data: "slug" |
||||
|
}, |
||||
|
{ |
||||
|
title: l("CreationTime"), |
||||
|
orderable: true, |
||||
|
data: 'creationTime' |
||||
|
} |
||||
|
] |
||||
|
})); |
||||
|
|
||||
|
$('#CmsKitPagesWrapper form.page-search-form').submit(function (e) { |
||||
|
e.preventDefault(); |
||||
|
_dataTable.ajax.reload(); |
||||
|
}); |
||||
|
|
||||
|
$('#AbpContentToolbar button[name=CreatePage]').on('click', function (e) { |
||||
|
e.preventDefault(); |
||||
|
window.location.href = "/CmsKit/Pages/Create" |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,65 @@ |
|||||
|
$(function () { |
||||
|
|
||||
|
var l = abp.localization.getResource("CmsKit"); |
||||
|
|
||||
|
var $formUpdate = $('#form-page-update'); |
||||
|
var $buttonSubmit = $('#button-page-update'); |
||||
|
var $pageContentInput = $('#ViewModel_Value'); |
||||
|
var $pageIdInput = $('#Id'); |
||||
|
var $contentIdInput = $('#ViewModel_Id'); |
||||
|
|
||||
|
$formUpdate.on('submit', function (e) { |
||||
|
e.preventDefault(); |
||||
|
|
||||
|
if ($formUpdate.valid()) { |
||||
|
|
||||
|
abp.ui.setBusy(); |
||||
|
|
||||
|
$formUpdate.ajaxSubmit({ |
||||
|
success: function (result) { |
||||
|
submitEntityContent(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
$buttonSubmit.click(function (e) { |
||||
|
e.preventDefault(); |
||||
|
$formUpdate.submit(); |
||||
|
}); |
||||
|
|
||||
|
function submitEntityContent() { |
||||
|
|
||||
|
var contentId = $contentIdInput.val(); |
||||
|
var pageId = $pageIdInput.val(); |
||||
|
var contentValue = $pageContentInput.val(); |
||||
|
|
||||
|
if (contentId) { |
||||
|
volo.cmsKit.admin.contents.contentAdmin |
||||
|
.update(contentId, |
||||
|
{ |
||||
|
value: contentValue |
||||
|
}) |
||||
|
.then(function (result) { |
||||
|
finishSaving(result); |
||||
|
}); |
||||
|
} |
||||
|
else { |
||||
|
volo.cmsKit.admin.contents.contentAdmin |
||||
|
.create({ |
||||
|
entityType: 'Page', |
||||
|
entityId: pageId, |
||||
|
value: contentValue |
||||
|
}) |
||||
|
.then(function (result) { |
||||
|
finishSaving(result); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function finishSaving(result) { |
||||
|
abp.notify.success(l('SuccessfullySaved')); |
||||
|
abp.ui.clearBusy(); |
||||
|
location.href = "/CmsKit/Pages/"; |
||||
|
} |
||||
|
}); |
||||
@ -0,0 +1,22 @@ |
|||||
|
@model TagEditorViewComponent.TagEditorViewModel |
||||
|
|
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Tags.Components.TagEditor |
||||
|
@using Volo.CmsKit.Localization |
||||
|
@using Microsoft.Extensions.Localization |
||||
|
|
||||
|
@inject IStringLocalizer<CmsKitResource> L |
||||
|
|
||||
|
<form class="tag-editor-form" data-entity-id="@Model.EntityId" data-entity-type="@Model.EntityType"> |
||||
|
|
||||
|
<div class="form-group"> |
||||
|
<label>@L["Tags"]</label> |
||||
|
<input name="tags" value="@string.Join(',', Model.Tags.Select(s => s.Name))" class="form-control"/> |
||||
|
</div> |
||||
|
|
||||
|
@if (Model.DisplaySubmitButton) |
||||
|
{ |
||||
|
<abp-form-row> |
||||
|
<abp-button type="submit" text="@L["Submit"]" /> |
||||
|
</abp-form-row> |
||||
|
} |
||||
|
</form> |
||||
@ -0,0 +1,49 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Widgets; |
||||
|
using Volo.CmsKit.Tags; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.Tags.Components.TagEditor |
||||
|
{ |
||||
|
[Widget( |
||||
|
ScriptFiles = new[] |
||||
|
{ |
||||
|
"/Pages/CmsKit/Tags/Components/TagEditor/default.js" |
||||
|
})] |
||||
|
public class TagEditorViewComponent : AbpViewComponent |
||||
|
{ |
||||
|
protected ITagAppService TagAppService { get; } |
||||
|
|
||||
|
public TagEditorViewComponent(ITagAppService tagAppService) |
||||
|
{ |
||||
|
TagAppService = tagAppService; |
||||
|
} |
||||
|
|
||||
|
public async Task<IViewComponentResult> InvokeAsync(string entityType, string entityId, bool displaySubmitButton = true) |
||||
|
{ |
||||
|
var tags = |
||||
|
entityId.IsNullOrWhiteSpace() ? |
||||
|
new List<TagDto>() : |
||||
|
await TagAppService.GetAllRelatedTagsAsync(entityType, entityId); |
||||
|
|
||||
|
return View("~/Pages/CmsKit/Tags/Components/TagEditor/Default.cshtml", new TagEditorViewModel |
||||
|
{ |
||||
|
EntityId = entityId, |
||||
|
EntityType = entityType, |
||||
|
Tags = tags, |
||||
|
DisplaySubmitButton = displaySubmitButton |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public class TagEditorViewModel |
||||
|
{ |
||||
|
public string EntityType { get; set; } |
||||
|
public string EntityId { get; set; } |
||||
|
public List<TagDto> Tags { get; set; } |
||||
|
public bool DisplaySubmitButton { get; set; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
$(function () { |
||||
|
|
||||
|
var $tagEditorForms = $('.tag-editor-form'); |
||||
|
|
||||
|
$tagEditorForms.on('submit', function (e) { |
||||
|
e.preventDefault(); |
||||
|
|
||||
|
var $form = $(e.currentTarget); |
||||
|
|
||||
|
if ($form.valid()) { |
||||
|
|
||||
|
abp.ui.setBusy(); |
||||
|
|
||||
|
var entityId = $form.data('entity-id'); |
||||
|
var entityType = $form.data('entity-type'); |
||||
|
var tags = $form.find('input').val().split(","); |
||||
|
|
||||
|
volo.cmsKit.admin.tags.entityTagAdmin |
||||
|
.setEntityTags({ |
||||
|
entityId: entityId, |
||||
|
entityType: entityType, |
||||
|
tags: tags |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
@ -0,0 +1,23 @@ |
|||||
|
@page |
||||
|
@using Microsoft.AspNetCore.Mvc.Localization |
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Tags |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal |
||||
|
@using Volo.CmsKit.Localization |
||||
|
|
||||
|
@inject IHtmlLocalizer<CmsKitResource> L |
||||
|
|
||||
|
@model CreateModalModel |
||||
|
|
||||
|
@{ |
||||
|
Layout = null; |
||||
|
} |
||||
|
|
||||
|
<abp-dynamic-form abp-model="ViewModel" asp-page="/CmsKit/Tags/CreateModal"> |
||||
|
<abp-modal> |
||||
|
<abp-modal-header title="@L["New"].Value"></abp-modal-header> |
||||
|
<abp-modal-body> |
||||
|
<abp-form-content/> |
||||
|
</abp-modal-body> |
||||
|
<abp-modal-footer buttons="@(AbpModalButtons.Cancel | AbpModalButtons.Save)"></abp-modal-footer> |
||||
|
</abp-modal> |
||||
|
</abp-dynamic-form> |
||||
@ -0,0 +1,58 @@ |
|||||
|
using AutoMapper; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.AspNetCore.Mvc.Rendering; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form; |
||||
|
using Volo.Abp.Validation; |
||||
|
using Volo.CmsKit.Admin.Tags; |
||||
|
using Volo.CmsKit.Tags; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.Tags |
||||
|
{ |
||||
|
public class CreateModalModel : CmsKitAdminPageModel |
||||
|
{ |
||||
|
protected ITagAdminAppService TagAdminAppService { get; } |
||||
|
|
||||
|
[BindProperty] |
||||
|
public TagCreateViewModel ViewModel { get; set; } |
||||
|
|
||||
|
public List<SelectListItem> TagDefinitions { get; set; } |
||||
|
|
||||
|
public CreateModalModel(ITagAdminAppService tagAdminAppService) |
||||
|
{ |
||||
|
TagAdminAppService = tagAdminAppService; |
||||
|
} |
||||
|
|
||||
|
public async Task OnGetAsync() |
||||
|
{ |
||||
|
var definitions = await TagAdminAppService.GetTagDefinitionsAsync(); |
||||
|
|
||||
|
TagDefinitions = definitions.Select(s => new SelectListItem(s.DisplayName, s.EntityType)).ToList(); |
||||
|
} |
||||
|
|
||||
|
public async Task<IActionResult> OnPostAsync() |
||||
|
{ |
||||
|
var tagCreateDto = ObjectMapper.Map<TagCreateViewModel, TagCreateDto>(ViewModel); |
||||
|
|
||||
|
await TagAdminAppService.CreateAsync(tagCreateDto); |
||||
|
|
||||
|
return NoContent(); |
||||
|
} |
||||
|
|
||||
|
[AutoMap(typeof(TagCreateDto), ReverseMap = true)] |
||||
|
public class TagCreateViewModel |
||||
|
{ |
||||
|
[DynamicMaxLength(typeof(TagConsts), nameof(TagConsts.MaxEntityTypeLength))] |
||||
|
[Required] |
||||
|
[SelectItems(nameof(TagDefinitions))] |
||||
|
public string EntityType { get; set; } |
||||
|
|
||||
|
[DynamicMaxLength(typeof(TagConsts), nameof(TagConsts.MaxNameLength))] |
||||
|
[Required] |
||||
|
public string Name { get; set; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
@page |
||||
|
@using Microsoft.AspNetCore.Mvc.Localization |
||||
|
@using Volo.CmsKit.Admin.Web.Pages |
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Tags |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal |
||||
|
@using Volo.CmsKit.Localization |
||||
|
|
||||
|
@inject IHtmlLocalizer<CmsKitResource> L |
||||
|
@model EditModalModel |
||||
|
|
||||
|
@{ |
||||
|
Layout = null; |
||||
|
} |
||||
|
|
||||
|
<abp-dynamic-form abp-model="ViewModel" asp-page="/CmsKit/Tags/EditModal"> |
||||
|
<abp-modal> |
||||
|
<abp-modal-header title="@L["Update"].Value"></abp-modal-header> |
||||
|
<abp-modal-body> |
||||
|
<abp-input asp-for="Id" /> |
||||
|
<abp-form-content /> |
||||
|
</abp-modal-body> |
||||
|
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer> |
||||
|
</abp-modal> |
||||
|
</abp-dynamic-form> |
||||
@ -0,0 +1,54 @@ |
|||||
|
using AutoMapper; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Linq; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Validation; |
||||
|
using Volo.CmsKit.Admin.Tags; |
||||
|
using Volo.CmsKit.Tags; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.Tags |
||||
|
{ |
||||
|
public class EditModalModel : CmsKitAdminPageModel |
||||
|
{ |
||||
|
protected ITagAdminAppService TagAdminAppService { get; } |
||||
|
|
||||
|
[HiddenInput] |
||||
|
[BindProperty(SupportsGet = true)] |
||||
|
public Guid Id { get; set; } |
||||
|
|
||||
|
[BindProperty] |
||||
|
public TagEditViewModel ViewModel { get; set; } |
||||
|
|
||||
|
public EditModalModel(ITagAdminAppService tagAdminAppService) |
||||
|
{ |
||||
|
this.TagAdminAppService = tagAdminAppService; |
||||
|
} |
||||
|
|
||||
|
public async Task OnGetAsync() |
||||
|
{ |
||||
|
var dto = await TagAdminAppService.GetAsync(Id); |
||||
|
|
||||
|
ViewModel = ObjectMapper.Map<TagDto, TagEditViewModel>(dto); |
||||
|
} |
||||
|
|
||||
|
public async Task<IActionResult> OnPostAsync() |
||||
|
{ |
||||
|
var tagDto = ObjectMapper.Map<TagEditViewModel, TagUpdateDto>(ViewModel); |
||||
|
await TagAdminAppService.UpdateAsync(Id, tagDto); |
||||
|
return NoContent(); |
||||
|
} |
||||
|
|
||||
|
[AutoMap(typeof(TagDto))] |
||||
|
[AutoMap(typeof(TagUpdateDto), ReverseMap = true)] |
||||
|
public class TagEditViewModel |
||||
|
{ |
||||
|
[Required] |
||||
|
[DynamicMaxLength(typeof(TagConsts), nameof(TagConsts.MaxNameLength))] |
||||
|
public string Name { get; set; } |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
@page |
||||
|
|
||||
|
@using Volo.CmsKit.Admin.Web.Pages |
||||
|
@using Volo.CmsKit.Admin.Web.Pages.CmsKit.Tags |
||||
|
@using Volo.CmsKit.Admin.Web.Menus |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageToolbar |
||||
|
@using Volo.Abp.AspNetCore.Mvc.UI.Theme.Shared.Pages.Shared.Components.AbpPageSearchBox |
||||
|
|
||||
|
@inherits CmsKitAdminPageBase |
||||
|
|
||||
|
@model IndexModel |
||||
|
|
||||
|
@{ |
||||
|
PageLayout.Content.Title = L["Tags"].Value; |
||||
|
PageLayout.Content.BreadCrumb.Add(L["Menu:CMS"].Value); |
||||
|
PageLayout.Content.MenuItemName = CmsKitAdminMenus.Tags.TagsMenu; |
||||
|
} |
||||
|
@section scripts { |
||||
|
<abp-script src="/Pages/CmsKit/Tags/Index.js" /> |
||||
|
} |
||||
|
|
||||
|
@section content_toolbar { |
||||
|
@await Component.InvokeAsync(typeof(AbpPageToolbarViewComponent), new { pageName = typeof(IndexModel).FullName }) |
||||
|
} |
||||
|
|
||||
|
<div id="CmsKitTagsWrapper"> |
||||
|
<abp-card> |
||||
|
<abp-card-body> |
||||
|
<abp-row> |
||||
|
<abp-column> |
||||
|
@await Component.InvokeAsync(typeof(AbpPageSearchBoxViewComponent)) |
||||
|
</abp-column> |
||||
|
</abp-row> |
||||
|
</abp-card-body> |
||||
|
</abp-card> |
||||
|
<abp-table striped-rows="true" id="TagsTable"></abp-table> |
||||
|
</div> |
||||
|
|
||||
@ -0,0 +1,8 @@ |
|||||
|
using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages.CmsKit.Tags |
||||
|
{ |
||||
|
public class IndexModel : CmsKitAdminPageModel |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,83 @@ |
|||||
|
$(function () { |
||||
|
var l = abp.localization.getResource("CmsKit"); |
||||
|
|
||||
|
var createModal = new abp.ModalManager(abp.appPath + "CmsKit/Tags/CreateModal"); |
||||
|
var updateModal = new abp.ModalManager(abp.appPath + "CmsKit/Tags/EditModal"); |
||||
|
|
||||
|
var service = volo.cmsKit.admin.tags.tagAdmin; |
||||
|
|
||||
|
var getFilter = function () { |
||||
|
return { |
||||
|
filter: $('#CmsKitTagsWrapper input.page-search-filter-text').val() |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
let dataTable = $("#TagsTable").DataTable(abp.libs.datatables.normalizeConfiguration({ |
||||
|
processing: true, |
||||
|
serverSide: true, |
||||
|
paging: true, |
||||
|
searching: false, |
||||
|
autoWidth: false, |
||||
|
scrollCollapse: true, |
||||
|
scrollX: true, |
||||
|
ordering: false, |
||||
|
ajax: abp.libs.datatables.createAjax(service.getList, getFilter), |
||||
|
columnDefs: [ |
||||
|
{ |
||||
|
title: l("Actions"), |
||||
|
rowAction: { |
||||
|
items: [ |
||||
|
{ |
||||
|
text: l("Edit"), |
||||
|
visible: abp.auth.isGranted('CmsKit.Tags.Update'), |
||||
|
action: function (data) { |
||||
|
updateModal.open({ id: data.record.id }); |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
text: l("Delete"), |
||||
|
visible: abp.auth.isGranted('CmsKit.Tags.Delete'), |
||||
|
confirmMessage: function (data) { |
||||
|
return l("TagDeletionConfirmationMessage", data.record.name) |
||||
|
}, |
||||
|
action: function (data) { |
||||
|
service |
||||
|
.delete(data.record.id) |
||||
|
.then(function () { |
||||
|
abp.notify.info(l("SuccessfullyDeleted")); |
||||
|
dataTable.ajax.reload(null, false); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
title: l("EntityType"), |
||||
|
data: "entityType" |
||||
|
}, |
||||
|
{ |
||||
|
title: l("Name"), |
||||
|
data: "name" |
||||
|
} |
||||
|
] |
||||
|
})); |
||||
|
|
||||
|
$('#CmsKitTagsWrapper form.page-search-form').on('submit', function (e) { |
||||
|
e.preventDefault(); |
||||
|
dataTable.ajax.reload(); |
||||
|
}); |
||||
|
|
||||
|
$('#AbpContentToolbar button[name=NewButton]').on('click', function (e) { |
||||
|
e.preventDefault(); |
||||
|
createModal.open(); |
||||
|
}); |
||||
|
|
||||
|
createModal.onResult(function () { |
||||
|
dataTable.ajax.reload(); |
||||
|
}); |
||||
|
|
||||
|
updateModal.onResult(function () { |
||||
|
dataTable.ajax.reload(); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,4 @@ |
|||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers |
||||
|
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI |
||||
|
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bootstrap |
||||
|
@addTagHelper *, Volo.Abp.AspNetCore.Mvc.UI.Bundling |
||||
@ -0,0 +1,20 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc.Razor.Internal; |
||||
|
using Microsoft.Extensions.Localization; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.AspNetCore.Mvc.UI.Layout; |
||||
|
using Volo.CmsKit.Localization; |
||||
|
|
||||
|
namespace Volo.CmsKit.Admin.Web.Pages |
||||
|
{ |
||||
|
public class CmsKitAdminPageBase : Microsoft.AspNetCore.Mvc.RazorPages.Page |
||||
|
{ |
||||
|
[RazorInject] public IStringLocalizer<CmsKitResource> L { get; set; } |
||||
|
|
||||
|
[RazorInject] public IPageLayout PageLayout { get; set; } |
||||
|
|
||||
|
public override Task ExecuteAsync() |
||||
|
{ |
||||
|
return Task.CompletedTask; // Will be overriden by razor pages. (.cshtml)
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,10 +1,11 @@ |
|||||
@page "/blogs/{blogSlug}" |
@page |
||||
|
|
||||
@using Volo.CmsKit.Public.Web.Pages |
@using Volo.CmsKit.Public.Web.Pages |
||||
|
@using Volo.CmsKit.Public.Web.Pages.Public.CmsKit.Blogs |
||||
|
|
||||
@inherits CmsKitPublicPageBase |
@inherits CmsKitPublicPageBase |
||||
|
|
||||
@model Volo.CmsKit.Public.Web.Pages.CmsKit.Blogs.IndexModel |
@model IndexModel |
||||
|
|
||||
<abp-row id="blogs-container"> |
<abp-row id="blogs-container"> |
||||
@foreach (var blog in Model.Blogs.Items) |
@foreach (var blog in Model.Blogs.Items) |
||||
Loading…
Reference in new issue