diff --git a/docs/en/Authorization.md b/docs/en/Authorization.md index de89920946..1bceda951f 100644 --- a/docs/en/Authorization.md +++ b/docs/en/Authorization.md @@ -204,6 +204,10 @@ If you define and register a policy to the ASP.NET Core authorization system wit See [policy based authorization](https://docs.microsoft.com/en-us/aspnet/core/security/authorization/policies) document to learn how to define a custom policy. +### Changing Permission Definitions of a Depended Module + +A class deriving from the `PermissionDefinitionProvider` (just like the example above) can also get existing permission definitions (defined by the depended [modules](Module-Development-Basics.md)) and change their definitions. + ## IAuthorizationService ASP.NET Core provides the `IAuthorizationService` that can be used to check for authorization. Once you inject, you can use it in your code to conditionally control the authorization. diff --git a/docs/en/Customizing-Application-Modules-Guide.md b/docs/en/Customizing-Application-Modules-Guide.md index 9989c6fc09..7f913f366a 100644 --- a/docs/en/Customizing-Application-Modules-Guide.md +++ b/docs/en/Customizing-Application-Modules-Guide.md @@ -53,3 +53,10 @@ This section suggests some approaches if you decided to use pre-built applicatio * [Overriding Services](Customizing-Application-Modules-Overriding-Services.md) * [Overriding the User Interface](Customizing-Application-Modules-Overriding-User-Interface.md) +### See Also + +Also, see the following documents: + +* See [the localization document](Localization.md) to learn how to extend existing localization resources. +* See [the settings document](Settings.md) to learn how to change setting definitions of a depended module. +* See [the authorization document](Authorization.md) to learn how to change permission definitions of a depended module. \ No newline at end of file diff --git a/docs/en/Modules/Account.md b/docs/en/Modules/Account.md new file mode 100644 index 0000000000..6901b38f59 --- /dev/null +++ b/docs/en/Modules/Account.md @@ -0,0 +1,3 @@ +# Account Module + +TODO \ No newline at end of file diff --git a/docs/en/Themes/Basic.md b/docs/en/Themes/Basic.md new file mode 100644 index 0000000000..a0165c803a --- /dev/null +++ b/docs/en/Themes/Basic.md @@ -0,0 +1,3 @@ +## Basic Theme + +TODO \ No newline at end of file diff --git a/docs/en/Tutorials/Part-1.md b/docs/en/Tutorials/Part-1.md index 05fd29fd41..20b64f3189 100644 --- a/docs/en/Tutorials/Part-1.md +++ b/docs/en/Tutorials/Part-1.md @@ -727,7 +727,7 @@ $(function () { * `abp.libs.datatables.createAjax` is a helper function to adapt ABP's dynamic JavaScript API proxies to [Datatable](https://datatables.net/)'s format. * `abp.libs.datatables.normalizeConfiguration` is another helper function. There's no requirement to use it, but it simplifies the [Datatables](https://datatables.net/) configuration by providing conventional values for missing options. -* `acme.bookStore.book.getList` is the function to get list of books (as described in [dynamic JavaScript proxies](#Dynamic JavaScript proxies)). +* `acme.bookStore.book.getList` is the function to get list of books (as described in [dynamic JavaScript proxies](#dynamic-javascript-proxies)). * See [Datatables documentation](https://datatables.net/manual/) for all configuration options. It's end of this part. The final UI of this work is shown as below: diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md index 8408cf6bfb..08e17e0e3f 100644 --- a/docs/en/Tutorials/Part-2.md +++ b/docs/en/Tutorials/Part-2.md @@ -641,7 +641,6 @@ Open `book-list.component.html` file in `books\book-list` folder and replace the
= { + name: "Home", + path: "home", + children: [ + { + name: "Dashboard", + path: "dashboard" + } + ] +}; + +this.config.dispatchPatchRouteByName("::Menu:Home", newRouteConfig); +// returns a state stream which emits after dispatch action is complete +``` + +### How to Add a New Route Configuration + +The `dispatchAddRoute` adds a new route to the configuration state in the `Store`. For this, the route config should be passed as the parameter of the method. + +```js +// this.config is instance of ConfigStateService + +const newRoute: ABP.Route = { + name: "My New Page", + iconClass: "fa fa-dashboard", + path: "page", + invisible: false, + order: 2, + requiredPolicy: "MyProjectName::MyNewPage" +}; + +this.config.dispatchAddRoute(newRoute); +// returns a state stream which emits after dispatch action is complete +``` + +The `newRoute` will be placed as at root level, i.e. without any parent routes and its url will be stored as `'/path'`. + +If you want **to add a child route, you can do this:** + +```js +// this.config is instance of ConfigStateService + +const newRoute: ABP.Route = { + parentName: "AbpAccount::Login", + name: "My New Page", + iconClass: "fa fa-dashboard", + path: "page", + invisible: false, + order: 2, + requiredPolicy: "MyProjectName::MyNewPage" +}; + +this.config.dispatchAddRoute(newRoute); +// returns a state stream which emits after dispatch action is complete +``` + +The `newRoute` will then be placed as a child of the parent route named `'AbpAccount::Login'` and its url will be set as `'/account/login/page'`. + +#### Route Configuration Properties + +Please refer to `ABP.Route` type for all the properties you can pass to `dispatchSetEnvironment` in its parameter. It can be found in the [common.ts file](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/common.ts#L27). + +### How to Set the Environment + +The `dispatchSetEnvironment` places environment variables passed to it in the `Store` under the configuration state. Here is how it is used: + +```js +// this.config is instance of ConfigStateService + +this.config.dispatchSetEnvironment({ + /* environment properties here */ +}); +// returns a state stream which emits after dispatch action is complete +``` + +Note that **you do not have to call this method at application initiation**, because the environment variables are already being stored at start. + +#### Environment Properties + +Please refer to `Config.Environment` type for all the properties you can pass to `dispatchSetEnvironment` as parameter. It can be found in the [config.ts file](https://github.com/abpframework/abp/blob/dev/npm/ng-packs/packages/core/src/lib/models/config.ts#L13). diff --git a/docs/en/UI/Angular/AddingSettingTab.md b/docs/en/UI/Angular/Custom-Setting-Page.md similarity index 66% rename from docs/en/UI/Angular/AddingSettingTab.md rename to docs/en/UI/Angular/Custom-Setting-Page.md index 2eb4e9bb61..7c51cf2cca 100644 --- a/docs/en/UI/Angular/AddingSettingTab.md +++ b/docs/en/UI/Angular/Custom-Setting-Page.md @@ -1,6 +1,6 @@ -## Creating a Settings Tab +# Custom Setting Page -There are several settings tabs from different modules. You can add custom settings tabs to your project in 3 steps. +There are several settings tabs from different modules. You can add custom settings page to your project in 3 steps. 1. Create a Component @@ -11,12 +11,11 @@ import { Component } from '@angular/core'; @Component({ selector: 'app-your-custom-settings', template: ` - your-custom-settings works! mySetting: {%{{{ mySetting$ | async }}}%} + custom-settings works! `, }) export class YourCustomSettingsComponent { - @Select(ConfigState.getSetting('MyProjectName.MySetting1')) // Gets a setting. MyProjectName.MySetting1 is a setting key. - mySetting$: Observable; // The selected setting is set to the mySetting variable as Observable. + // Your component logic } ``` @@ -38,6 +37,6 @@ ngOnInit() { } ``` -Open the `setting-management` page to see the changes: +Navigate to `/setting-management` route to see the changes: ![Custom Settings Tab](./images/custom-settings.png) diff --git a/docs/en/UI/Angular/images/custom-settings.png b/docs/en/UI/Angular/images/custom-settings.png index 32d7516849..05dd3f695a 100644 Binary files a/docs/en/UI/Angular/images/custom-settings.png and b/docs/en/UI/Angular/images/custom-settings.png differ diff --git a/docs/en/UI/AspNetCore/Bundling-Minification.md b/docs/en/UI/AspNetCore/Bundling-Minification.md index 8868d441e1..fc4ddaced3 100644 --- a/docs/en/UI/AspNetCore/Bundling-Minification.md +++ b/docs/en/UI/AspNetCore/Bundling-Minification.md @@ -143,7 +143,7 @@ This time, no file defined in the tag helper definition because the bundle files ### Configuring An Existing Bundle -ABP supports [modularity](../Module-Development-Basics.md) for bundling as well. A module can modify an existing bundle that is created by a dependant module. Example: +ABP supports [modularity](../Module-Development-Basics.md) for bundling as well. A module can modify an existing bundle that is created by a depended module. Example: ````C# [DependsOn(typeof(MyWebModule))] diff --git a/docs/en/UI/AspNetCore/Customization-User-Interface.md b/docs/en/UI/AspNetCore/Customization-User-Interface.md index ce50141ac0..ef4d1e97f7 100644 --- a/docs/en/UI/AspNetCore/Customization-User-Interface.md +++ b/docs/en/UI/AspNetCore/Customization-User-Interface.md @@ -2,7 +2,17 @@ This document explains how to override the user interface of a depended [application module](../../Modules/Index.md) for ASP.NET Core MVC / Razor Page applications. -## Overriding a Page Model +## Overriding a Page + +This section covers the [Razor Pages](https://docs.microsoft.com/en-us/aspnet/core/razor-pages/) development, which is the recommended approach to create server rendered user interface for ASP.NET Core. Pre-built modules typically uses the Razor Pages approach instead of the classic MVC pattern (next sections will cover the MVC pattern too). + +You typically have three kind of override requirement for a page: + +* Overriding **only the Page Model** (C#) side to perform additional logic without changing the page UI. +* Overring **only the Razor Page** (.chtml file) to change the UI without changing the c# behind the page. +* **Completely overriding** the page. + +### Overriding a Page Model (C#) ````csharp using System.Threading.Tasks; @@ -36,5 +46,425 @@ namespace Acme.BookStore.Web.Pages.Identity.Users } ```` -This class replaces `EditModalModel` for the users and overrides the `OnPostAsync` method. +* This class inherits from and replaces the `EditModalModel` for the users and overrides the `OnPostAsync` method to perform additional logic before and after the underlying code. +* It uses `ExposeServices` and `Dependency` attributes to replace the class. + +### Overriding a Razor Page (.CSHTML) + +Overriding a `.cshtml` file (razor page, razor view, view component... etc.) is possible through the [Virtual File System](../../Virtual-File-System.md). + +Virtual File system allows us to **embed resources into assemblies**. In this way, pre-built modules define the razor pages inside their NuGet packages. When you depend a module, you can override any file added to the virtual file system by that module, including pages/views. + +#### Example + +This example overrides the **login page** UI defined by the [Account Module](../../Modules/Account.md). + +Physical files override the embedded files defined in the same location. The account module defines a `Login.cshtml` file under the `Pages/Account` folder. So, you can override it by creating a file in the same path: + +![overriding-login-cshtml](../../images/overriding-login-cshtml.png) + +You typically want to copy the original `.cshtml` file of the module, then make the necessary changes. You can find the original file [here](https://github.com/abpframework/abp/blob/dev/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml). Do not copy the `Login.cshtml.cs` file which is the code behind file for the razor page and we don't want to override it yet (see the next section). + +That's all, you can change the file content however you like. + +### Completely Overriding a Razor Page + +You may want to completely override a page; the razor and the c# file related to the page. + +In such a case; + +1. Override the C# page model class just like described above, but don't replace the existing page model class. +2. Override the Razor Page just described above, but also change the @model directive to point your new page model. + +#### Example + +This example overrides the **login page** defined by the [Account Module](../../Modules/Account.md). + +Create a page model class deriving from the ` LoginModel ` (defined in the ` Volo.Abp.Account.Web.Pages.Account ` namespace): + +````csharp +public class MyLoginModel : LoginModel +{ + public MyLoginModel( + IAuthenticationSchemeProvider schemeProvider, + IOptions accountOptions + ) : base( + schemeProvider, + accountOptions) + { + + } + + public override Task OnPostAsync(string action) + { + //TODO: Add logic + return base.OnPostAsync(action); + } + + //TODO: Add new methods and properties... +} +```` + +You can override any method or add new properties/methods if needed. + +> Notice that we didn't use `[Dependency(ReplaceServices = true)]` or `[ExposeServices(typeof(LoginModel))]` since we don't want to replace the existing class in the dependency injection, we define a new one. + +Copy `Login.cshtml` file into your solution as just described above. Change the **@model** directive to point to the `MyLoginModel`: + +````xml +@page +... +@model Acme.BookStore.Web.Pages.Account.MyLoginModel +... +```` + +That's all! Make any change in the view and run your application. + +#### Replacing Page Model Without Inheritance + +You don't have to inherit from the original page model class (like done in the previous example). Instead, you can completely **re-implement** the page yourself. In this case, just derive from `PageModel`, `AbpPageModel` or any suitable base class you need. + +## Overriding a View Component + +The ABP Framework, pre-built themes and modules define some **re-usable view components**. These view components can be replaced just like a page described above. + +### Example + +The screenshot below was taken from the **basic theme** comes with the application startup template. + +![bookstore-brand-area-highlighted](../../images/bookstore-brand-area-highlighted.png) + +[The basic theme](../../Themes/Basic.md) defines some view components for the layout. For example, the highlighted area with the red rectangle above is called **Brand component**. You probably want to customize this component by adding your **own application logo**. Let's see how to do it. + +First, create your logo and place under a folder in your web application. We used `wwwroot/logos/bookstore-logo.png` path. Then copy the Brand component's view ([from here](https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Brand/Default.cshtml)) from the basic theme files under the `Themes/Basic/Components/Brand` folder. The result should be similar the picture below: + +![bookstore-added-brand-files](../../images/bookstore-added-brand-files.png) + +Then change the `Default.cshtml` as you like. Example content can be like that: + +````xml + + + +```` + +Now, you can run the application to see the result: + +![bookstore-added-logo](../../images/bookstore-added-logo.png) + +If you need, you can also replace [the code behind c# class](https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Brand/MainNavbarBrandViewComponent.cs) of the component just using the dependency injection system. + +### Overriding the Theme + +Just as explained above, you can replace any component, layout or c# class of the used theme. See the [theming document](Theming.md) for more information on the theming system. + +## Overriding Static Resources + +Overriding a static embedded resource (like JavaScript, Css or image files) of a module is pretty easy. Just place a file in the same path in your solution and let the Virtual File System to handle it. + +## Manipulating the Bundles + +The [Bundling & Minification](Bundling-Minification.md) system provides an **extensible and dynamic** system to create **script** and **style** bundles. It allows you to extend and manipulate the existing bundles. + +### Example: Add a Global CSS File + +For example, ABP Framework defines a **global style bundle** which is added to every page (actually, added to the layout by the themes). Let's add a **custom style file** to the end of the bundle files, so we can override any global style. + +First, create a CSS file and locate it in a folder inside the `wwwroot`: + +![bookstore-global-css-file](../../images/bookstore-global-css-file.png) + +Define some custom CSS rules inside the file. Example: + +````css +.card-title { + color: orange; + font-size: 2em; + text-decoration: underline; +} + +.btn-primary { + background-color: red; +} +```` + +Then add this file to the standard global style bundle in the `ConfigureServices` method of your [module](../../Module-Development-Basics.md): + +````csharp +Configure(options => +{ + options.StyleBundles.Configure( + StandardBundles.Styles.Global, //The bundle name! + bundleConfiguration => + { + bundleConfiguration.AddFiles("/styles/my-global-styles.css"); + } + ); +}); +```` + +#### The Global Script Bundle + +Just like the `StandardBundles.Styles.Global`, there is a `StandardBundles.Scripts.Global` that you can add files or manipulate the existing ones. + +### Example: Manipulate the Bundle Files + +The example above adds a new file to the bundle. You can do more if you create a **bundle contributor** class. Example: + +````csharp +public class MyGlobalStyleBundleContributor : BundleContributor +{ + public override void ConfigureBundle(BundleConfigurationContext context) + { + context.Files.Clear(); + context.Files.Add("/styles/my-global-styles.css"); + } +} +```` + +Then you can add the contributor to an existing bundle: + +````csharp +Configure(options => +{ + options.StyleBundles.Configure( + StandardBundles.Styles.Global, + bundleConfiguration => + { + bundleConfiguration.AddContributors(typeof(MyGlobalStyleBundleContributor)); + } + ); +}); +```` + +It is not a good idea to clear all CSS files. In a real world scenario, you can find and replace a specific file with your own file. + +### Example: Add a JavaScript File for a Specific Page + +The examples above works with the global bundle added to the layout. What if you want to add a CSS/JavaScript file (or replace a file) for a specific page defines inside a depended module? + +Assume that you want to run a **JavaScript code** once the user enters to the **Role Management** page of the Identity Module. + +First, create a standard JavaScript file under the `wwwroot`, `Pages` or `Views` folder (ABP support to add static resources inside these folders by default). We prefer the `Pages/Identity/Roles` folder to follow the conventions: + +![bookstore-added-role-js-file](../../images/bookstore-added-role-js-file.png) + +Content of the file is simple: + +````js +$(function() { + abp.log.info('My custom role script file has been loaded!'); +}); +```` + +Then add this file to the bundle of the role management page: + +````csharp +Configure(options => +{ + options.ScriptBundles + .Configure( + typeof(Volo.Abp.Identity.Web.Pages.Identity.Roles.IndexModel).FullName, + bundleConfig => + { + bundleConfig.AddFiles("/Pages/Identity/Roles/my-role-script.js"); + }); +}); +```` + +`typeof(Volo.Abp.Identity.Web.Pages.Identity.Roles.IndexModel).FullName` is the safe way to get the bundle name for the role management page. + +> Notice that not every page defines such page bundles. They define only if needed. + +In addition to adding new CSS/JavaScript file to a page, you also can replace the existing one (by defining a bundle contributor). + +## Layout Customization + +Layouts are defined by the theme ([see the theming](Theming.md)) by design. They are not included in a downloaded application solution. In this way you can easily **upgrade** the theme and get new features. You can not **directly change** the layout code in your application unless you replace it by your own layout (will be explained in the next sections). + +There are some common ways to **customize the layout** described in the next sections. + +### Menu Contributors + +There are two **standard menus** defined by the ABP Framework: + +![bookstore-menus-highlighted](../../images/bookstore-menus-highlighted.png) + +* `StandardMenus.Main`: The main menu of the application. +* `StandardMenus.User`: The user menu (generally at the top right of the screen). + +Rendering the menus is a responsibility of the theme, but **menu items** are determined by the modules and your application code. Just implement the `IMenuContributor` interface and **manipulate the menu items** in the `ConfigureMenuAsync` method. + +Menu contributors are executed whenever need to render the menu. There is already a menu contributor defined in the **application startup template**, so you can take it as an example and improve if necessary. See the [navigation menu](Navigation-Menu.md) document for more. + +### Toolbar Contributors + +[Toolbar system](Toolbars.md) is used to define **toolbars** on the user interface. Modules (or your application) can add **items** to a toolbar, then the theme renders the toolbar on the **layout**. + +There is only one **standard toolbar** (named "Main" - defined as a constant: `StandardToolbars.Main`). For the basic theme, it is rendered as shown below:![bookstore-toolbar-highlighted](../../images/bookstore-toolbar-highlighted.png) + +In the screenshot above, there are two items added to the main toolbar: Language switch component & user menu. You can add your own items here. + +#### Example: Add a Notification Icon + +In this example, we will add a **notification (bell) icon** to the left of the language switch item. A item in the toolbar should be a **view component**. So, first, create a new view component in your project: + +![bookstore-notification-view-component](../../images/bookstore-notification-view-component.png) + +**NotificationViewComponent.cs** + +````csharp +public class NotificationViewComponent : AbpViewComponent +{ + public async Task InvokeAsync() + { + return View("/Pages/Shared/Components/Notification/Default.cshtml"); + } +} +```` + +**Default.cshtml** + +````xml +
+ +
+```` + +Now, we can create a class implementing the `IToolbarContributor` interface: + +````csharp +public class MyToolbarContributor : IToolbarContributor +{ + public Task ConfigureToolbarAsync(IToolbarConfigurationContext context) + { + if (context.Toolbar.Name == StandardToolbars.Main) + { + context.Toolbar.Items + .Insert(0, new ToolbarItem(typeof(NotificationViewComponent))); + } + + return Task.CompletedTask; + } +} +```` + +This class adds the `NotificationViewComponent` as the first item in the `Main` toolbar. + +Finally, you need to add this contributor to the `AbpToolbarOptions`, in the `ConfigureServices` of your module: + +````csharp +Configure(options => +{ + options.Contributors.Add(new MyToolbarContributor()); +}); +```` + +That's all, you will see the notification icon on the toolbar when you run the application: + +![bookstore-notification-icon-on-toolbar](../../images/bookstore-notification-icon-on-toolbar.png) + +`NotificationViewComponent` in this sample simply returns a view without any data. In real life, you probably want to **query database** (or call an HTTP API) to get notifications and pass to the view. If you need, you can add a `JavaScript` or `CSS` file to the global bundle (as described before) for your toolbar item. + +See the [toolbars document](Toolbars.md) for more about the toolbar system. + +### Layout Hooks + +[Layout Hooks](Layout-Hooks.md) system allows you to **add code** at some specific parts of the layout. All layouts of all themes should implement these hooks. Then you can then add a **view component** into a hook point. + +#### Example: Add Google Analytics Script + +Assume that you need to add the Google Analytics script to the layout (that will be available for all the pages). First, **create a view component** in your project: + +![bookstore-google-analytics-view-component](../../images/bookstore-google-analytics-view-component.png) + +**NotificationViewComponent.cs** + +````csharp +public class GoogleAnalyticsViewComponent : AbpViewComponent +{ + public IViewComponentResult Invoke() + { + return View("/Pages/Shared/Components/GoogleAnalytics/Default.cshtml"); + } +} +```` + +**Default.cshtml** + +````html + +```` + +Change `UA-xxxxxx-1` with your own code. + +You can then add this component to any of the hook points in the `ConfigureServices` of your module: + +````csharp +Configure(options => +{ + options.Add( + LayoutHooks.Head.Last, //The hook name + typeof(GoogleAnalyticsViewComponent) //The component to add + ); +}); +```` + +Now, the GA code will be inserted in the `head` of the page as the last item. You (or the modules you are using) can add multiple items to the same hook. All of them will be added to the layout. + +The configuration above adds the `GoogleAnalyticsViewComponent` to all layouts. You may want to only add to a specific layout: + +````csharp +Configure(options => +{ + options.Add( + LayoutHooks.Head.Last, + typeof(GoogleAnalyticsViewComponent), + layout: StandardLayouts.Application //Set the layout to add + ); +}); +```` + +See the layouts section below to learn more about the layout system. + +### Layouts + +Layout system allows themes to define standard, named layouts and allows any page to select a proper layout for its purpose. There are three pre-defined layouts: + +* "**Application**": The main (and the default) layout for an application. It typically contains header, menu (sidebar), footer, toolbar... etc. +* "**Account**": This layout is used by login, register and other similar pages. It is used for the pages under the `/Pages/Account` folder by default. +* "**Empty**": Empty and minimal layout. + +These names are defined in the `StandardLayouts` class as constants. You can definitely create your own layouts, but these are standard layout names and implemented by all the themes out of the box. + +#### Layout Location + +You can find the layout files [here](https://github.com/abpframework/abp/tree/dev/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Layouts) for the basic theme. You can take them as references to build your own layouts or you can override them if necessary. + +#### ITheme + +ABP Framework uses the `ITheme` service to get the layout location by the layout name. You can replace this service to dynamically select the layout location. + +#### IThemeManager + +`IThemeManager` is used to obtain the current theme and get the layout path. Any page can determine the layout of its own. Example: + +````html +@using Volo.Abp.AspNetCore.Mvc.UI.Theming +@inject IThemeManager ThemeManager +@{ + Layout = ThemeManager.CurrentTheme.GetLayout(StandardLayouts.Empty); +} +```` + +This page will use the empty layout. You use `ThemeManager.CurrentTheme.GetEmptyLayout();` extension method as a shortcut. +If you want to set the layout for all the pages under a specific folder, then write the code above in a `_ViewStart.cshtml` file under that folder. \ No newline at end of file diff --git a/docs/en/UI/AspNetCore/Layout-Hooks.md b/docs/en/UI/AspNetCore/Layout-Hooks.md new file mode 100644 index 0000000000..49b334c6c0 --- /dev/null +++ b/docs/en/UI/AspNetCore/Layout-Hooks.md @@ -0,0 +1,3 @@ +# Layout Hooks + +TODO \ No newline at end of file diff --git a/docs/en/UI/AspNetCore/Navigation-Menu.md b/docs/en/UI/AspNetCore/Navigation-Menu.md new file mode 100644 index 0000000000..4e0164ec72 --- /dev/null +++ b/docs/en/UI/AspNetCore/Navigation-Menu.md @@ -0,0 +1,3 @@ +# Navigation Menu + +TODO \ No newline at end of file diff --git a/docs/en/UI/AspNetCore/Tag-Helpers/Buttons.md b/docs/en/UI/AspNetCore/Tag-Helpers/Buttons.md index ba1bfb4032..ae62c7f2b7 100644 --- a/docs/en/UI/AspNetCore/Tag-Helpers/Buttons.md +++ b/docs/en/UI/AspNetCore/Tag-Helpers/Buttons.md @@ -1,30 +1,26 @@ # Buttons -ABP framework has a special Tag Helper to create bootstrap button easily. +## Introduction -`` +`abp-button` is the main element to create buttons. -## Attributes +Basic usage: -`` has 7 different attribute. +````xml +Click Me +```` -* [`button-type`](#button-type) -* [`size`](#size) -* [`busy-text`](#busy-text) -* [`text`](#text) -* [`icon`](#icon) -* [`disabled`](#disabled) -* [`icon-type`](#icon-type) +## Demo -### `button-type` +See the [buttons demo page](https://bootstrap-taghelpers.abp.io/Components/Buttons) to see it in action. -`button-type` is a selectable parameter. It's default value is `Default`. +## Attributes -`Button` +### button-type -You can choose one of the button type listed below. +A value indicates the main style/type of the button. Should be one of the following values: -* `Default` +* `Default` (default value) * `Primary` * `Secondary` * `Success` @@ -43,15 +39,11 @@ You can choose one of the button type listed below. * `Outline_Dark` * `Link` -### `size` - -`size` is a selectable parameter. It's default value is `Default`. +### size -`Button` +A value indicates the size of the button. Should be one of the following values: -You can choose one of the size type listed below. - -* `Default` +* `Default` (default value) * `Small` * `Medium` * `Large` @@ -60,35 +52,35 @@ You can choose one of the size type listed below. * `Block_Medium` * `Block_Large` -### `busy-text` - -`busy-text` is a string parameter. It shows the text while the button is busy. - -### `text` +### busy-text -`text` is a string parameter that displaying on button. +A text that is shown when the button is busy. -### `icon` +### text -`icon` is a string parameter. It is depending to [`icon-type`](#`icon-type`). For default, we use [Font Awesome](https://fontawesome.com/) for icons. To use it, you need to set `icon` parameter as a icon name. +The text of the button. This is a shortcut if you simply want to set a text to the button. Example: -##### Example +````xml + +```` -[fa-address-card](https://fontawesome.com/icons/address-card): ![fa-address-card](fa-address-card.png "Address Card") +In this case, you can use a self-closing tag to make it shorter. -`` +### icon -> Don't forget: You dont need to write prefix! It will add automatically "fa" prefix for [Font Awesome](https://fontawesome.com/) icons while you did not change `icon-type`. +Used to set an icon for the button. It works with the [Font Awesome](https://fontawesome.com/) icon classes by default. Example: -### `disabled` +````xml + +```` -`disabled` is a boolean parameter. If you set it `true`, your button will be disabled. +##### icon-type -### `icon-type` +If you don't want to use font-awesome, you have two options: -`icon-type` is a selectable parameter. It's default value is `FontAwesome`. You can create your own icon type provider and change it. +1. Set `icon-type` to `Other` and write the CSS class of the font icon you're using. +2. If you don't use a font icon use the opening and closing tags manually and write any code inside the tags. -You can choose one of the icon type listed below. +### disabled -* `FontAwesome` -* `Other` +Set `true` to make the button initially disabled. \ No newline at end of file diff --git a/docs/en/UI/AspNetCore/Tag-Helpers/Index.md b/docs/en/UI/AspNetCore/Tag-Helpers/Index.md index bdeb7668b7..d7e393933e 100644 --- a/docs/en/UI/AspNetCore/Tag-Helpers/Index.md +++ b/docs/en/UI/AspNetCore/Tag-Helpers/Index.md @@ -1,3 +1,26 @@ -## ABP Tag Helpers +# ABP Tag Helpers -"ABP tag helpers" documentation is creating now. You can see a [demo of components](http://bootstrap-taghelpers.abp.io/) for now. \ No newline at end of file +ABP Framework defines a set of **tag helper components** to simply the user interface development for ASP.NET Core (MVC / Razor Pages) applications. + +## Bootstrap Component Wrappers + +Most of the tag helpers are [Bootstrap](https://getbootstrap.com/) (v4+) wrappers. Coding bootstrap is not so easy, not so type-safe and contains too much repetitive HTML tags. ABP Tag Helpers makes it **easier** and **type safe**. + +We don't aim to wrap bootstrap components 100%. Writing **native bootstrap style code** is still possible (actually, tag helpers generates native bootstrap code in the end), but we suggest to use the tag helpers wherever possible. + +ABP Framework also adds some **useful features** to the standard bootstrap components. + +Here, the list of components those are wrapped by the ABP Framework: + +* [Buttons](Buttons.md) +* ... + +> Until all the tag helpers are documented, you can visit https://bootstrap-taghelpers.abp.io/ to see them with live samples. + +## Form Elements + +See [demo](https://bootstrap-taghelpers.abp.io/Components/FormElements). + +## Dynamic Inputs + +See [demo](https://bootstrap-taghelpers.abp.io/Components/DynamicForms). \ No newline at end of file diff --git a/docs/en/UI/AspNetCore/Tag-Helpers/fa-address-card.png b/docs/en/UI/AspNetCore/Tag-Helpers/fa-address-card.png deleted file mode 100644 index a3b2c815b8..0000000000 Binary files a/docs/en/UI/AspNetCore/Tag-Helpers/fa-address-card.png and /dev/null differ diff --git a/docs/en/UI/AspNetCore/Theming.md b/docs/en/UI/AspNetCore/Theming.md index 470ef1a458..e41545635c 100644 --- a/docs/en/UI/AspNetCore/Theming.md +++ b/docs/en/UI/AspNetCore/Theming.md @@ -1,3 +1,3 @@ -# Theming +# ASP.NET Core MVC / Razor Pages Theming TODO \ No newline at end of file diff --git a/docs/en/UI/AspNetCore/Toolbars.md b/docs/en/UI/AspNetCore/Toolbars.md new file mode 100644 index 0000000000..cc3bcd95be --- /dev/null +++ b/docs/en/UI/AspNetCore/Toolbars.md @@ -0,0 +1,3 @@ +# Toolbars + +TODO \ No newline at end of file diff --git a/docs/en/docs-nav.json b/docs/en/docs-nav.json index c3af945e85..c273e59346 100644 --- a/docs/en/docs-nav.json +++ b/docs/en/docs-nav.json @@ -293,16 +293,7 @@ }, { "text": "Tag Helpers", - "items": [ - { - "text": "Live Demo", - "path": "UI/AspNetCore/Tag-Helpers/Index.md" - }, - { - "text": "Buttons", - "path": "UI/AspNetCore/Tag-Helpers/Buttons.md" - } - ] + "path": "UI/AspNetCore/Tag-Helpers/Index.md" }, { "text": "Widgets", @@ -325,9 +316,17 @@ "text": "Permission Management", "path": "UI/Angular/Permission-Management.md" }, + { + "text": "Config State", + "path": "UI/Angular/Config-State.md" + }, { "text": "Component Replacement", "path": "UI/Angular/Component-Replacement.md" + }, + { + "text": "Custom Setting Page", + "path": "UI/Angular/Custom-Setting-Page.md" } ] } diff --git a/docs/en/images/bookstore-added-brand-files.png b/docs/en/images/bookstore-added-brand-files.png new file mode 100644 index 0000000000..15ef283a2a Binary files /dev/null and b/docs/en/images/bookstore-added-brand-files.png differ diff --git a/docs/en/images/bookstore-added-logo.png b/docs/en/images/bookstore-added-logo.png new file mode 100644 index 0000000000..55af77a5f4 Binary files /dev/null and b/docs/en/images/bookstore-added-logo.png differ diff --git a/docs/en/images/bookstore-added-role-js-file.png b/docs/en/images/bookstore-added-role-js-file.png new file mode 100644 index 0000000000..00881bf808 Binary files /dev/null and b/docs/en/images/bookstore-added-role-js-file.png differ diff --git a/docs/en/images/bookstore-brand-area-highlighted.png b/docs/en/images/bookstore-brand-area-highlighted.png new file mode 100644 index 0000000000..7ab1b8fb7f Binary files /dev/null and b/docs/en/images/bookstore-brand-area-highlighted.png differ diff --git a/docs/en/images/bookstore-global-css-file.png b/docs/en/images/bookstore-global-css-file.png new file mode 100644 index 0000000000..e764fef8ee Binary files /dev/null and b/docs/en/images/bookstore-global-css-file.png differ diff --git a/docs/en/images/bookstore-google-analytics-view-component.png b/docs/en/images/bookstore-google-analytics-view-component.png new file mode 100644 index 0000000000..e548a00935 Binary files /dev/null and b/docs/en/images/bookstore-google-analytics-view-component.png differ diff --git a/docs/en/images/bookstore-menus-highlighted.png b/docs/en/images/bookstore-menus-highlighted.png new file mode 100644 index 0000000000..c4f3237b00 Binary files /dev/null and b/docs/en/images/bookstore-menus-highlighted.png differ diff --git a/docs/en/images/bookstore-notification-icon-on-toolbar.png b/docs/en/images/bookstore-notification-icon-on-toolbar.png new file mode 100644 index 0000000000..817491e9eb Binary files /dev/null and b/docs/en/images/bookstore-notification-icon-on-toolbar.png differ diff --git a/docs/en/images/bookstore-notification-view-component.png b/docs/en/images/bookstore-notification-view-component.png new file mode 100644 index 0000000000..3fc300efe1 Binary files /dev/null and b/docs/en/images/bookstore-notification-view-component.png differ diff --git a/docs/en/images/bookstore-toolbar-highlighted.png b/docs/en/images/bookstore-toolbar-highlighted.png new file mode 100644 index 0000000000..f2b7c3f57e Binary files /dev/null and b/docs/en/images/bookstore-toolbar-highlighted.png differ diff --git a/docs/en/images/overriding-login-cshtml.png b/docs/en/images/overriding-login-cshtml.png new file mode 100644 index 0000000000..d6daa4a141 Binary files /dev/null and b/docs/en/images/overriding-login-cshtml.png differ diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/BasicTheme.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/BasicTheme.cs index 01d3a35db2..60d1307b38 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/BasicTheme.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/BasicTheme.cs @@ -8,7 +8,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic { public const string Name = "Basic"; - public string GetLayout(string name, bool fallbackToDefault = true) + public virtual string GetLayout(string name, bool fallbackToDefault = true) { switch (name) { diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/AngularModuleSourceCodeAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/AngularModuleSourceCodeAdder.cs new file mode 100644 index 0000000000..2d1e237933 --- /dev/null +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/AngularModuleSourceCodeAdder.cs @@ -0,0 +1,198 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Cli.ProjectModification +{ + public class AngularModuleSourceCodeAdder : ITransientDependency + { + public async Task AddAsync(string solutionFilePath, string angularPath) + { + var angularProjectsPath = Path.Combine(angularPath, "projects"); + + var projects = await CopyAndGetNamesOfAngularProjectsAsync(solutionFilePath, angularProjectsPath); + + if (!projects.Any()) + { + return; + } + + await ReplaceProjectNamesAndCopySourCodeRequirementsToProjectsAsync(angularProjectsPath, projects); + + await RemoveRedundantFilesAsync(angularProjectsPath, projects); + + await AddPathsToTsConfigAsync(angularPath, angularProjectsPath, projects); + + await AddProjectToAngularJsonAsync(angularPath, projects); + } + + private async Task AddProjectToAngularJsonAsync(string angularPath, List projects) + { + var angularJsonFilePath = Path.Combine(angularPath, "angular.json"); + var fileContent = File.ReadAllText(angularJsonFilePath); + + var json = JObject.Parse(fileContent); + + var projectsJobject = (JObject)json["projects"]; + + foreach (var project in projects) + { + projectsJobject.Add(project, new JObject( + new JProperty("projectType", "library"), + new JProperty("root", $"projects/{project}"), + new JProperty("sourceRoot", $"projects/{project}/src"), + new JProperty("prefix", "lib"), + new JProperty("architect", new JObject( + new JProperty("build", new JObject( + new JProperty("builder", "@angular-devkit/build-ng-package:build"), + new JProperty("options", new JObject( + new JProperty("tsConfig", $"projects/{project}/tsconfig.lib.json"), + new JProperty("project", $"projects/{project}/ng-package.json") + )), + new JProperty("configurations", new JObject( + new JProperty("production", new JObject( + new JProperty("tsConfig", $"projects/{project}/tsconfig.lib.prod.json"))) + )))), + new JProperty("test", new JObject( + new JProperty("builder", "@angular-devkit/build-angular:karma"), + new JProperty("options", new JObject( + new JProperty("main", $"projects/{project}/src/test.ts"), + new JProperty("tsConfig", $"projects/{project}/tsconfig.spec.json"), + new JProperty("karmaConfig", $"projects/{project}/karma.conf.js") + ) + ))), + new JProperty("lint", new JObject( + new JProperty("builder", "@angular-devkit/build-angular:tslint"), + new JProperty("options", new JObject( + new JProperty("tsConfig", new JArray(new JValue($"projects/{project}/tsconfig.lib.json"), new JValue($"projects/{project}/tsconfig.spec.json"))), + new JProperty("exclude", new JArray(new JValue("**/node_modules/**"))) + )) + )) + )) + )); + } + + File.WriteAllText(angularJsonFilePath, json.ToString(Formatting.Indented)); + } + + private async Task AddPathsToTsConfigAsync(string angularPath, string angularProjectsPath, List projects) + { + var tsConfigPath = Path.Combine(angularPath, "tsconfig.json"); + var fileContent = File.ReadAllText(tsConfigPath); + + var tsConfigAsJson = JObject.Parse(fileContent); + + var compilerOptions = (JObject)tsConfigAsJson["compilerOptions"]; + + foreach (var project in projects) + { + var projectPackageName = await GetProjectPackageNameAsync(angularProjectsPath, project); + + var property = new JProperty($"{projectPackageName}", + new JArray(new object[] { $"projects/{project}/src/public-api.ts" }) + ); + var property2 = new JProperty($"{projectPackageName}/*", + new JArray(new object[] { $"projects/{project}/src/lib/*" }) + ); + + if (compilerOptions["paths"] == null) + { + + compilerOptions.Add("paths", new JObject()); + } + + ((JObject)compilerOptions["paths"]).Add(property); + ((JObject)compilerOptions["paths"]).Add(property2); + } + + File.WriteAllText(tsConfigPath, tsConfigAsJson.ToString(Formatting.Indented)); + } + + private async Task GetProjectPackageNameAsync(string angularProjectsPath, string project) + { + var packageJsonPath = Path.Combine(angularProjectsPath, project, "package.json"); + + var fileContent = File.ReadAllText(packageJsonPath); + + return (string)JObject.Parse(fileContent)["name"]; + } + + private async Task RemoveRedundantFilesAsync(string angularProjectsPath, List projects) + { + foreach (var project in projects) + { + var jestConfigPath = Path.Combine(angularProjectsPath, project, "jest.config.js"); + + File.Delete(jestConfigPath); + + var testPath = Path.Combine(angularProjectsPath, project, "src", "test.ts"); + + File.Delete(testPath); + } + } + + private async Task ReplaceProjectNamesAndCopySourCodeRequirementsToProjectsAsync(string angularProjectsPath, List projects) + { + foreach (var project in projects) + { + var sourceCodeReqFolder = Path.Combine(angularProjectsPath, project, "source-code-requirements"); + + Directory.CreateDirectory(sourceCodeReqFolder); + + var filesUnderSourceCodeReqFolder = Directory.GetFiles(sourceCodeReqFolder); + + foreach (var fileUnderSourceCodeReqFolder in filesUnderSourceCodeReqFolder) + { + var newDest = Path.Combine(angularProjectsPath, project, Path.GetFileName(fileUnderSourceCodeReqFolder)); + File.Move(fileUnderSourceCodeReqFolder, newDest); + + var fileContent = File.ReadAllText(newDest); + fileContent = fileContent.Replace("{{project-name}}", project); + fileContent = fileContent.Replace("{{library-name-kebab-case}}", project); + File.WriteAllText(newDest, fileContent); + } + } + } + + private async Task> CopyAndGetNamesOfAngularProjectsAsync(string solutionFilePath, string angularProjectsPath) + { + var projects = new List(); + + if (!Directory.Exists(angularProjectsPath)) + { + Directory.CreateDirectory(angularProjectsPath); + } + + var angularPathsInDownloadedSourceCode = Directory.GetDirectories(Path.Combine(Path.Combine(Path.GetDirectoryName(solutionFilePath), "modules"))).Select(p => Path.Combine(p, "angular")) + .Where(Directory.Exists); + + + foreach (var folder in angularPathsInDownloadedSourceCode) + { + var projectsInFolder = Directory.GetDirectories(folder); + foreach (var projectInFolder in projectsInFolder) + { + var projectName = Path.GetFileName(projectInFolder.TrimEnd('\\').TrimEnd('/')); + + var destDirName = Path.Combine(angularProjectsPath, projectName); + if (Directory.Exists(destDirName)) + { + Directory.Delete(projectInFolder, true); + continue; + } + + + projects.Add(projectName); + + Directory.Move(projectInFolder, destDirName); + } + } + + return projects; + } + } +} diff --git a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs index 8ded87bf6f..dcb36d7bae 100644 --- a/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs +++ b/framework/src/Volo.Abp.Cli.Core/Volo/Abp/Cli/ProjectModification/SolutionModuleAdder.cs @@ -30,6 +30,7 @@ namespace Volo.Abp.Cli.ProjectModification public SourceCodeDownloadService SourceCodeDownloadService { get; } public SolutionFileModifier SolutionFileModifier { get; } public NugetPackageToLocalReferenceConverter NugetPackageToLocalReferenceConverter { get; } + public AngularModuleSourceCodeAdder AngularModuleSourceCodeAdder { get; } public SolutionModuleAdder( IJsonSerializer jsonSerializer, @@ -42,7 +43,8 @@ namespace Volo.Abp.Cli.ProjectModification IRemoteServiceExceptionHandler remoteServiceExceptionHandler, SourceCodeDownloadService sourceCodeDownloadService, SolutionFileModifier solutionFileModifier, - NugetPackageToLocalReferenceConverter nugetPackageToLocalReferenceConverter) + NugetPackageToLocalReferenceConverter nugetPackageToLocalReferenceConverter, + AngularModuleSourceCodeAdder angularModuleSourceCodeAdder) { JsonSerializer = jsonSerializer; ProjectNugetPackageAdder = projectNugetPackageAdder; @@ -55,6 +57,7 @@ namespace Volo.Abp.Cli.ProjectModification SourceCodeDownloadService = sourceCodeDownloadService; SolutionFileModifier = solutionFileModifier; NugetPackageToLocalReferenceConverter = nugetPackageToLocalReferenceConverter; + AngularModuleSourceCodeAdder = angularModuleSourceCodeAdder; Logger = NullLogger.Instance; } @@ -83,11 +86,25 @@ namespace Volo.Abp.Cli.ProjectModification await DownloadSourceCodesToSolutionFolder(module, modulesFolderInSolution, version); await SolutionFileModifier.AddModuleToSolutionFileAsync(module, solutionFile); await NugetPackageToLocalReferenceConverter.Convert(module, solutionFile); + + await HandleAngularProject(module, solutionFile); } ModifyDbContext(projectFiles, module, startupProject, skipDbMigrations); } + private async Task HandleAngularProject(ModuleWithMastersInfo module, string solutionFilePath) + { + var angularPath = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(solutionFilePath)), "angular"); + + if (!Directory.Exists(angularPath)) + { + return; + } + + await AngularModuleSourceCodeAdder.AddAsync(solutionFilePath, angularPath); + } + private async Task DownloadSourceCodesToSolutionFolder(ModuleWithMastersInfo module, string modulesFolderInSolution, string version = null) { await SourceCodeDownloadService.DownloadAsync( diff --git a/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ar.json b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ar.json new file mode 100644 index 0000000000..3fb22139b7 --- /dev/null +++ b/framework/src/Volo.Abp.UI/Localization/Resources/AbpUi/ar.json @@ -0,0 +1,62 @@ +{ + "culture": "ar", + "texts": { + "InternalServerErrorMessage": "حدث خطأ داخلي أثناء طلبك!", + "ValidationErrorMessage": "طلبك غير صحيح!", + "ValidationNarrativeErrorMessageTitle": "تم الكشف عن الأخطاء التالية أثناء التحقق .", + "DefaultErrorMessage": "حدث خطأ!", + "DefaultErrorMessageDetail": "لم يتم إرسال تفاصيل الخطأ بواسطة الخادم.", + "DefaultErrorMessage401": "أنت غير مصدق!", + "DefaultErrorMessage401Detail": "يجب عليك تسجيل الدخول لأداء هذه العملية.", + "DefaultErrorMessage403": "أنك غير مخول!", + "DefaultErrorMessage403Detail": "لا يسمح لك بإجراء هذه العملية!", + "DefaultErrorMessage404": "المورد غير موجود!", + "DefaultErrorMessage404Detail": "لم يتم العثور على المورد المطلوب على الخادم!", + "EntityNotFoundErrorMessage": "لا يوجد كيان {0} بالمعرف = {1}!", + "Languages": "اللغات", + "Error": "خطأ", + "AreYouSure": "هل أنت متأكد؟", + "Cancel": "إلغاء", + "Yes": "نعم", + "No": "لا", + "Ok": "حسنا", + "Close": "أغلق", + "Save": "حفظ", + "SavingWithThreeDot": "حفظ...", + "Actions": "أجراءات", + "Delete": "حذف", + "Edit": "تعديل", + "Refresh": "تحديث", + "Language": "لغة", + "LoadMore": "تحميل المزيد", + "ProcessingWithThreeDot": "معالجة...", + "LoadingWithThreeDot": "جار التحميل...", + "Welcome": "أهلا بك", + "Login": "تسجيل الدخول", + "Register": "تسجيل", + "Logout": "تسجيل خروج", + "Submit": "إرسال", + "Back": "عودة", + "PagerSearch": "بحث", + "PagerNext": "التالى", + "PagerPrevious": "السابق", + "PagerFirst": "الأول", + "PagerLast": "الاخير", + "PagerInfo": "عرض _START_ الي _END_ من _TOTAL_ إدخالات", + "PagerInfoEmpty": "عرض 0 الي 0 من 0 إدخالات", + "PagerInfoFiltered": "(تمت تصفيته من _MAX_ مجموع الإدخالات)", + "NoDataAvailableInDatatable": "لا توجد بيانات متاحة في الجدول", + "PagerShowMenuEntries": "عرض _MENU_ إدخالات", + "DatatableActionDropdownDefaultText": "أجراءات", + "ChangePassword": "تغيير كلمة السر", + "PersonalInfo": "ملفي الشخصي", + "AreYouSureYouWantToCancelEditingWarningMessage": "لم تحفظ التغييرات.", + "UnhandledException": "استثناء غير معالج!", + "401Message": "غير مصرح", + "403Message": "ممنوع", + "404Message": "الصفحة غير موجودة", + "500Message": "خطأ في الخادم الداخلي", + "GoHomePage": "اذهب الى الصفحة الرئيسية", + "GoBack": "عد" + } +} diff --git a/npm/ng-packs/package.json b/npm/ng-packs/package.json index b8ec511a18..e020b3f235 100644 --- a/npm/ng-packs/package.json +++ b/npm/ng-packs/package.json @@ -21,19 +21,19 @@ "generate:changelog": "conventional-changelog -p angular -i CHANGELOG.md -s" }, "devDependencies": { - "@abp/ng.account": "^2.1.0", - "@abp/ng.account.config": "^2.1.0", - "@abp/ng.core": "^2.1.0", - "@abp/ng.feature-management": "^2.1.0", - "@abp/ng.identity": "^2.1.0", - "@abp/ng.identity.config": "^2.1.0", - "@abp/ng.permission-management": "^2.1.0", - "@abp/ng.setting-management": "^2.1.0", - "@abp/ng.setting-management.config": "^2.1.0", - "@abp/ng.tenant-management": "^2.1.0", - "@abp/ng.tenant-management.config": "^2.1.0", - "@abp/ng.theme.basic": "^2.1.0", - "@abp/ng.theme.shared": "^2.1.0", + "@abp/ng.account": "~2.1.0", + "@abp/ng.account.config": "~2.1.0", + "@abp/ng.core": "~2.1.0", + "@abp/ng.feature-management": "~2.1.0", + "@abp/ng.identity": "~2.1.0", + "@abp/ng.identity.config": "~2.1.0", + "@abp/ng.permission-management": "~2.1.0", + "@abp/ng.setting-management": "~2.1.0", + "@abp/ng.setting-management.config": "~2.1.0", + "@abp/ng.tenant-management": "~2.1.0", + "@abp/ng.tenant-management.config": "~2.1.0", + "@abp/ng.theme.basic": "~2.1.0", + "@abp/ng.theme.shared": "~2.1.0", "@angular-builders/jest": "^8.2.0", "@angular-devkit/build-angular": "~0.803.21", "@angular-devkit/build-ng-packagr": "~0.803.21", diff --git a/npm/ng-packs/packages/account/package.json b/npm/ng-packs/packages/account/package.json index d4b136a17d..84d4b6bf56 100644 --- a/npm/ng-packs/packages/account/package.json +++ b/npm/ng-packs/packages/account/package.json @@ -7,8 +7,8 @@ "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.account.config": "^2.2.0", - "@abp/ng.theme.shared": "^2.2.0" + "@abp/ng.account.config": "~2.2.0", + "@abp/ng.theme.shared": "~2.2.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/core/src/lib/states/config.state.ts b/npm/ng-packs/packages/core/src/lib/states/config.state.ts index 3e4c544f11..f424d83cb2 100644 --- a/npm/ng-packs/packages/core/src/lib/states/config.state.ts +++ b/npm/ng-packs/packages/core/src/lib/states/config.state.ts @@ -156,7 +156,7 @@ export class ConfigState { const selector = createSelector([ConfigState], (state: Config.State) => { if (!state.localization) return defaultValue || key; - const { defaultResourceName } = state.environment.localization; + const defaultResourceName = snq(() => state.environment.localization.defaultResourceName); if (keys[0] === '') { if (!defaultResourceName) { throw new Error( @@ -170,7 +170,7 @@ export class ConfigState { ); } - keys[0] = snq(() => defaultResourceName); + keys[0] = defaultResourceName; } let localization = (keys as any).reduce((acc, val) => { diff --git a/npm/ng-packs/packages/feature-management/package.json b/npm/ng-packs/packages/feature-management/package.json index 4ac09b6f9c..f4518675c6 100644 --- a/npm/ng-packs/packages/feature-management/package.json +++ b/npm/ng-packs/packages/feature-management/package.json @@ -7,7 +7,7 @@ "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "^2.2.0" + "@abp/ng.theme.shared": "~2.2.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/identity/package.json b/npm/ng-packs/packages/identity/package.json index 67a19edb65..d003761f4c 100644 --- a/npm/ng-packs/packages/identity/package.json +++ b/npm/ng-packs/packages/identity/package.json @@ -7,9 +7,9 @@ "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.identity.config": "^2.2.0", - "@abp/ng.permission-management": "^2.2.0", - "@abp/ng.theme.shared": "^2.2.0" + "@abp/ng.identity.config": "~2.2.0", + "@abp/ng.permission-management": "~2.2.0", + "@abp/ng.theme.shared": "~2.2.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/permission-management/package.json b/npm/ng-packs/packages/permission-management/package.json index cc0c80f01d..cb114343cb 100644 --- a/npm/ng-packs/packages/permission-management/package.json +++ b/npm/ng-packs/packages/permission-management/package.json @@ -7,7 +7,7 @@ "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "^2.2.0" + "@abp/ng.theme.shared": "~2.2.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/setting-management/package.json b/npm/ng-packs/packages/setting-management/package.json index 5947478234..78b733b3b1 100644 --- a/npm/ng-packs/packages/setting-management/package.json +++ b/npm/ng-packs/packages/setting-management/package.json @@ -7,8 +7,8 @@ "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.setting-management.config": "^2.2.0", - "@abp/ng.theme.shared": "^2.2.0" + "@abp/ng.setting-management.config": "~2.2.0", + "@abp/ng.theme.shared": "~2.2.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/tenant-management/package.json b/npm/ng-packs/packages/tenant-management/package.json index a53304ba9c..4e17d00c9c 100644 --- a/npm/ng-packs/packages/tenant-management/package.json +++ b/npm/ng-packs/packages/tenant-management/package.json @@ -7,9 +7,9 @@ "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.feature-management": "^2.2.0", - "@abp/ng.tenant-management.config": "^2.2.0", - "@abp/ng.theme.shared": "^2.2.0" + "@abp/ng.feature-management": "~2.2.0", + "@abp/ng.tenant-management.config": "~2.2.0", + "@abp/ng.theme.shared": "~2.2.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/theme-basic/package.json b/npm/ng-packs/packages/theme-basic/package.json index 9bfc619578..7ac94efb0a 100644 --- a/npm/ng-packs/packages/theme-basic/package.json +++ b/npm/ng-packs/packages/theme-basic/package.json @@ -7,7 +7,7 @@ "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.theme.shared": "^2.2.0" + "@abp/ng.theme.shared": "~2.2.0" }, "publishConfig": { "access": "public" diff --git a/npm/ng-packs/packages/theme-shared/package.json b/npm/ng-packs/packages/theme-shared/package.json index 1776bb0a12..efd21cd245 100644 --- a/npm/ng-packs/packages/theme-shared/package.json +++ b/npm/ng-packs/packages/theme-shared/package.json @@ -7,7 +7,7 @@ "url": "https://github.com/abpframework/abp.git" }, "dependencies": { - "@abp/ng.core": "^2.2.0", + "@abp/ng.core": "~2.2.0", "@fortawesome/fontawesome-free": "^5.12.1", "@ng-bootstrap/ng-bootstrap": "^5.3.0", "@ngx-validate/core": "^0.0.7", diff --git a/samples/BookStore-Angular-MongoDb/angular/package.json b/samples/BookStore-Angular-MongoDb/angular/package.json index 18d8a6ca48..8b5656ea8f 100644 --- a/samples/BookStore-Angular-MongoDb/angular/package.json +++ b/samples/BookStore-Angular-MongoDb/angular/package.json @@ -13,11 +13,11 @@ }, "private": true, "dependencies": { - "@abp/ng.account": "^2.1.0", - "@abp/ng.identity": "^2.1.0", - "@abp/ng.setting-management": "^2.1.0", - "@abp/ng.tenant-management": "^2.1.0", - "@abp/ng.theme.basic": "^2.1.0", + "@abp/ng.account": "~2.2.0", + "@abp/ng.identity": "~2.2.0", + "@abp/ng.setting-management": "~2.2.0", + "@abp/ng.tenant-management": "~2.2.0", + "@abp/ng.theme.basic": "~2.2.0", "@angular/animations": "~8.2.14", "@angular/common": "~8.2.14", "@angular/compiler": "~8.2.14", diff --git a/templates/app/angular/package.json b/templates/app/angular/package.json index faf50f8c72..20b3d0ea23 100644 --- a/templates/app/angular/package.json +++ b/templates/app/angular/package.json @@ -1,5 +1,5 @@ { - "name": "ng9-abp", + "name": "MyProjectName", "version": "0.0.0", "scripts": { "ng": "ng", diff --git a/templates/module/angular/package.json b/templates/module/angular/package.json index 34ea25858d..bd98cfb809 100644 --- a/templates/module/angular/package.json +++ b/templates/module/angular/package.json @@ -12,11 +12,11 @@ }, "private": true, "dependencies": { - "@abp/ng.account": "^2.2.0", - "@abp/ng.theme.basic": "^2.2.0", - "@abp/ng.identity": "^2.2.0", - "@abp/ng.tenant-management": "^2.2.0", - "@abp/ng.setting-management": "^2.2.0", + "@abp/ng.account": "~2.2.0", + "@abp/ng.theme.basic": "~2.2.0", + "@abp/ng.identity": "~2.2.0", + "@abp/ng.tenant-management": "~2.2.0", + "@abp/ng.setting-management": "~2.2.0", "@angular/animations": "~8.2.14", "@angular/common": "~8.2.14", "@angular/compiler": "~8.2.14", diff --git a/templates/module/angular/projects/my-project-name-config/package.json b/templates/module/angular/projects/my-project-name-config/package.json index 6900d392a2..a7e2e66bf3 100644 --- a/templates/module/angular/projects/my-project-name-config/package.json +++ b/templates/module/angular/projects/my-project-name-config/package.json @@ -2,6 +2,6 @@ "name": "my-project-name.config", "version": "0.0.1", "peerDependencies": { - "@abp/ng.core": ">=2.2.0" + "@abp/ng.core": "~2.2.0" } } diff --git a/templates/module/angular/projects/my-project-name/package.json b/templates/module/angular/projects/my-project-name/package.json index d9e41b2608..d41e9f74be 100644 --- a/templates/module/angular/projects/my-project-name/package.json +++ b/templates/module/angular/projects/my-project-name/package.json @@ -2,7 +2,7 @@ "name": "my-project-name", "version": "0.0.1", "dependencies": { - "@abp/ng.theme.shared": "^2.2.0", + "@abp/ng.theme.shared": "~2.2.0", "my-project-name.config": "^0.0.1" } } diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/sl.json b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/sl.json new file mode 100644 index 0000000000..687d42579c --- /dev/null +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/sl.json @@ -0,0 +1,6 @@ +{ + "culture": "sl", + "texts": { + "ManageYourProfile": "Upravljajte svojim profilom" + } +} \ No newline at end of file diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hans.json b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hans.json new file mode 100644 index 0000000000..99586f01c9 --- /dev/null +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hans.json @@ -0,0 +1,6 @@ +{ + "culture": "zh-Hans", + "texts": { + "ManageYourProfile": "管理个人资料" + } +} \ No newline at end of file diff --git a/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hant.json b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hant.json new file mode 100644 index 0000000000..ceea055597 --- /dev/null +++ b/templates/module/aspnet-core/src/MyCompanyName.MyProjectName.Domain.Shared/Localization/MyProjectName/zh-Hant.json @@ -0,0 +1,6 @@ +{ + "culture": "zh-Hant", + "texts": { + "ManageYourProfile": "管理個人資料" + } +} \ No newline at end of file