mirror of https://github.com/abpframework/abp.git
108 changed files with 1426 additions and 340 deletions
@ -0,0 +1,236 @@ |
|||
# Moving Background Job Execution To A Separate Application |
|||
|
|||
In this article, I will show you how to move the background job execution to a separate application. |
|||
|
|||
Here are some benefits of doing this: |
|||
|
|||
* If your background jobs consume high system resources (CPU, RAM or Disk), then you can deploy that background application to a dedicated server so it won't affect your application's performance. |
|||
* You can scale your background job application independently from your web application. For example, you can deploy multiple instances of your background job application to a Kubernetes cluster and scale it easily. |
|||
|
|||
Here are some disadvantages of doing this: |
|||
|
|||
* You need to deploy and maintain at least two applications instead of one. |
|||
* You need to implement a mechanism to share the common code between your applications. For example, you can create a shared project and add it to your applications as a project reference. |
|||
|
|||
## Source code |
|||
|
|||
You can find the source code of the application at [abpframework/abp-samples](https://github.com/abpframework/abp-samples/tree/master/SeparateBackgroundJob). |
|||
|
|||
You can check the PR to see the changes step by step: [abpframework/abp-samples#250](https://github.com/abpframework/abp-samples/pull/250) |
|||
|
|||
## Creating the Web Application |
|||
|
|||
First, we need to create a new web application using the ABP CLI: |
|||
|
|||
```bash |
|||
abp new SeparateBackgroundJob -t app |
|||
``` |
|||
|
|||
* Create a shared project named `SeparateBackgroundJob.Common.Shared` to share the `BackgroundJob` and `BackgroundJobArgs` classes between the web and job executor applications. |
|||
* Install the `Volo.Abp.BackgroundJobs.Abstractions` package to the `SeparateBackgroundJob.Common.Shared` project. |
|||
|
|||
Add the `SeparateBackgroundJobCommonSharedModule` class to the `SeparateBackgroundJob.Common.Shared` project: |
|||
|
|||
```csharp |
|||
[DependsOn(typeof(AbpBackgroundJobsAbstractionsModule))] |
|||
public class SeparateBackgroundJobCommonSharedModule : AbpModule |
|||
{ |
|||
} |
|||
``` |
|||
|
|||
Add the `MyReportJob` and `MyReportJobArgs` classes to the `SeparateBackgroundJob.Common.Shared` project: |
|||
|
|||
```csharp |
|||
public class MyReportJob : AsyncBackgroundJob<MyReportJobArgs>, ITransientDependency |
|||
{ |
|||
public override Task ExecuteAsync(MyReportJobArgs args) |
|||
{ |
|||
Logger.LogInformation("Executing MyReportJob with args: {0}", args.Content); |
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
|
|||
public class MyReportJobArgs |
|||
{ |
|||
public string? Content { get; set; } |
|||
} |
|||
``` |
|||
|
|||
Add the `SeparateBackgroundJob.Common.Shared` project reference to the `SeparateBackgroundJob.Domain` project and add `SeparateBackgroundJobCommonSharedModule` to the `DependsOn` attribute of the `SeparateBackgroundJobDomainModule` class: |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
typeof(SeparateBackgroundJobDomainSharedModule), |
|||
typeof(AbpAuditLoggingDomainModule), |
|||
typeof(AbpBackgroundJobsDomainModule), |
|||
typeof(AbpFeatureManagementDomainModule), |
|||
typeof(AbpIdentityDomainModule), |
|||
typeof(AbpOpenIddictDomainModule), |
|||
typeof(AbpPermissionManagementDomainOpenIddictModule), |
|||
typeof(AbpPermissionManagementDomainIdentityModule), |
|||
typeof(AbpSettingManagementDomainModule), |
|||
typeof(AbpTenantManagementDomainModule), |
|||
typeof(AbpEmailingModule), |
|||
typeof(SeparateBackgroundJobCommonSharedModule) //Add this line |
|||
)] |
|||
public class SeparateBackgroundJobDomainModule : AbpModule |
|||
``` |
|||
|
|||
Open the `Index.cshtml` and replace the content with the following code: |
|||
|
|||
```csharp |
|||
@page |
|||
@using Microsoft.AspNetCore.Mvc.Localization |
|||
@using SeparateBackgroundJob.Localization |
|||
@using Volo.Abp.Users |
|||
@model SeparateBackgroundJob.Web.Pages.IndexModel |
|||
@inject IHtmlLocalizer<SeparateBackgroundJobResource> L |
|||
@inject ICurrentUser CurrentUser |
|||
|
|||
@section styles { |
|||
<abp-style src="/Pages/Index.css"/> |
|||
} |
|||
|
|||
@section scripts { |
|||
<abp-script src="/Pages/Index.js"/> |
|||
} |
|||
|
|||
<div class="container"> |
|||
<abp-card> |
|||
<abp-card-header> |
|||
<abp-card-title> |
|||
Add NEW BACKGROUND JOB |
|||
</abp-card-title> |
|||
</abp-card-header> |
|||
<abp-card-body> |
|||
<form id="NewItemForm" method="post" class="row row-cols-lg-auto g-3 align-items-center"> |
|||
<div class="col-12"> |
|||
<div class="input-group"> |
|||
<input id="ReportContent" required name="ReportContent" type="text" class="form-control" placeholder="enter text..."> |
|||
</div> |
|||
</div> |
|||
<div class="col-12"> |
|||
<button type="submit" class="btn btn-primary">Add</button> |
|||
</div> |
|||
</form> |
|||
</abp-card-body> |
|||
</abp-card> |
|||
</div> |
|||
``` |
|||
|
|||
Open the `Index.cshtml.cs` and replace the content with the following code: |
|||
|
|||
```csharp |
|||
public class IndexModel : SeparateBackgroundJobPageModel |
|||
{ |
|||
private readonly IBackgroundJobManager _backgroundJobManager; |
|||
|
|||
[BindProperty(SupportsGet = true)] |
|||
public string? ReportContent { get; set; } |
|||
|
|||
public IndexModel(IBackgroundJobManager backgroundJobManager) |
|||
{ |
|||
_backgroundJobManager = backgroundJobManager; |
|||
} |
|||
|
|||
public void OnGet() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public async Task OnPostAsync() |
|||
{ |
|||
await _backgroundJobManager.EnqueueAsync(new MyReportJobArgs |
|||
{ |
|||
Content = ReportContent |
|||
}); |
|||
|
|||
Alerts.Success("Job is queued!"); |
|||
} |
|||
} |
|||
``` |
|||
|
|||
Run the application and navigate to the home page. You should see the following page: |
|||
|
|||
 |
|||
|
|||
When you enter some text and click the **Add** button, the job will be queued and executed in the web application: |
|||
|
|||
## Creating the Console Application |
|||
|
|||
Now we split the background job execution to a separate console application. |
|||
|
|||
Open the `SeparateBackgroundJobWebModule` class to disable the background job execution in the web application: |
|||
|
|||
```csharp |
|||
public class SeparateBackgroundJobWebModule : AbpModule |
|||
{ |
|||
.... |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
... |
|||
|
|||
//Disable background job execution in the web application |
|||
Configure<AbpBackgroundJobOptions>(options => |
|||
{ |
|||
options.IsJobExecutionEnabled = false; |
|||
}); |
|||
} |
|||
|
|||
... |
|||
} |
|||
``` |
|||
|
|||
* Create a new console application using the ABP CLI: |
|||
|
|||
```bash |
|||
abp new BackgroundJobExecutor -t console |
|||
``` |
|||
|
|||
* Add the `BackgroundJobExecutor` project to the solution of the web application. |
|||
* Add the `SeparateBackgroundJob.Common.Shared` project reference to the `BackgroundJobExecutor` project. |
|||
* Install the `Volo.Abp.BackgroundJobs.EntityFrameworkCore` and `Volo.Abp.EntityFrameworkCore.SqlServer` packages to the `BackgroundJobExecutor` project. |
|||
|
|||
Update the `BackgroundJobExecutorModule` class as follows: |
|||
|
|||
```csharp |
|||
[DependsOn( |
|||
typeof(AbpAutofacModule), |
|||
typeof(AbpBackgroundJobsEntityFrameworkCoreModule), |
|||
typeof(AbpEntityFrameworkCoreSqlServerModule), |
|||
typeof(SeparateBackgroundJobCommonSharedModule) |
|||
)] |
|||
public class BackgroundJobExecutorModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpDbContextOptions>(options => |
|||
{ |
|||
options.UseSqlServer(); |
|||
}); |
|||
} |
|||
|
|||
.... |
|||
} |
|||
``` |
|||
|
|||
Open the `appsettings.json` file to configure the [connection string](https://docs.abp.io/en/abp/latest/Connection-Strings#configure-the-connection-strings): |
|||
|
|||
```json |
|||
{ |
|||
"ConnectionStrings": { |
|||
"AbpBackgroundJobs": "Server=(LocalDb)\\MSSQLLocalDB;Database=SeparateBackgroundJob;Trusted_Connection=True" |
|||
} |
|||
} |
|||
``` |
|||
|
|||
> You must use the same connection string for the web application, `AbpBackgroundJobs` is the default connection string name for the background job module. |
|||
|
|||
The solution structure should look like this: |
|||
|
|||
 |
|||
|
|||
Now, run the web and console application. When you enter some text and click the **Add** button, the job will be queued and executed in the console application: |
|||
|
|||
 |
|||
|
After Width: | Height: | Size: 140 KiB |
|
After Width: | Height: | Size: 376 KiB |
|
After Width: | Height: | Size: 102 KiB |
@ -0,0 +1,12 @@ |
|||
using System.Collections.Generic; |
|||
using Volo.Abp.AspNetCore.Mvc.UI.Bundling; |
|||
|
|||
namespace Volo.Abp.AspNetCore.Mvc.UI.Packages.Zxcvbn; |
|||
|
|||
public class ZxcvbnScriptContributor : BundleContributor |
|||
{ |
|||
public override void ConfigureBundle(BundleConfigurationContext context) |
|||
{ |
|||
context.Files.AddIfNotContains("/libs/zxcvbn/zxcvbn.js"); |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using Volo.Abp.Cli.ProjectBuilding.Building; |
|||
|
|||
namespace Volo.Abp.Cli.ProjectBuilding.Templates; |
|||
|
|||
public class CheckRedisPreRequirements : ProjectBuildPipelineStep |
|||
{ |
|||
public override void Execute(ProjectBuildContext context) |
|||
{ |
|||
var modules = context.Files.Where(f => f.Name.EndsWith("Module.cs", StringComparison.OrdinalIgnoreCase)); |
|||
if (modules.Any(module => module.Content.Contains("Redis:Configuration"))) |
|||
{ |
|||
context.BuildArgs.ExtraProperties["PreRequirements:Redis"] = "true"; |
|||
} |
|||
} |
|||
} |
|||
@ -1,228 +1,229 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"AddSubMenuItem": "Add Sub Menu Item", |
|||
"AreYouSure": "Are You Sure?", |
|||
"BlogDeletionConfirmationMessage": "The blog '{0}' will be deleted. Are you sure?", |
|||
"BlogFeatureNotAvailable": "This feature is not available now. Enable with 'GlobalFeatureManager' to use it.", |
|||
"BlogId": "Blog", |
|||
"BlogPostDeletionConfirmationMessage": "The blog post '{0}' will be deleted. Are you sure?", |
|||
"BlogPosts": "Blog Posts", |
|||
"Blogs": "Blogs", |
|||
"ChoosePreference": "Choose Preference...", |
|||
"Cms": "CMS", |
|||
"CmsKit.Comments": "Comments", |
|||
"CmsKit.Ratings": "Ratings", |
|||
"CmsKit.Reactions": "Reactions", |
|||
"CmsKit.Tags": "Tags", |
|||
"CmsKit:0002": "Content already exists!", |
|||
"CmsKit:0003": "The entity {0} is not taggable.", |
|||
"CmsKit:Blog:0001": "The given slug ({Slug}) already exists!", |
|||
"CmsKit:BlogPost:0001": "The given slug already exists!", |
|||
"CmsKit:Comments:0001": "The entity {EntityType} is not commentable.", |
|||
"CmsKit:Media:0001": "'{Name}' is not a valid media name.", |
|||
"CmsKit:Media:0002": "The entity can't have media.", |
|||
"CmsKit:Page:0001": "The given url ({Slug}) already exists. Please try with different url.", |
|||
"CmsKit:Rating:0001": "The entity {EntityType} can't be rated.", |
|||
"CmsKit:Reaction:0001": "The entity {EntityType} can't have reactions.", |
|||
"CmsKit:Tag:0002": "The entity is not taggable!", |
|||
"CommentAuthorizationExceptionMessage": "Those comments are not allowed for public display.", |
|||
"CommentDeletionConfirmationMessage": "This comment and all replies will be deleted!", |
|||
"Comments": "Comments", |
|||
"Content": "Content", |
|||
"ContentDeletionConfirmationMessage": "Are you sure to delete this content?", |
|||
"Contents": "Contents", |
|||
"CoverImage": "Cover Image", |
|||
"CreateBlogPostPage": "New Blog Post", |
|||
"CreationTime": "Creation Time", |
|||
"Delete": "Delete", |
|||
"Detail": "Detail", |
|||
"Details": "Details", |
|||
"DisplayName": "Display Name", |
|||
"DoYouPreferAdditionalEmails": "Do you prefer additional emails?", |
|||
"Edit": "Edit", |
|||
"EndDate": "End Date", |
|||
"EntityId": "Entity Id", |
|||
"EntityType": "Entity Type", |
|||
"ExportCSV": "Export CSV", |
|||
"Features": "Features", |
|||
"GenericDeletionConfirmationMessage": "Are you sure to delete '{0}'?", |
|||
"IsActive": "Active", |
|||
"LastModification": "Last Modification", |
|||
"LastModificationTime": "Last Modification Time", |
|||
"LoginToAddComment": "Login to add comment", |
|||
"LoginToRate": "Login to rate", |
|||
"LoginToReact": "Login to react", |
|||
"LoginToReply": "Login to reply", |
|||
"MainMenu": "Main Menu", |
|||
"MakeMainMenu": "Make Main Menu", |
|||
"Menu:CMS": "CMS", |
|||
"Menus": "Menus", |
|||
"MenuDeletionConfirmationMessage": "The menu '{0}' will be deleted. Are you sure?", |
|||
"MenuItemDeletionConfirmationMessage": "Are sure to delete this menu item?", |
|||
"MenuItemMoveConfirmMessage": "Are you sure you want to move '{0}' under '{1}'?", |
|||
"MenuItems": "Menu Items", |
|||
"Message": "Message", |
|||
"MessageDeletionConfirmationMessage": "This comment will be deleted completely.", |
|||
"NewBlog": "New Blog", |
|||
"NewBlogPost": "New Blog Post", |
|||
"NewMenu": "New Menu", |
|||
"NewMenuItem": "New Root Menu Item", |
|||
"NewPage": "New Page", |
|||
"NewTag": "New Tag", |
|||
"NoMenuItems": "There is no menu item yet!", |
|||
"OK": "OK", |
|||
"PageDeletionConfirmationMessage": "Are you sure to delete this page?", |
|||
"PageId": "Page", |
|||
"Pages": "Pages", |
|||
"PageSlugInformation": "Slug is used on url. Your url will be '/{{slug}}'.", |
|||
"Permission:BlogManagement": "Blog Management", |
|||
"Permission:BlogManagement.Create": "Create", |
|||
"Permission:BlogManagement.Delete": "Delete", |
|||
"Permission:BlogManagement.Features": "Features", |
|||
"Permission:BlogManagement.Update": "Update", |
|||
"Permission:BlogPostManagement": "Blog Post Management", |
|||
"Permission:BlogPostManagement.Create": "Create", |
|||
"Permission:BlogPostManagement.Delete": "Delete", |
|||
"Permission:BlogPostManagement.Update": "Update", |
|||
"Permission:BlogPostManagement.Publish": "Publish", |
|||
"Permission:CmsKit": "CmsKit Admin", |
|||
"Permission:Comments": "Comment Management", |
|||
"Permission:Comments.Delete": "Delete", |
|||
"Permission:Contents": "Content Management", |
|||
"Permission:Contents.Create": "Create Content", |
|||
"Permission:Contents.Delete": "Delete Content", |
|||
"Permission:Contents.Update": "Update Content", |
|||
"Permission:MediaDescriptorManagement": "Media Management", |
|||
"Permission:MediaDescriptorManagement:Create": "Create", |
|||
"Permission:MediaDescriptorManagement:Delete": "Delete", |
|||
"Permission:MenuItemManagement": "Menu Item Management", |
|||
"Permission:MenuItemManagement.Create": "Create", |
|||
"Permission:MenuItemManagement.Delete": "Delete", |
|||
"Permission:MenuItemManagement.Update": "Update", |
|||
"Permission:MenuManagement": "Menu Management", |
|||
"Permission:MenuManagement.Create": "Create", |
|||
"Permission:MenuManagement.Delete": "Delete", |
|||
"Permission:MenuManagement.Update": "Update", |
|||
"Permission:Menus": "Menu Management", |
|||
"Permission:Menus.Create": "Create", |
|||
"Permission:Menus.Delete": "Delete", |
|||
"Permission:Menus.Update": "Update", |
|||
"Permission:PageManagement": "Page Management", |
|||
"Permission:PageManagement:Create": "Create", |
|||
"Permission:PageManagement:Delete": "Delete", |
|||
"Permission:PageManagement:Update": "Update", |
|||
"Permission:PageManagement:SetAsHomePage": "Set As Home Page", |
|||
"Permission:TagManagement": "Tag Management", |
|||
"Permission:TagManagement.Create": "Create", |
|||
"Permission:TagManagement.Delete": "Delete", |
|||
"Permission:TagManagement.Update": "Update", |
|||
"Permission:GlobalResources": "Global Resources", |
|||
"Permission:CmsKitPublic": "CmsKit Public", |
|||
"Permission:Comments.DeleteAll": "Delete All", |
|||
"PickYourReaction": "Pick your reaction", |
|||
"Rating": "Rating", |
|||
"RatingUndoMessage": "Your rating will be undo.", |
|||
"Reactions": "Reactions", |
|||
"Read": "Read", |
|||
"RepliesToThisComment": "Replies to this comment", |
|||
"Reply": "Reply", |
|||
"ReplyTo": "Reply to", |
|||
"SamplePageMessage": "A sample page for the Pro module", |
|||
"SaveChanges": "Save Changes", |
|||
"Script": "Script", |
|||
"SelectAll": "Select All", |
|||
"Send": "Send", |
|||
"SendMessage": "Send Message", |
|||
"SelectedAuthor": "Author", |
|||
"ShortDescription": "Short description", |
|||
"Slug": "Slug", |
|||
"Source": "Source", |
|||
"SourceUrl": "Source Url", |
|||
"Star": "Star", |
|||
"StartDate": "Start Date", |
|||
"Style": "Style", |
|||
"Subject": "Subject", |
|||
"SubjectPlaceholder": "Please type a subject", |
|||
"Submit": "Submit", |
|||
"Subscribe": "Subscribe", |
|||
"SuccessfullySaved": "Successfully saved!", |
|||
"TagDeletionConfirmationMessage": "Are you sure to delete '{0}' tag?", |
|||
"Tags": "Tags", |
|||
"Text": "Text", |
|||
"ThankYou": "Thank you", |
|||
"Title": "Title", |
|||
"Undo": "Undo", |
|||
"Update": "Update", |
|||
"UpdatePreferenceSuccessMessage": "Your preferences have been saved.", |
|||
"UpdateYourEmailPreferences": "Update your email preferences", |
|||
"UnMakeMainMenu": "Unmake Main Menu", |
|||
"UploadFailedMessage": "Upload failed.", |
|||
"UserId": "User Id", |
|||
"Username": "Username", |
|||
"YourComment": "Your comment", |
|||
"YourEmailAddress": "Your e-mail address", |
|||
"YourFullName": "Your full name", |
|||
"YourMessage": "Your Message", |
|||
"YourReply": "Your reply", |
|||
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> supported.", |
|||
"GlobalResources": "Global Resources", |
|||
"SavedSuccessfully": "Saved successfully", |
|||
"CmsKit.BlogPost.Status.0": "Draft", |
|||
"CmsKit.BlogPost.Status.1": "Published", |
|||
"CmsKit.BlogPost.Status.2": "Waiting for review", |
|||
"BlogPostPublishConfirmationMessage": "Are you sure to publish the blog post \"{0}\"?", |
|||
"SuccessfullyPublished": "Successfully published!", |
|||
"Draft": "Draft", |
|||
"Publish": "Publish", |
|||
"BlogPostDraftConfirmationMessage": "Are you sure to set the blog post \"{0}\" as draft?", |
|||
"BlogPostSendToReviewConfirmationMessage": "Are you sure to send the blog post \"{0}\" to admin review for publishing?", |
|||
"SaveAsDraft": "Save as draft", |
|||
"SendToReview": "Send to review", |
|||
"SendToReviewToPublish": "Send to review to publish", |
|||
"BlogPostSendToReviewSuccessMessage": "The blog post \"{0}\" has been sent to admin review for publishing.", |
|||
"HasBlogPostWaitingForReviewMessage": "You have a blog post waiting for review. Click to list.", |
|||
"SelectAStatus": "Select a status", |
|||
"Status": "Status", |
|||
"CmsKit.BlogPost.ScrollIndex": "Quick navigation bar in blog posts", |
|||
"Add": "Add", |
|||
"AddWidget": "Add Widget", |
|||
"PleaseConfigureWidgets": "Please configure widgets", |
|||
"SelectAnAuthor": "Select an Author", |
|||
"InThisDocument": "In This Document", |
|||
"GoToTop": "Go To Top", |
|||
"SetAsHomePage": "Change Home Page Status", |
|||
"CompletedSettingAsHomePage": "Set as home page", |
|||
"IsHomePage": "Is Home Page", |
|||
"RemovedSettingAsHomePage": "Removed setting the home page", |
|||
"Feature:CmsKitGroup": "Cms Kit", |
|||
"Feature:BlogEnable": "Blogpost", |
|||
"Feature:BlogEnableDescription": "CMS Kit's blogpost system that allows create blogs and posts dynamically in the application.", |
|||
"Feature:CommentEnable": "Commenting", |
|||
"Feature:CommentEnableDescription": "CMS Kit's comment system allows commenting on entities such as BlogPost.", |
|||
"Feature:GlobalResourceEnable": "Global resourcing", |
|||
"Feature:GlobalResourceEnableDescription": "CMS Kit's global resoruces feature that allows managing global styles & scripts.", |
|||
"Feature:MenuEnable": "Menu", |
|||
"Feature:MenuEnableDescription": "CMS Kit's dynamic menu system that allows adding/removing application menus dynamically.", |
|||
"Feature:PageEnable": "Paging", |
|||
"Feature:PageEnableDescription": "CMS Kit's page system that allows creating static pages with specific URL.", |
|||
"Feature:RatingEnable": "Rating", |
|||
"Feature:RatingEnableDescription": "CMS Kit's rating system that allows users to rate entities such as BlogPost.", |
|||
"Feature:ReactionEnable": "Reaction", |
|||
"Feature:ReactionEnableDescription": "CMS Kit's reaction system that allows users to send reactions to entities such as BlogPost, Comments, etc.", |
|||
"Feature:TagEnable": "Taging", |
|||
"Feature:TagEnableDescription": "CMS Kit's tag system that allows tagging entities such as BlogPost.", |
|||
"DeleteBlogPostMessage": "The blog will be deleted. Are you sure?", |
|||
"CaptchaCode": "Captcha code", |
|||
"CommentTextRequired": "Comment is required", |
|||
"CaptchaCodeErrorMessage": "The answer you entered for the CAPTCHA was not correct. Please try again", |
|||
"CaptchaCodeMissingMessage": "The captcha code is missing!", |
|||
"UnAllowedExternalUrlMessage": "You included an unallowed external URL. Please try again without the external URL.", |
|||
"URL": "URL", |
|||
"PopularTags": "Popular Tags", |
|||
"RemoveCoverImageConfirmationMessage": "Are you sure you want to remove the cover image?", |
|||
"RemoveCoverImage": "Remove cover image", |
|||
"CssClass": "CSS Class", |
|||
"TagsHelpText": "Tags should be comma-separated (e.g.: tag1, tag2, tag3)" |
|||
} |
|||
"texts": { |
|||
"AddSubMenuItem": "Add Sub Menu Item", |
|||
"AreYouSure": "Are You Sure?", |
|||
"BlogDeletionConfirmationMessage": "The blog '{0}' will be deleted. Are you sure?", |
|||
"BlogFeatureNotAvailable": "This feature is not available now. Enable with 'GlobalFeatureManager' to use it.", |
|||
"BlogId": "Blog", |
|||
"BlogPostDeletionConfirmationMessage": "The blog post '{0}' will be deleted. Are you sure?", |
|||
"BlogPosts": "Blog Posts", |
|||
"Blogs": "Blogs", |
|||
"ChoosePreference": "Choose Preference...", |
|||
"Cms": "CMS", |
|||
"CmsKit.Comments": "Comments", |
|||
"CmsKit.Ratings": "Ratings", |
|||
"CmsKit.Reactions": "Reactions", |
|||
"CmsKit.Tags": "Tags", |
|||
"CmsKit:0002": "Content already exists!", |
|||
"CmsKit:0003": "The entity {0} is not taggable.", |
|||
"CmsKit:Blog:0001": "The given slug ({Slug}) already exists!", |
|||
"CmsKit:BlogPost:0001": "The given slug already exists!", |
|||
"CmsKit:Comments:0001": "The entity {EntityType} is not commentable.", |
|||
"CmsKit:Media:0001": "'{Name}' is not a valid media name.", |
|||
"CmsKit:Media:0002": "The entity can't have media.", |
|||
"CmsKit:Page:0001": "The given url ({Slug}) already exists. Please try with different url.", |
|||
"CmsKit:Rating:0001": "The entity {EntityType} can't be rated.", |
|||
"CmsKit:Reaction:0001": "The entity {EntityType} can't have reactions.", |
|||
"CmsKit:Tag:0002": "The entity is not taggable!", |
|||
"CommentAuthorizationExceptionMessage": "Those comments are not allowed for public display.", |
|||
"CommentDeletionConfirmationMessage": "This comment and all replies will be deleted!", |
|||
"Comments": "Comments", |
|||
"Content": "Content", |
|||
"ContentDeletionConfirmationMessage": "Are you sure to delete this content?", |
|||
"Contents": "Contents", |
|||
"CoverImage": "Cover Image", |
|||
"CreateBlogPostPage": "New Blog Post", |
|||
"CreationTime": "Creation Time", |
|||
"Delete": "Delete", |
|||
"Detail": "Detail", |
|||
"Details": "Details", |
|||
"DisplayName": "Display Name", |
|||
"DoYouPreferAdditionalEmails": "Do you prefer additional emails?", |
|||
"Edit": "Edit", |
|||
"EndDate": "End Date", |
|||
"EntityId": "Entity Id", |
|||
"EntityType": "Entity Type", |
|||
"ExportCSV": "Export CSV", |
|||
"Features": "Features", |
|||
"GenericDeletionConfirmationMessage": "Are you sure to delete '{0}'?", |
|||
"IsActive": "Active", |
|||
"LastModification": "Last Modification", |
|||
"LastModificationTime": "Last Modification Time", |
|||
"LoginToAddComment": "Login to add comment", |
|||
"LoginToRate": "Login to rate", |
|||
"LoginToReact": "Login to react", |
|||
"LoginToReply": "Login to reply", |
|||
"MainMenu": "Main Menu", |
|||
"MakeMainMenu": "Make Main Menu", |
|||
"Menu:CMS": "CMS", |
|||
"Menus": "Menus", |
|||
"MenuDeletionConfirmationMessage": "The menu '{0}' will be deleted. Are you sure?", |
|||
"MenuItemDeletionConfirmationMessage": "Are sure to delete this menu item?", |
|||
"MenuItemMoveConfirmMessage": "Are you sure you want to move '{0}' under '{1}'?", |
|||
"MenuItems": "Menu Items", |
|||
"Message": "Message", |
|||
"MessageDeletionConfirmationMessage": "This comment will be deleted completely.", |
|||
"NewBlog": "New Blog", |
|||
"NewBlogPost": "New Blog Post", |
|||
"NewMenu": "New Menu", |
|||
"NewMenuItem": "New Root Menu Item", |
|||
"NewPage": "New Page", |
|||
"NewTag": "New Tag", |
|||
"NoMenuItems": "There is no menu item yet!", |
|||
"OK": "OK", |
|||
"PageDeletionConfirmationMessage": "Are you sure to delete this page?", |
|||
"PageId": "Page", |
|||
"Pages": "Pages", |
|||
"PageSlugInformation": "Slug is used on url. Your url will be '/{{slug}}'.", |
|||
"Permission:BlogManagement": "Blog Management", |
|||
"Permission:BlogManagement.Create": "Create", |
|||
"Permission:BlogManagement.Delete": "Delete", |
|||
"Permission:BlogManagement.Features": "Features", |
|||
"Permission:BlogManagement.Update": "Update", |
|||
"Permission:BlogPostManagement": "Blog Post Management", |
|||
"Permission:BlogPostManagement.Create": "Create", |
|||
"Permission:BlogPostManagement.Delete": "Delete", |
|||
"Permission:BlogPostManagement.Update": "Update", |
|||
"Permission:BlogPostManagement.Publish": "Publish", |
|||
"Permission:CmsKit": "CmsKit Admin", |
|||
"Permission:Comments": "Comment Management", |
|||
"Permission:Comments.Delete": "Delete", |
|||
"Permission:Contents": "Content Management", |
|||
"Permission:Contents.Create": "Create Content", |
|||
"Permission:Contents.Delete": "Delete Content", |
|||
"Permission:Contents.Update": "Update Content", |
|||
"Permission:MediaDescriptorManagement": "Media Management", |
|||
"Permission:MediaDescriptorManagement:Create": "Create", |
|||
"Permission:MediaDescriptorManagement:Delete": "Delete", |
|||
"Permission:MenuItemManagement": "Menu Item Management", |
|||
"Permission:MenuItemManagement.Create": "Create", |
|||
"Permission:MenuItemManagement.Delete": "Delete", |
|||
"Permission:MenuItemManagement.Update": "Update", |
|||
"Permission:MenuManagement": "Menu Management", |
|||
"Permission:MenuManagement.Create": "Create", |
|||
"Permission:MenuManagement.Delete": "Delete", |
|||
"Permission:MenuManagement.Update": "Update", |
|||
"Permission:Menus": "Menu Management", |
|||
"Permission:Menus.Create": "Create", |
|||
"Permission:Menus.Delete": "Delete", |
|||
"Permission:Menus.Update": "Update", |
|||
"Permission:PageManagement": "Page Management", |
|||
"Permission:PageManagement:Create": "Create", |
|||
"Permission:PageManagement:Delete": "Delete", |
|||
"Permission:PageManagement:Update": "Update", |
|||
"Permission:PageManagement:SetAsHomePage": "Set As Home Page", |
|||
"Permission:TagManagement": "Tag Management", |
|||
"Permission:TagManagement.Create": "Create", |
|||
"Permission:TagManagement.Delete": "Delete", |
|||
"Permission:TagManagement.Update": "Update", |
|||
"Permission:GlobalResources": "Global Resources", |
|||
"Permission:CmsKitPublic": "CmsKit Public", |
|||
"Permission:Comments.DeleteAll": "Delete All", |
|||
"PickYourReaction": "Pick your reaction", |
|||
"Rating": "Rating", |
|||
"RatingUndoMessage": "Your rating will be undo.", |
|||
"Reactions": "Reactions", |
|||
"Read": "Read", |
|||
"RepliesToThisComment": "Replies to this comment", |
|||
"Reply": "Reply", |
|||
"ReplyTo": "Reply to", |
|||
"SamplePageMessage": "A sample page for the Pro module", |
|||
"SaveChanges": "Save Changes", |
|||
"Script": "Script", |
|||
"SelectAll": "Select All", |
|||
"Send": "Send", |
|||
"SendMessage": "Send Message", |
|||
"SelectedAuthor": "Author", |
|||
"ShortDescription": "Short description", |
|||
"Slug": "Slug", |
|||
"Source": "Source", |
|||
"SourceUrl": "Source Url", |
|||
"Star": "Star", |
|||
"StartDate": "Start Date", |
|||
"Style": "Style", |
|||
"Subject": "Subject", |
|||
"SubjectPlaceholder": "Please type a subject", |
|||
"Submit": "Submit", |
|||
"Subscribe": "Subscribe", |
|||
"SuccessfullySaved": "Successfully saved!", |
|||
"TagDeletionConfirmationMessage": "Are you sure to delete '{0}' tag?", |
|||
"Tags": "Tags", |
|||
"Text": "Text", |
|||
"ThankYou": "Thank you", |
|||
"Title": "Title", |
|||
"Undo": "Undo", |
|||
"Update": "Update", |
|||
"UpdatePreferenceSuccessMessage": "Your preferences have been saved.", |
|||
"UpdateYourEmailPreferences": "Update your email preferences", |
|||
"UnMakeMainMenu": "Unmake Main Menu", |
|||
"UploadFailedMessage": "Upload failed.", |
|||
"UserId": "User Id", |
|||
"Username": "Username", |
|||
"YourComment": "Your comment", |
|||
"YourEmailAddress": "Your e-mail address", |
|||
"YourFullName": "Your full name", |
|||
"YourMessage": "Your Message", |
|||
"YourReply": "Your reply", |
|||
"MarkdownSupported": "<a href=\"https://www.markdownguide.org/basic-syntax/\">Markdown</a> supported.", |
|||
"GlobalResources": "Global Resources", |
|||
"SavedSuccessfully": "Saved successfully", |
|||
"CmsKit.BlogPost.Status.0": "Draft", |
|||
"CmsKit.BlogPost.Status.1": "Published", |
|||
"CmsKit.BlogPost.Status.2": "Waiting for review", |
|||
"BlogPostPublishConfirmationMessage": "Are you sure to publish the blog post \"{0}\"?", |
|||
"SuccessfullyPublished": "Successfully published!", |
|||
"Draft": "Draft", |
|||
"Publish": "Publish", |
|||
"BlogPostDraftConfirmationMessage": "Are you sure to set the blog post \"{0}\" as draft?", |
|||
"BlogPostSendToReviewConfirmationMessage": "Are you sure to send the blog post \"{0}\" to admin review for publishing?", |
|||
"SaveAsDraft": "Save as draft", |
|||
"SendToReview": "Send to review", |
|||
"SendToReviewToPublish": "Send to review to publish", |
|||
"BlogPostSendToReviewSuccessMessage": "The blog post \"{0}\" has been sent to admin review for publishing.", |
|||
"HasBlogPostWaitingForReviewMessage": "You have a blog post waiting for review. Click to list.", |
|||
"SelectAStatus": "Select a status", |
|||
"Status": "Status", |
|||
"CmsKit.BlogPost.ScrollIndex": "Quick navigation bar in blog posts", |
|||
"CmsKit.BlogPost.PreventXssFeature": "Prevent XSS", |
|||
"Add": "Add", |
|||
"AddWidget": "Add Widget", |
|||
"PleaseConfigureWidgets": "Please configure widgets", |
|||
"SelectAnAuthor": "Select an Author", |
|||
"InThisDocument": "In This Document", |
|||
"GoToTop": "Go To Top", |
|||
"SetAsHomePage": "Change Home Page Status", |
|||
"CompletedSettingAsHomePage": "Set as home page", |
|||
"IsHomePage": "Is Home Page", |
|||
"RemovedSettingAsHomePage": "Removed setting the home page", |
|||
"Feature:CmsKitGroup": "Cms Kit", |
|||
"Feature:BlogEnable": "Blogpost", |
|||
"Feature:BlogEnableDescription": "CMS Kit's blogpost system that allows create blogs and posts dynamically in the application.", |
|||
"Feature:CommentEnable": "Commenting", |
|||
"Feature:CommentEnableDescription": "CMS Kit's comment system allows commenting on entities such as BlogPost.", |
|||
"Feature:GlobalResourceEnable": "Global resourcing", |
|||
"Feature:GlobalResourceEnableDescription": "CMS Kit's global resoruces feature that allows managing global styles & scripts.", |
|||
"Feature:MenuEnable": "Menu", |
|||
"Feature:MenuEnableDescription": "CMS Kit's dynamic menu system that allows adding/removing application menus dynamically.", |
|||
"Feature:PageEnable": "Paging", |
|||
"Feature:PageEnableDescription": "CMS Kit's page system that allows creating static pages with specific URL.", |
|||
"Feature:RatingEnable": "Rating", |
|||
"Feature:RatingEnableDescription": "CMS Kit's rating system that allows users to rate entities such as BlogPost.", |
|||
"Feature:ReactionEnable": "Reaction", |
|||
"Feature:ReactionEnableDescription": "CMS Kit's reaction system that allows users to send reactions to entities such as BlogPost, Comments, etc.", |
|||
"Feature:TagEnable": "Taging", |
|||
"Feature:TagEnableDescription": "CMS Kit's tag system that allows tagging entities such as BlogPost.", |
|||
"DeleteBlogPostMessage": "The blog will be deleted. Are you sure?", |
|||
"CaptchaCode": "Captcha code", |
|||
"CommentTextRequired": "Comment is required", |
|||
"CaptchaCodeErrorMessage": "The answer you entered for the CAPTCHA was not correct. Please try again", |
|||
"CaptchaCodeMissingMessage": "The captcha code is missing!", |
|||
"UnAllowedExternalUrlMessage": "You included an unallowed external URL. Please try again without the external URL.", |
|||
"URL": "URL", |
|||
"PopularTags": "Popular Tags", |
|||
"RemoveCoverImageConfirmationMessage": "Are you sure you want to remove the cover image?", |
|||
"RemoveCoverImage": "Remove cover image", |
|||
"CssClass": "CSS Class", |
|||
"TagsHelpText": "Tags should be comma-separated (e.g.: tag1, tag2, tag3)" |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing'; |
|||
import { Tree, readProjectConfiguration } from '@nx/devkit'; |
|||
|
|||
import { changeThemeGenerator } from './generator'; |
|||
import { ChangeThemeGeneratorSchema } from './schema'; |
|||
|
|||
describe('change-theme generator', () => { |
|||
let tree: Tree; |
|||
const options: ChangeThemeGeneratorSchema = { name: 'test' }; |
|||
|
|||
beforeEach(() => { |
|||
tree = createTreeWithEmptyWorkspace(); |
|||
}); |
|||
|
|||
it('should run successfully', async () => { |
|||
await changeThemeGenerator(tree, options); |
|||
const config = readProjectConfiguration(tree, 'test'); |
|||
expect(config).toBeDefined(); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,26 @@ |
|||
import { Tree } from '@nx/devkit'; |
|||
import { wrapAngularDevkitSchematic } from '@nx/devkit/ngcli-adapter'; |
|||
import { ChangeThemeGeneratorSchema } from './schema'; |
|||
import { ThemeOptionsEnum } from './theme-options.enum'; |
|||
|
|||
export async function changeThemeGenerator(host: Tree, schema: ChangeThemeGeneratorSchema) { |
|||
const schematicPath = schema.localPath || '@abp/ng.schematics'; |
|||
|
|||
const runAngularLibrarySchematic = wrapAngularDevkitSchematic( |
|||
schema.localPath ? `${host.root}${schematicPath}` : schematicPath, |
|||
'change-theme', |
|||
); |
|||
|
|||
await runAngularLibrarySchematic(host, { |
|||
...schema, |
|||
}); |
|||
|
|||
return () => { |
|||
const destTheme = Object.values(ThemeOptionsEnum).find( |
|||
(theme, index) => index + 1 === schema.name, |
|||
); |
|||
console.log(`✅️ Switched to Theme ${destTheme}`); |
|||
}; |
|||
} |
|||
|
|||
export default changeThemeGenerator; |
|||
@ -0,0 +1,3 @@ |
|||
export * from './theme-options.enum'; |
|||
export * from './schema'; |
|||
export * from './generator'; |
|||
@ -0,0 +1,5 @@ |
|||
export interface ChangeThemeGeneratorSchema { |
|||
name: number; |
|||
targetOption: string; |
|||
localPath?: string; |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
{ |
|||
"$schema": "http://json-schema.org/schema", |
|||
"$id": "SchematicsABPThemeChanger", |
|||
"title": "ABP Theme Style Generator API Schema", |
|||
"type": "object", |
|||
"properties": { |
|||
"name": { |
|||
"description": "The name of theme will change.", |
|||
"type": "number", |
|||
"$default": { |
|||
"$source": "argv", |
|||
"index": 0 |
|||
}, |
|||
"enum": [1, 2, 3, 4], |
|||
"x-prompt": { |
|||
"message": "Which theme would you like to use?", |
|||
"type": "list", |
|||
"items": [ |
|||
{ "value": 1, "label": "Basic" }, |
|||
{ "value": 2, "label": "Lepton" }, |
|||
{ "value": 3, "label": "LeptonXLite" }, |
|||
{ "value": 4, "label": "LeptonX" } |
|||
] |
|||
} |
|||
}, |
|||
"targetProject": { |
|||
"description": "The name of the project will change the style.The project type must be 'application'", |
|||
"type": "string", |
|||
"x-prompt": "Please enter the project name", |
|||
"$default": { |
|||
"$source": "argv", |
|||
"index": 1 |
|||
} |
|||
}, |
|||
"localPath": { |
|||
"description": "If set value schematics will work on given path", |
|||
"type": "string" |
|||
} |
|||
}, |
|||
"required": ["name", "targetProject"] |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
// this enum create by https://raw.githubusercontent.com/abpframework/abp/rel-7.4/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectBuilding/Building/Theme.cs
|
|||
|
|||
export enum ThemeOptionsEnum { |
|||
Basic = 1, |
|||
Lepton = 2, |
|||
LeptonXLite = 3, |
|||
LeptonX = 4, |
|||
} |
|||
@ -1 +1,3 @@ |
|||
export * from './generators/generate-proxy' |
|||
export * from './generators/change-theme'; |
|||
export * from './generators/generate-proxy'; |
|||
export * from './generators/update-version'; |
|||
|
|||
@ -0,0 +1,129 @@ |
|||
## ℹ️ Description |
|||
|
|||
ABP Framework is a complete open-source infrastructure to create modern web applications by following the best practices and conventions of software development. This package is a part of the [ABP Framework](https://abp.io) and contains client-side files. |
|||
For more information, check out the below links: |
|||
|
|||
🔗Official Website: https://abp.io |
|||
|
|||
🔗Commercial Website: https://commercial.abp.io |
|||
|
|||
🔗Commercial Demo: https://commercial.abp.io/demo |
|||
|
|||
🔗GitHub Repository: https://github.com/abpframework/abp |
|||
|
|||
🔗Official Theme: https://www.LeptonTheme.com |
|||
|
|||
🔗Documentation: https://docs.abp.io |
|||
|
|||
🔗Community: https://community.abp.io |
|||
|
|||
🔗Blog: https://blog.abp.io |
|||
|
|||
🔗Books: https://abp.io/books |
|||
|
|||
🔗Twitter: https://twitter.com/abpframework |
|||
|
|||
🔗Discord: https://community.abp.io/discord |
|||
|
|||
🔗Stackoverflow: https://stackoverflow.com/questions/tagged/abp |
|||
|
|||
🔗YouTube: https://www.youtube.com/@Volosoft |
|||
|
|||
|
|||
## 🤔 Why ABP Platform? |
|||
|
|||
Why should you use the ABP.IO Platform instead of creating a new solution from scratch? |
|||
|
|||
You can find the answer here 👉🏻 [Why ABP Platform?](https://docs.abp.io/en/commercial/latest/why-abp-io-platform) |
|||
|
|||
## 🚀 Key Features of the ABP Framework |
|||
|
|||
🟡 Modularity |
|||
|
|||
🟡 Multi-Tenancy |
|||
|
|||
🟡 Bootstrap Tag Helpers |
|||
|
|||
🟡 Dynamic Forms |
|||
|
|||
🟡 Authentication |
|||
|
|||
🟡 Authorization |
|||
|
|||
🟡 Distributed Event Bus |
|||
|
|||
🟡 BLOB Storing |
|||
|
|||
🟡 Text Templating |
|||
|
|||
🟡 Tooling: ABP CLI |
|||
|
|||
🟡 Cross-Cutting Concerns |
|||
|
|||
🟡 Bundling & Minification |
|||
|
|||
🟡 Virtual File System |
|||
|
|||
🟡 Theming |
|||
|
|||
🟡 Background Jobs |
|||
|
|||
🟡 DDD Infrastructure |
|||
|
|||
🟡 Auto REST APIs |
|||
|
|||
🟡 Dynamic Client Proxies |
|||
|
|||
🟡 Multiple Database Providers |
|||
|
|||
🟡 Data filtering |
|||
|
|||
🟡 Test Infrastructure |
|||
|
|||
🟡 Audit Logging |
|||
|
|||
🟡 Object to Object Mapping |
|||
|
|||
🟡 Email & SMS Abstractions |
|||
|
|||
🟡 Localization |
|||
|
|||
🟡 Setting Management |
|||
|
|||
🟡 Extension Methods |
|||
|
|||
🟡 Aspect Oriented Programming |
|||
|
|||
🟡 Dependency Injection |
|||
|
|||
|
|||
## 🧐 How It Works? |
|||
|
|||
The following page explains how you use the ABP.IO Platform as a .NET developer 👉 [How it works?](https://commercial.abp.io/how-it-works) |
|||
|
|||
|
|||
### 📘 Supported Database Providers |
|||
|
|||
🔵 Entity Framework Core |
|||
|
|||
🔵 MongoDB |
|||
|
|||
🔵 Dapper |
|||
|
|||
|
|||
### 🎴 Supported UI Frameworks |
|||
|
|||
🔵 Angular |
|||
|
|||
🔵 Razor Pages |
|||
|
|||
🔵 Blazor Web Assembly |
|||
|
|||
🔵 Blazor Server |
|||
|
|||
🔵 MAUI with Blazor Hybrid |
|||
|
|||
|
|||
## 📫 Bug & Support |
|||
|
|||
Support for open-source ABP Framework client-side packages is available at [GitHub Issues](https://github.com/abpframework/abp/issues), and the commercial support is available at [support.abp.io](https://support.abp.io). |
|||
@ -0,0 +1,5 @@ |
|||
module.exports = { |
|||
mappings: { |
|||
"@node_modules/zxcvbn/dist/zxcvbn.js": "@libs/zxcvbn/" |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
{ |
|||
"version": "7.4.0-rc.3", |
|||
"name": "@abp/zxcvbn", |
|||
"publishConfig": { |
|||
"access": "public" |
|||
}, |
|||
"dependencies": { |
|||
"@abp/core": "~7.4.0-rc.3", |
|||
"zxcvbn" : "^4.4.2" |
|||
}, |
|||
"gitHead": "bb4ea17d5996f01889134c138d00b6c8f858a431", |
|||
"homepage": "https://abp.io", |
|||
"repository": { |
|||
"type": "git", |
|||
"url": "https://github.com/abpframework/abp.git" |
|||
}, |
|||
"license": "LGPL-3.0", |
|||
"keywords": [ |
|||
"aspnetcore", |
|||
"boilerplate", |
|||
"framework", |
|||
"web", |
|||
"best-practices", |
|||
"angular", |
|||
"maui", |
|||
"blazor", |
|||
"mvc", |
|||
"csharp", |
|||
"webapp" |
|||
] |
|||
} |
|||
|
|||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue