mirror of https://github.com/abpframework/abp.git
19 changed files with 621 additions and 14 deletions
@ -0,0 +1,41 @@ |
|||
<abp-page [title]="'CmsKit::BlogPosts' | abpLocalization"> |
|||
<div class="card"> |
|||
<div class="card-body"> |
|||
@if (form && (!isEditMode || blogPost)) { |
|||
<form [formGroup]="form" (ngSubmit)="saveAsDraft()" validateOnSubmit> |
|||
<!-- Basic fields --> |
|||
<abp-extensible-form [selectedRecord]="blogPost || {}" /> |
|||
|
|||
<!-- Content editor --> |
|||
<div class="mt-3"> |
|||
<label class="form-label">{{ 'CmsKit::Content' | abpLocalization }}</label> |
|||
<abp-toastui-editor formControlName="content" /> |
|||
</div> |
|||
</form> |
|||
} @else { |
|||
<div class="text-center"> |
|||
<i class="fa fa-pulse fa-spinner" aria-hidden="true"></i> |
|||
</div> |
|||
} |
|||
</div> |
|||
<div class="card-footer"> |
|||
<div class="d-flex justify-content-start gap-2"> |
|||
<button class="btn btn-secondary" (click)="saveAsDraft()" [disabled]="form?.invalid"> |
|||
{{ 'CmsKit::SaveAsDraft' | abpLocalization }} |
|||
</button> |
|||
@if (isEditMode) { |
|||
<abp-button (click)="publish()" [disabled]="form?.invalid"> |
|||
{{ 'CmsKit::Publish' | abpLocalization }} |
|||
</abp-button> |
|||
} @else { |
|||
<abp-button (click)="publish()" [disabled]="form?.invalid"> |
|||
{{ 'CmsKit::Publish' | abpLocalization }} |
|||
</abp-button> |
|||
<abp-button (click)="sendToReview()" [disabled]="form?.invalid"> |
|||
{{ 'CmsKit::SendToReviewToPublish' | abpLocalization }} |
|||
</abp-button> |
|||
} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</abp-page> |
|||
@ -0,0 +1,117 @@ |
|||
import { Component, OnInit, inject, Injector, DestroyRef } from '@angular/core'; |
|||
import { CommonModule } from '@angular/common'; |
|||
import { ReactiveFormsModule, FormGroup, FormControl } from '@angular/forms'; |
|||
import { ActivatedRoute } from '@angular/router'; |
|||
import { NgxValidateCoreModule } from '@ngx-validate/core'; |
|||
import { LocalizationPipe } from '@abp/ng.core'; |
|||
import { |
|||
ExtensibleFormComponent, |
|||
FormPropData, |
|||
generateFormFromProps, |
|||
EXTENSIONS_IDENTIFIER, |
|||
} from '@abp/ng.components/extensible'; |
|||
import { PageComponent } from '@abp/ng.components/page'; |
|||
import { ButtonComponent } from '@abp/ng.theme.shared'; |
|||
import { ToastuiEditorComponent, prepareSlugFromControl } from '@abp/ng.cms-kit'; |
|||
import { BlogPostAdminService, BlogPostDto } from '@abp/ng.cms-kit/proxy'; |
|||
import { eCmsKitAdminComponents } from '../../../enums'; |
|||
import { BlogPostFormService } from '../../../services'; |
|||
|
|||
@Component({ |
|||
selector: 'abp-blog-post-form', |
|||
templateUrl: './blog-post-form.component.html', |
|||
providers: [ |
|||
{ |
|||
provide: EXTENSIONS_IDENTIFIER, |
|||
useValue: eCmsKitAdminComponents.BlogPostForm, |
|||
}, |
|||
], |
|||
imports: [ |
|||
ButtonComponent, |
|||
ExtensibleFormComponent, |
|||
PageComponent, |
|||
ToastuiEditorComponent, |
|||
LocalizationPipe, |
|||
ReactiveFormsModule, |
|||
CommonModule, |
|||
NgxValidateCoreModule, |
|||
], |
|||
}) |
|||
export class BlogPostFormComponent implements OnInit { |
|||
private blogPostService = inject(BlogPostAdminService); |
|||
private injector = inject(Injector); |
|||
private blogPostFormService = inject(BlogPostFormService); |
|||
private route = inject(ActivatedRoute); |
|||
private destroyRef = inject(DestroyRef); |
|||
|
|||
form: FormGroup; |
|||
blogPost: BlogPostDto | null = null; |
|||
blogPostId: string | null = null; |
|||
isEditMode = false; |
|||
|
|||
ngOnInit() { |
|||
const id = this.route.snapshot.params['id']; |
|||
if (id) { |
|||
this.isEditMode = true; |
|||
this.blogPostId = id; |
|||
this.loadBlogPost(id); |
|||
} else { |
|||
this.isEditMode = false; |
|||
this.buildForm(); |
|||
} |
|||
} |
|||
|
|||
private loadBlogPost(id: string) { |
|||
this.blogPostService.get(id).subscribe(blogPost => { |
|||
this.blogPost = blogPost; |
|||
this.buildForm(); |
|||
}); |
|||
} |
|||
|
|||
private buildForm() { |
|||
const data = new FormPropData(this.injector, this.blogPost || {}); |
|||
const baseForm = generateFormFromProps(data); |
|||
this.form = new FormGroup({ |
|||
...baseForm.controls, |
|||
content: new FormControl(this.blogPost?.content || ''), |
|||
}); |
|||
prepareSlugFromControl(this.form, 'title', 'slug', this.destroyRef); |
|||
} |
|||
|
|||
private executeSaveOperation(operation: 'save' | 'draft' | 'publish' | 'sendToReview') { |
|||
if (this.isEditMode) { |
|||
if (!this.blogPost || !this.blogPostId) { |
|||
return; |
|||
} |
|||
this.blogPostFormService.update(this.blogPostId, this.form, this.blogPost).subscribe(); |
|||
return; |
|||
} |
|||
|
|||
switch (operation) { |
|||
case 'save': |
|||
this.blogPostFormService.createAsDraft(this.form).subscribe(); |
|||
break; |
|||
case 'draft': |
|||
this.blogPostFormService.createAsDraft(this.form).subscribe(); |
|||
break; |
|||
case 'publish': |
|||
this.blogPostFormService.createAndPublish(this.form).subscribe(); |
|||
break; |
|||
case 'sendToReview': |
|||
this.blogPostFormService.createAndSendToReview(this.form).subscribe(); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
saveAsDraft() { |
|||
this.executeSaveOperation('draft'); |
|||
} |
|||
|
|||
publish() { |
|||
this.executeSaveOperation('publish'); |
|||
} |
|||
|
|||
sendToReview() { |
|||
this.executeSaveOperation('sendToReview'); |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
<abp-page [title]="'CmsKit::BlogPosts' | abpLocalization" [toolbar]="data.items"> |
|||
<div class="card mb-4"> |
|||
<div class="card-body"> |
|||
<div class="row"> |
|||
<div class="col-2"> |
|||
<select class="form-select" [(ngModel)]="statusFilter" (change)="onStatusChange()"> |
|||
<option [ngValue]="null">{{ 'CmsKit::AllPosts' | abpLocalization }}</option> |
|||
<option [ngValue]="BlogPostStatus.Draft"> |
|||
{{ 'CmsKit::' + 'CmsKit.BlogPost.Status.0' | abpLocalization }} |
|||
</option> |
|||
<option [ngValue]="BlogPostStatus.Published"> |
|||
{{ 'CmsKit::' + 'CmsKit.BlogPost.Status.1' | abpLocalization }} |
|||
</option> |
|||
<option [ngValue]="BlogPostStatus.WaitingForReview"> |
|||
{{ 'CmsKit::' + 'CmsKit.BlogPost.Status.2' | abpLocalization }} |
|||
</option> |
|||
</select> |
|||
</div> |
|||
<div class="col-10"> |
|||
<div class="input-group"> |
|||
<input |
|||
type="search" |
|||
class="form-control" |
|||
[placeholder]="'AbpUi::PagerSearch' | abpLocalization" |
|||
[(ngModel)]="filter" |
|||
(keyup.enter)="onSearch()" |
|||
/> |
|||
<button class="btn btn-primary" type="button" (click)="onSearch()"> |
|||
<i class="fa fa-search" aria-hidden="true"></i> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="card"> |
|||
<abp-extensible-table [data]="data.items" [recordsTotal]="data.totalCount" [list]="list" /> |
|||
</div> |
|||
</abp-page> |
|||
@ -0,0 +1,70 @@ |
|||
import { Component, OnInit, inject } from '@angular/core'; |
|||
import { CommonModule } from '@angular/common'; |
|||
import { FormsModule } from '@angular/forms'; |
|||
import { ListService, PagedResultDto, LocalizationPipe } from '@abp/ng.core'; |
|||
import { ExtensibleTableComponent, EXTENSIONS_IDENTIFIER } from '@abp/ng.components/extensible'; |
|||
import { PageComponent } from '@abp/ng.components/page'; |
|||
import { |
|||
BlogPostAdminService, |
|||
BlogPostGetListInput, |
|||
BlogPostListDto, |
|||
BlogPostStatus, |
|||
} from '@abp/ng.cms-kit/proxy'; |
|||
import { eCmsKitAdminComponents } from '../../../enums'; |
|||
|
|||
@Component({ |
|||
selector: 'abp-blog-post-list', |
|||
templateUrl: './blog-post-list.component.html', |
|||
providers: [ |
|||
ListService, |
|||
{ |
|||
provide: EXTENSIONS_IDENTIFIER, |
|||
useValue: eCmsKitAdminComponents.BlogPosts, |
|||
}, |
|||
], |
|||
imports: [ExtensibleTableComponent, PageComponent, LocalizationPipe, FormsModule, CommonModule], |
|||
}) |
|||
export class BlogPostListComponent implements OnInit { |
|||
data: PagedResultDto<BlogPostListDto> = { items: [], totalCount: 0 }; |
|||
|
|||
public readonly list = inject(ListService<BlogPostGetListInput>); |
|||
private blogPostService = inject(BlogPostAdminService); |
|||
|
|||
filter = ''; |
|||
statusFilter: BlogPostStatus | null = null; |
|||
BlogPostStatus = BlogPostStatus; |
|||
|
|||
ngOnInit() { |
|||
this.hookToQuery(); |
|||
} |
|||
|
|||
onSearch() { |
|||
this.list.filter = this.filter; |
|||
this.list.get(); |
|||
} |
|||
|
|||
onStatusChange() { |
|||
this.list.get(); |
|||
} |
|||
|
|||
private hookToQuery() { |
|||
this.list |
|||
.hookToQuery(query => { |
|||
let filters: Partial<BlogPostGetListInput> = {}; |
|||
if (this.list.filter) { |
|||
filters.filter = this.list.filter; |
|||
} |
|||
if (this.statusFilter !== null) { |
|||
filters.status = this.statusFilter; |
|||
} |
|||
const input: BlogPostGetListInput = { |
|||
...query, |
|||
...filters, |
|||
}; |
|||
return this.blogPostService.getList(input); |
|||
}) |
|||
.subscribe(res => { |
|||
this.data = res; |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,2 @@ |
|||
export * from './blog-post-list/blog-post-list.component'; |
|||
export * from './blog-post-form/blog-post-form.component'; |
|||
@ -0,0 +1,51 @@ |
|||
import { Validators } from '@angular/forms'; |
|||
import { map } from 'rxjs/operators'; |
|||
import { CreateBlogPostDto, BlogAdminService } from '@abp/ng.cms-kit/proxy'; |
|||
import { FormProp, ePropType } from '@abp/ng.components/extensible'; |
|||
|
|||
export const DEFAULT_BLOG_POST_CREATE_FORM_PROPS = FormProp.createMany<CreateBlogPostDto>([ |
|||
{ |
|||
type: ePropType.Enum, |
|||
name: 'blogId', |
|||
displayName: 'CmsKit::Blog', |
|||
id: 'blogId', |
|||
options: data => { |
|||
const blogService = data.getInjected(BlogAdminService); |
|||
return blogService.getList({ maxResultCount: 1000 }).pipe( |
|||
map(result => |
|||
result.items.map(blog => ({ |
|||
key: blog.name || '', |
|||
value: blog.id || '', |
|||
})), |
|||
), |
|||
); |
|||
}, |
|||
validators: () => [Validators.required], |
|||
}, |
|||
{ |
|||
type: ePropType.String, |
|||
name: 'title', |
|||
displayName: 'CmsKit::Title', |
|||
id: 'title', |
|||
validators: () => [Validators.required], |
|||
}, |
|||
{ |
|||
type: ePropType.String, |
|||
name: 'slug', |
|||
displayName: 'CmsKit::Slug', |
|||
id: 'slug', |
|||
validators: () => [Validators.required], |
|||
tooltip: { |
|||
text: 'CmsKit::BlogPostSlugInformation', |
|||
}, |
|||
}, |
|||
{ |
|||
type: ePropType.String, |
|||
name: 'shortDescription', |
|||
displayName: 'CmsKit::ShortDescription', |
|||
id: 'shortDescription', |
|||
}, |
|||
]); |
|||
|
|||
export const DEFAULT_BLOG_POST_EDIT_FORM_PROPS: FormProp<any>[] = |
|||
DEFAULT_BLOG_POST_CREATE_FORM_PROPS; |
|||
@ -0,0 +1,38 @@ |
|||
import { Router } from '@angular/router'; |
|||
import { ListService } from '@abp/ng.core'; |
|||
import { EntityAction } from '@abp/ng.components/extensible'; |
|||
import { ConfirmationService, Confirmation } from '@abp/ng.theme.shared'; |
|||
import { BlogPostAdminService, BlogPostListDto } from '@abp/ng.cms-kit/proxy'; |
|||
|
|||
export const DEFAULT_BLOG_POST_ENTITY_ACTIONS = EntityAction.createMany<BlogPostListDto>([ |
|||
{ |
|||
text: 'AbpUi::Edit', |
|||
action: data => { |
|||
const router = data.getInjected(Router); |
|||
router.navigate(['/cms/blog-posts/update', data.record.id]); |
|||
}, |
|||
permission: 'CmsKit.BlogPosts.Update', |
|||
}, |
|||
{ |
|||
text: 'AbpUi::Delete', |
|||
action: data => { |
|||
const blogPostService = data.getInjected(BlogPostAdminService); |
|||
const confirmationService = data.getInjected(ConfirmationService); |
|||
const list = data.getInjected(ListService); |
|||
|
|||
confirmationService |
|||
.warn('CmsKit::BlogPostDeletionConfirmationMessage', 'AbpUi::AreYouSure', { |
|||
yesText: 'AbpUi::Yes', |
|||
cancelText: 'AbpUi::Cancel', |
|||
}) |
|||
.subscribe((status: Confirmation.Status) => { |
|||
if (status === Confirmation.Status.confirm) { |
|||
blogPostService.delete(data.record.id!).subscribe(() => { |
|||
list.get(); |
|||
}); |
|||
} |
|||
}); |
|||
}, |
|||
permission: 'CmsKit.BlogPosts.Delete', |
|||
}, |
|||
]); |
|||
@ -0,0 +1,65 @@ |
|||
import { of } from 'rxjs'; |
|||
import { BlogPostListDto, BlogPostStatus } from '@abp/ng.cms-kit/proxy'; |
|||
import { EntityProp, ePropType } from '@abp/ng.components/extensible'; |
|||
import { LocalizationService } from '@abp/ng.core'; |
|||
|
|||
export const DEFAULT_BLOG_POST_ENTITY_PROPS = EntityProp.createMany<BlogPostListDto>([ |
|||
{ |
|||
type: ePropType.String, |
|||
name: 'blogName', |
|||
displayName: 'CmsKit::Blog', |
|||
sortable: true, |
|||
columnWidth: 150, |
|||
}, |
|||
{ |
|||
type: ePropType.String, |
|||
name: 'title', |
|||
displayName: 'CmsKit::Title', |
|||
sortable: true, |
|||
columnWidth: 200, |
|||
}, |
|||
{ |
|||
type: ePropType.String, |
|||
name: 'slug', |
|||
displayName: 'CmsKit::Slug', |
|||
sortable: true, |
|||
columnWidth: 200, |
|||
}, |
|||
{ |
|||
type: ePropType.String, |
|||
name: 'status', |
|||
displayName: 'CmsKit::Status', |
|||
sortable: true, |
|||
columnWidth: 120, |
|||
valueResolver: data => { |
|||
const localization = data.getInjected(LocalizationService); |
|||
let result = ''; |
|||
switch (data.record.status) { |
|||
case BlogPostStatus.Draft: |
|||
result = localization.instant('CmsKit::CmsKit.BlogPost.Status.0'); |
|||
break; |
|||
case BlogPostStatus.Published: |
|||
result = localization.instant('CmsKit::CmsKit.BlogPost.Status.1'); |
|||
break; |
|||
case BlogPostStatus.WaitingForReview: |
|||
result = localization.instant('CmsKit::CmsKit.BlogPost.Status.2'); |
|||
break; |
|||
} |
|||
return of(result); |
|||
}, |
|||
}, |
|||
{ |
|||
type: ePropType.Date, |
|||
name: 'creationTime', |
|||
displayName: 'AbpIdentity::CreationTime', |
|||
sortable: true, |
|||
columnWidth: 200, |
|||
}, |
|||
{ |
|||
type: ePropType.Date, |
|||
name: 'lastModificationTime', |
|||
displayName: 'AbpIdentity::LastModificationTime', |
|||
sortable: true, |
|||
columnWidth: 200, |
|||
}, |
|||
]); |
|||
@ -0,0 +1,15 @@ |
|||
import { Router } from '@angular/router'; |
|||
import { BlogPostListDto } from '@abp/ng.cms-kit/proxy'; |
|||
import { ToolbarAction } from '@abp/ng.components/extensible'; |
|||
|
|||
export const DEFAULT_BLOG_POST_TOOLBAR_ACTIONS = ToolbarAction.createMany<BlogPostListDto[]>([ |
|||
{ |
|||
text: 'CmsKit::NewBlogPost', |
|||
action: data => { |
|||
const router = data.getInjected(Router); |
|||
router.navigate(['/cms/blog-posts/create']); |
|||
}, |
|||
permission: 'CmsKit.BlogPosts.Create', |
|||
icon: 'fa fa-plus', |
|||
}, |
|||
]); |
|||
@ -0,0 +1,4 @@ |
|||
export * from './default-blog-post-entity-actions'; |
|||
export * from './default-blog-post-entity-props'; |
|||
export * from './default-blog-post-toolbar-actions'; |
|||
export * from './default-blog-post-create-form-props'; |
|||
@ -0,0 +1,92 @@ |
|||
import { Injectable, inject } from '@angular/core'; |
|||
import { Router } from '@angular/router'; |
|||
import { FormGroup } from '@angular/forms'; |
|||
import { Observable } from 'rxjs'; |
|||
import { tap } from 'rxjs/operators'; |
|||
import { ToasterService } from '@abp/ng.theme.shared'; |
|||
import { |
|||
BlogPostAdminService, |
|||
CreateBlogPostDto, |
|||
UpdateBlogPostDto, |
|||
BlogPostDto, |
|||
BlogPostStatus, |
|||
} from '@abp/ng.cms-kit/proxy'; |
|||
|
|||
@Injectable({ |
|||
providedIn: 'root', |
|||
}) |
|||
export class BlogPostFormService { |
|||
private blogPostService = inject(BlogPostAdminService); |
|||
private toasterService = inject(ToasterService); |
|||
private router = inject(Router); |
|||
|
|||
create(form: FormGroup): Observable<BlogPostDto> { |
|||
if (!form.valid) { |
|||
throw new Error('Form is invalid'); |
|||
} |
|||
|
|||
return this.blogPostService.create(form.value as CreateBlogPostDto).pipe( |
|||
tap(() => { |
|||
this.toasterService.success('AbpUi::SavedSuccessfully'); |
|||
this.router.navigate(['/cms/blog-posts']); |
|||
}), |
|||
); |
|||
} |
|||
|
|||
createAsDraft(form: FormGroup): Observable<BlogPostDto> { |
|||
if (!form.valid) { |
|||
throw new Error('Form is invalid'); |
|||
} |
|||
|
|||
return this.blogPostService.create(form.value as CreateBlogPostDto).pipe( |
|||
tap(() => { |
|||
this.toasterService.success('AbpUi::SavedSuccessfully'); |
|||
this.router.navigate(['/cms/blog-posts']); |
|||
}), |
|||
); |
|||
} |
|||
|
|||
createAndPublish(form: FormGroup): Observable<BlogPostDto> { |
|||
if (!form.valid) { |
|||
throw new Error('Form is invalid'); |
|||
} |
|||
|
|||
return this.blogPostService.createAndPublish(form.value as CreateBlogPostDto).pipe( |
|||
tap(() => { |
|||
this.toasterService.success('AbpUi::SavedSuccessfully'); |
|||
this.router.navigate(['/cms/blog-posts']); |
|||
}), |
|||
); |
|||
} |
|||
|
|||
createAndSendToReview(form: FormGroup): Observable<BlogPostDto> { |
|||
if (!form.valid) { |
|||
throw new Error('Form is invalid'); |
|||
} |
|||
|
|||
return this.blogPostService.createAndSendToReview(form.value as CreateBlogPostDto).pipe( |
|||
tap(() => { |
|||
this.toasterService.success('AbpUi::SavedSuccessfully'); |
|||
this.router.navigate(['/cms/blog-posts']); |
|||
}), |
|||
); |
|||
} |
|||
|
|||
update(blogPostId: string, form: FormGroup, blogPost: BlogPostDto): Observable<BlogPostDto> { |
|||
if (!form.valid || !blogPost) { |
|||
throw new Error('Form is invalid or blog post is missing'); |
|||
} |
|||
|
|||
const formValue = { |
|||
...blogPost, |
|||
...form.value, |
|||
} as UpdateBlogPostDto; |
|||
|
|||
return this.blogPostService.update(blogPostId, formValue).pipe( |
|||
tap(() => { |
|||
this.toasterService.success('AbpUi::SavedSuccessfully'); |
|||
this.router.navigate(['/cms/blog-posts']); |
|||
}), |
|||
); |
|||
} |
|||
} |
|||
@ -1,8 +1,2 @@ |
|||
// Services will be exported here
|
|||
// export * from './comments';
|
|||
// export * from './tags';
|
|||
export * from './page-form.service'; |
|||
// export * from './blogs';
|
|||
// export * from './blog-posts';
|
|||
// export * from './menus';
|
|||
// export * from './global-resources';
|
|||
export * from './blog-post-form.service'; |
|||
|
|||
Loading…
Reference in new issue