How to Convert Startup Code from asp.net core 2.1 to Startup Code asp.net Core 3.1 - asp.net-core-3.1

I want to upgrate my project from asp.net core 2.1 to asp.net core 3.1, I have changed the SDK to 3.1 like the below code, but I did not know what to change in my startup code as per need of startup code 3.1
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
Now I want to update my startup Code from asp.net core 2.1 to asp.net core 3.1 Please modify the below startup code as per the need of asp.net core 3.1
Here is my Startup Code Version asp.net Core 2.1
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
var connection = Configuration.GetConnectionString("DBconnection");
services.AddDbContext<HoshmandDBContext>(option => option.UseSqlServer(connection));
services.AddAuthentication(option =>
{
option.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
option.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
option.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.LoginPath = "/Logins/UserLogin/";
options.AccessDeniedPath = "/AccessDenied";
options.Cookie.Expiration = new TimeSpan(10,00,00);
});
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromHours(2);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
services.ConfigureApplicationCookie(option =>
{
option.ExpireTimeSpan = TimeSpan.FromMinutes(540);
});
services.AddAuthorization(options =>
{
options.AddPolicy("HasAccess", policy => policy.AddRequirements(new HasAccessRequirment()));
});
services.AddTransient<IAuthorizationHandler, HasAccessHandler>();
services.AddTransient<IMvcControllerDiscovery, MvcControllerDiscovery>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();
app.UseCookiePolicy();
app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=UserProfiles}/{action=Index}/{id?}");
});
}
}
Package Manger Reference Code asp.net core 2.1 need to update to asp.net core 3.1
<ItemGroup>
<PackageReference Include="BCrypt-Core" Version="2.0.0" />
<PackageReference Include="ClosedXML" Version="0.97.0" />
<PackageReference Include="Magick.NET-Q16-AnyCPU" Version="7.8.0" />
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.7" />
<PackageReference Include="microsoft.aspnetcore.app" Version="2.1.4" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.1.3" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.14" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.1.14" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.14">
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="2.1.1" />
<PackageReference Include="Microsoft.VisualStudio.Web.BrowserLink" Version="2.1.1" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.10" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="5.0.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.7" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Tools" Version="2.0.3" />
</ItemGroup>
Here is my UserProfile Controller Code, by default from Startup EndpointRouting user will come to this page, if a user is not login it will redirect to the login page
[Authorize]
public async Task<IActionResult> Index(bool? passIsChanged = null) // userId
{
if (passIsChanged != null)
{
ViewBag.isSuccessed = passIsChanged;
}
int id = Convert.ToInt32(User.Identity.Name);
var user = _context.UserAccountTbs.FirstOrDefault(a => a.UserId == id);
if (user == null)
{
return RedirectToAction("UserLogin", "Logins");
}
userprofile = new UserProfile
{
UserId = user.UserId,
Name = users.FirstName,
};
return await Task.FromResult(View(userprofile));
}
Here is My Login Controller code, if user is not login it return a new login model, if user is login it will check its password and user name then and validate it,
public LoginsController(HoshmandDBContext context) : base(context)
{
}
[HttpGet]
public async Task<IActionResult> UserLogin()
{
return await Task.Run(() => View(new Login()));
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> UserLogin([FromForm] Login currentUser)
{
if (ModelState.IsValid)
{
var properties = new AuthenticationProperties
{
IsPersistent = currentUser.RememberMe,
AllowRefresh = false,
ExpiresUtc = DateTimeOffset.UtcNow.AddDays(10)
};
UserAccountTb user = _context.UserAccountTbs.FirstOrDefault(a => a.UserName.Equals(currentUser.UserName));
if (user == null)
{
TempData["UserLoginFailed"] = "Login Failed: Please Enter Correct Credential";
return View();
}
else
{
// validate the password and login if not validate
show a message that enter correct password
}

First, you need to change SDK in .csproj file:
<TargetFramework>netcoreapp3.1</TargetFramework>
and remove this line:
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.1.2" PrivateAssets="All" />
Second, update the Nuget package you added to 3.1
corresponding version:
Then, you can change your Startup.cs:
options.Cookie.Expiration is not supported in .net 3.1, you need to use options.ExpireTimeSpan to replace it.
Change services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); to services.AddMvc();.
The use of app.UseMvc(); to define endpoints will no longer be supported in .net 3.1. You need to use app.UseRouting(); and app.UseEndpoints();.
In the end, your Startup.cs should look like this:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
var connection = Configuration.GetConnectionString("DBconnection");
services.AddDbContext<HoshmandDBContext>(option => option.UseSqlServer(connection));
services.AddAuthentication(option =>
{
option.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
option.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
option.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.LoginPath = "/Logins/UserLogin/";
options.AccessDeniedPath = "/AccessDenied";
options.ExpireTimeSpan = new TimeSpan(10, 00, 00);
});
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromHours(2);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
services.ConfigureApplicationCookie(option =>
{
option.ExpireTimeSpan = TimeSpan.FromMinutes(540);
});
services.AddAuthorization(options =>
{
options.AddPolicy("HasAccess", policy => policy.AddRequirements(new HasAccessRequirment()));
});
services.AddTransient<IAuthorizationHandler, HasAccessHandler>();
services.AddTransient<IMvcControllerDiscovery, MvcControllerDiscovery>();
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseCookiePolicy();
app.UseSession();
app.UseEndpoints(endpoint => {
endpoint.MapControllerRoute(
name: "default",
pattern: "{controller=UserProfiles}/{action=Index}/{id?}");
});
}
}
In addition, I added the [Authorize] attribute to the Index method of UserProfilesController to verify redirection:
public class UserProfilesController : Controller
{
[Authorize]
public IActionResult Index()
{
return View();
}
}
LoginsController:
public class LoginsController : Controller
{
public IActionResult UserLogin()
{
return View();
}
}
Then run the project:
Right click on your project and select "Manage NuGet Packages", you can see the references of all your packages. Then select the corresponding package and update to the appropriate version:

Related

How to play text to speech using xamarin.essential

I want to play text to speech but I can't hear anything using Xamarin.essential. I 'm sure I don't have muted smartphone. The code:
using Xamarin.Essential
private void PlayTextMethod()
{
if(do sth)
{
SpeekFrommethodAsync();
}
}
private async SpeekFrommethodAsync()
{
await TextToSpeech.SpeakAsync(Tasklabel.Text, new SpeechOptions
{
Volume = 1f
});
}
If your project's Target Android version is set to Android 11 (R API 30),
Open the AndroidManifest.xml file under the Properties folder and add the following inside of the manifest node:
<queries>
<intent>
<action android:name="android.intent.action.TTS_SERVICE" />
</intent>
</queries>
And try this in your method:
public async Task SpeakNow()
{
var locales = await TextToSpeech.GetLocalesAsync();
// Grab the first locale
var locale = locales.FirstOrDefault();
var settings = new SpeechOptions()
{
Volume = .75f,
Pitch = 1.0f,
Locale = locale
};
await TextToSpeech.SpeakAsync("Hello World", settings);
}
Refer: https://learn.microsoft.com/en-us/xamarin/essentials/text-to-speech?context=xamarin%2Fandroid&tabs=android
I made a demo and I tested it on the Android 12 emulator. I found the Text-to-speech function can not speak the words which belong to another country. So I added a picker to choose the country.
Here is the code in MainPage.xaml
<StackLayout>
<Frame BackgroundColor="#2196F3" Padding="24" CornerRadius="0">
<Label Text="Text to speech sample!" HorizontalTextAlignment="Center" TextColor="White" FontSize="36"/>
</Frame>
<Picker x:Name="Languages"></Picker>
<Entry x:Name="TextToSpeak"></Entry>
<Button Text="Speak Please" Clicked="Button_Clicked"></Button>
</StackLayout>
And here is the code in MainPage.xaml.cs.
using Xamarin.Essentials;
public partial class MainPage : ContentPage
{
IEnumerable<Locale> locales;
public MainPage()
{
InitializeComponent();
}
protected async override void OnAppearing()
{
base.OnAppearing();
locales = await TextToSpeech.GetLocalesAsync();
// add the locales to the picker
foreach(var locale in locales)
{
Languages.Items.Add(locale.Name);
}
}
// realize the button
private void Button_Clicked(object sender, EventArgs e)
{
if(Languages.SelectedIndex > 0)
{
// use the TextToSpeech to read the words in the Entry.
TextToSpeech.SpeakAsync(TextToSpeak.Text, new SpeechOptions
{
Locale = locales.Single(locales=> locales.Name == Languages.SelectedItem.ToString())
});
}
}
}
This is the view of the project.

form validation with FluentValidation and MudBlazor in dialog

I am using MudBlazor and i want validation form with fluentvalidation (EditForm) in dialog.
BookDialog.razor
<MudDialog>
<DialogContent>
<EditForm Model="#model">
<FluentValidationValidator />
<MudTextField Label="#L["Code"]" #bind-Value="model.Code" For="() => model.Code" />
<MudTextField Label="#L["Title"]" #bind-Value="model.Title" For="() => model.Title" />
</EditForm>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel">Cancel</MudButton>
<MudButton Color="MudBlazor.Color.Primary" OnClick="Submit">Ok</MudButton>
</DialogActions>
</MudDialog>
#code{
[Parameter]
public BookInfo model { get; set; }
[CascadingParameter] MudDialogInstance MudDialog { get; set; }
private void Cancel()
{
MudDialog.Cancel();
}
private void Submit()
{
MudDialog.Close(DialogResult.Ok<BookInfo>(model));
}
}
Index.razor
#inject IDialogService dialogService
<MudIconButton Icon="#Icons.Material.Filled.Add" OnClick="CreateBook"></MudIconButton>
#code{
private async Task CreateBook()
{
var parameters = new dialogParameters();
parameters.Add("model", new BookInfo());
var dialog = await dialogService.Show<BookDialog>("Create A Book", parameters).Result;
if (dialog.Data != null)
{
//....
}
}
}
BookValidator.cs
public class BookValidator : AbstractValidator<BookInfo>
{
public BookValidator()
{
CascadeMode = CascadeMode.Stop;
RuleFor(x => x.Code).NotEmpty().WithMessage("*");
RuleFor(x => x.Title).NotEmpty().WithMessage("*");
}
}
Error messages show correctly, but closes the dialog and not stop form submiting.
I fount a solution by validate model directly.
private void Submit()
{
var validator = new BookValidator();
var result = validator.Validate(model);
if (result.IsValid)
{
MudDialog.Close(DialogResult.Ok<BookInfo>(model));
}
}

Xamarin Forms with Prism - Problem with using WebService

Can anyone help me? I created simply project Xamarin Forms with Prism i VS2017 on Android (screen). I used Prism Template Pack. I would like connect project with my WebService. here is a link to screen of all project
I have two projects PrismCoursApp and PrismCoursApp.Droid. First project contains SecondPageViewModel.cs where I try use connected WebService (wsMES) but I can't add namespace with PrismCoursApp.Droid.
The namespace of project PrismCourseApp.Android is PrismCourseApp.Droid and
PrismCourseApp.Android depends on PrismCourseApp.
I could add reference to Web service only in PrismCoursApp.Android project but I would like to use it in SecondPageViewModel.cs in PrismCourseApp.
Can someone tell me what I'm doing wrong?
Thanks
SecondPageViewModel.cs
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;
using PrismCourseApp.Models;
using System.Collections.ObjectModel;
namespace PrismCourseApp.ViewModels
{
public class SecondPageViewModel : BindableBase, INavigationAware
{
//zmienna do WebService
//wsMES.WSwitoMES ws = new wsMES.WSwitoMES();
private string _title;
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
private string _UserCode;
public string UserCode
{
get { return _UserCode; }
set { SetProperty(ref _UserCode, value); }
}
private string _LokalizCode;
public string LokalizCode
{
get { return _LokalizCode; }
set { SetProperty(ref _LokalizCode, value); }
}
public SecondPageViewModel()
{
UserCode = AppStateTest.User;
LokalizCode = AppStateTest.CurrentCode;
Title = "Użytkownik/Lokalizacja";
}
public void OnNavigatedFrom(INavigationParameters parameters)
{
}
public void OnNavigatedTo(INavigationParameters parameters)
{
if (parameters.ContainsKey("par1"))
{
string par1 = (string)parameters["par1"];
string par2 = (string)parameters["par2"];
}
}
public void OnNavigatingTo(INavigationParameters parameters)
{
}
}
}
SecondPage.axml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="PrismCourseApp.Views.SecondPage"
BackgroundColor="White"
Title="{Binding Title}"
xmlns:b="clr-namespace:Prism.Behaviors;assembly=Prism.Forms"
xmlns:c="clr-namespace:PrismCourseApp.Converters;assembly=PrismCourseApp">
<ContentPage.Resources>
<ResourceDictionary>
<!--<c:ItemTappedEventArgsConverter x:Key="itemTappedEventArgsConverter" />-->
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout
Spacing="20">
<Label
Text="Zalogowany użytkownik:"
TextColor="Gray"/>
<Label
Text="{Binding UserCode}"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<Label
Text="Lokalizacja:"
TextColor="Gray"/>
<Label
Text="{Binding LokalizCode}"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
<ListView
x:Name="lstView">
<!--ItemsSource="{Binding MyDatas}">-->
<!--<ListView.Behaviors>
<b:EventToCommandBehavior EventName="ItemTapped"
Command="{Binding ItemTappedCommand}"
EventArgsConverter="{StaticResource itemTappedEventArgsConverter}" />
</ListView.Behaviors>-->
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding name}" Detail="{Binding comment}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
SecondPage.axml.cs
using Xamarin.Forms;
using PrismCourseApp.Models;
using System.Collections.ObjectModel;
namespace PrismCourseApp.Views
{
public partial class SecondPage : ContentPage
{
//Elementy do ListView (klasa MyDate w PrismCourseApp)
private ObservableCollection<MyDate> MyDatas { get; set; }
public SecondPage()
{
InitializeComponent();
MyDatas = new ObservableCollection<MyDate>();
lstView.ItemsSource = MyDatas;
for (int i = 0; i < 30; i++)
{
MyDatas.Add(new MyDate
{
name = "Pozycja " + (i+1).ToString(),
comment = "Miejsce na szczegóły " + (i+1).ToString()
});
}
}
}
}
MainActivity.cs in Android Project
using Android.App;
using Android.Content.PM;
using Android.OS;
using Prism;
using Prism.Ioc;
namespace PrismCourseApp.Droid
{
[Activity(Label = "PrismCourseApp", Icon = "#drawable/ic_launcher", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App(new AndroidInitializer()));
}
}
public class AndroidInitializer : IPlatformInitializer
{
public void RegisterTypes(IContainerRegistry container)
{
// Register any platform specific implementations
}
}
}
Summarising the discussion in the comments above:
One should not make a web service dependent upon something specific to one of many client platforms. It's preferable to put the service's interface between the server and the part of the client that's shared between the different client implementations.
Say you have a mobile app for Android and IOS. Then you'll have two projects MyApp.Droid and MyApp.IOS for the respective client-specific implementations. Also, there's a project that both of them reference, and that (hopefully) contains most of your app's client-side logic: MyApp.Logic.
Now for the server: you have the MyApp.Server project that implements the service. If you need to define interfaces to communicate between the app and the service (WCF comes to mind), you define a project referenced by both the client-side logic (MyApp.Logic) and the server implementation (MyApp.Server): MyApp.Interface.
MyApp.Droid & MyApp.IOS -ref-> MyApp.Logic -ref-> MyApp.Interface <-ref- MyApp.Server

How to add additional attachments in the workflow?

In my document management process it is often necessary to provide some additional documents (e.g. list of comments, list of differences, some screenshot, etc).
These additional documents would be convenient to add in the Activiti forms. I would like to be able to add documents at the initiation stage of a business process and at the stage of the revise.
For this, I added an aspect with associations in the workflow-model.xml (relevant part):
...
<type name="mswf:activitiRevise">
...
<mandatory-aspects>
...
<aspect>mswf:attachments</aspect>
</mandatory-aspects>
</type>
...
<aspect name="mswf:attachments">
<associations>
<association name="mswf:package">
<source>
<mandatory>false</mandatory>
<many>true</many>
</source>
<target>
<class>cm:content</class>
<mandatory>false</mandatory>
<many>true</many>
</target>
</association>
</associations>
</aspect>
...
etc
In share-config-custom.xml I have the following (relevant part):
...
<config evaluator="task-type" condition="bpm:startTask">
<forms>
<form id="workflow-details">
<field-visibility>
...
<show id="mswf:package" />
</field-visibility>
<appearance>
...
<set id="attachments" appearance="title" label-id="Additional docs" />
<field id="mswf:package" set="attachments" />
</appearance>
</form>
<form>
<field-visibility>
<show id="mswf:package" />
...
</field-visibility>
<appearance>
<set id="attachments" appearance="title" label-id="Additional docs" />
<field id="mswf:package" set="attachments" />
...
</appearance>
</form>
</forms>
</config>
...
etc
Now I have an additional field where I can choose the appropriate documents.
It works - I added some documents and can see them at all of the stages of the document management process.
The problem occurs when I try to change a set of initially selected files. For example, when I try to add a new one. If I add a new one (or remove) - changes are not saved and in the next task, I see the same documents that were selected at the beginning.
To get control of this behavior, I developed WebScript, in which I try to manage properties. I call the WebScript from Share in the getAddedItems() method:
/**
* Returns items that have been added to the current value
*
* #method getAddedItems
* #return {array}
*/
getAddedItems: function ObjectFinder_getAddedItems() {
var addedItems = [],
currentItems = Alfresco.util.arrayToObject(this.options.currentValue.split(","));
for (var item in this.selectedItems) {
if (this.selectedItems.hasOwnProperty(item)) {
if (!(item in currentItems)) {
addedItems.push(item);
}
}
}
...
// here the call to the WebScript
return addedItems;
},
Part of my Java-backed WebScript:
...
public class WorkflowAttachmentsManipulator extends DeclarativeWebScript {
private static final String WORKFLOW_MODEL_URI = "...";
private WorkflowService workflowService;
#Override
protected Map<String, Object> executeImpl(WebScriptRequest req, Status status) {
Map<String, Object> model = new HashMap<>();
String taskId = req.getParameter("taskId");
String addedItem = req.getParameter("addedItem");
WorkflowTask workflowTask = workflowService.getTaskById(taskId);
Map<QName, Serializable> taskProperties = workflowTask.getProperties();
...
taskProperties.replace(
QName.createQName(WORKFLOW_MODEL_URI, "package"), oldValue, addedItem);
workflowService.updateTask(taskId, taskProperties, null, null);
...
I'm trying to replace the selected files with some arbitrary and the replace(...) method returns true. In alfrescotomcat-stdout.2017-09-06.log I also see that the property has been replaced:
Before calling the WebScript (two files in the package):
key: {WORKFLOW_MODEL_URI_HERE}package
value: [workspace://SpacesStore/7f980005-2a1b-49a5-a8ff-ce9dff31a98a,
workspace://SpacesStore/30d9122f-4467-451b-aeab-ca8b164f7769]
After calling the WebScript (one file in the package):
key: {WORKFLOW_MODEL_URI_HERE}package
value: workspace://SpacesStore/1a0b110f-1e09-4ca2-b367-fe25e4964a4e
After updating the form at the current task I see my new file.
But the value is not saved (lost) after revise / review and in the next task I see the same files. Let's say, the task ID for the current user was activiti$204587, then it became equals activiti$204647...
I added some debugging code to the BPMN diagram and found that the contents of mswf_package did not change after the calling the WebScript.
In 'Submit', main config:
for(var i = 0; i < mswf_package.size(); i++) {
logger.log(mswf_package.get(i).nodeRef);
}
Output:
DEBUG [repo.jscript.ScriptLogger] [http-apr-8080-exec-9] workspace://SpacesStore/7f980005-2a1b-49a5-a8ff-ce9dff31a98a
DEBUG [repo.jscript.ScriptLogger] [http-apr-8080-exec-9] workspace://SpacesStore/30d9122f-4467-451b-aeab-ca8b164f7769
In 'Review Task', listeners of the create and complete events:
for(var i = 0; i < mswf_package.size(); i++) {
logger.log(mswf_package.get(i).nodeRef);
}
Output:
DEBUG [repo.jscript.ScriptLogger] [http-apr-8080-exec-9] workspace://SpacesStore/7f980005-2a1b-49a5-a8ff-ce9dff31a98a
DEBUG [repo.jscript.ScriptLogger] [http-apr-8080-exec-9] workspace://SpacesStore/30d9122f-4467-451b-aeab-ca8b164f7769
How to add additional attachments in the workflow? Is it possible?
A set of strings with NodeRefs can be passed to the following WebScript, for example:
public class WorkflowAttachmentsManipulator extends DeclarativeWebScript {
private static final String WORKFLOW_MODEL_URI = "...";
private WorkflowService workflowService;
#Override
protected Map<String, Object> executeImpl(WebScriptRequest req, Status status) {
Map<String, Object> model = new HashMap<>();
String taskId = req.getParameter("taskId");
String addedItems = req.getParameter("addedItems");
String oldValue = "";
WorkflowTask workflowTask = workflowService.getTaskById(taskId);
Map<QName, Serializable> taskProperties = workflowTask.getProperties();
Iterator taskIterator = taskProperties.entrySet().iterator();
while(taskIterator.hasNext()) {
Map.Entry taskPair = (Map.Entry)taskIterator.next();
Object key = taskPair.getKey();
if(key != null &&
key.toString().equalsIgnoreCase("{" + WORKFLOW_MODEL_URI + "}package")) {
if(taskPair.getValue() != null) {
oldValue = taskPair.getValue().toString();
if(!oldValue.equals("[]")) {
oldValue = oldValue.replaceAll("[\\[\\]]", "");
addedItems = "[" + oldValue + "," + addedItems + "]";
} else {
if(addedItems.indexOf(",") > 0) {
addedItems = "[" + addedItems + "]";
}
}
}
taskProperties.replace(
QName.createQName(WORKFLOW_MODEL_URI, "package"),
oldValue,
addedItems);
workflowService.updateTask(workflowTask.getId(),
taskProperties, null, null);
break;
}
}
...
}
public WorkflowService getWorkflowService() {
return workflowService;
}
public void setWorkflowService(WorkflowService workflowService) {
this.workflowService = workflowService;
}
}
This code overrides attachments for the certain task.
Additional files need to differentiate from those that are involved in the document management process. It can be done, for example, as follows:
/**
* Returns items that have been added to the current value
*
* #method getAddedItems
* #return {array}
*/
getAddedItems: function ObjectFinder_getAddedItems() {
var addedItems = [],
currentItems = Alfresco.util.arrayToObject(this.options.currentValue.split(","));
var attachments = [];
for (var item in this.selectedItems) {
if (this.selectedItems.hasOwnProperty(item)) {
if (!(item in currentItems)) {
// modified for differentiation
if (this.options.displayMode == "items") {
attachments.push(item);
} else {
addedItems.push(item);
}
}
}
}
...
// call to the WebScript with attachments
// modified for merge
return addedItems.concat(attachments);
},
For saving the overridden attachments in the process variable it is necessary to define the listener of the complete event.
Moreover, it is possible to "pass" files by the chain from task to task (with changes) by using this technique:
Listener of the complete event:
public class TaskCompleteListener implements TaskListener {
#Override
public void notify(DelegateTask delegateTask) {
DelegateExecution execution = delegateTask.getExecution();
execution.setVariable("mswf_package", delegateTask.getVariable("mswf_package"));
}
}
Listener of the create event:
public class TaskCreateListener implements TaskListener {
#Override
public void notify(DelegateTask delegateTask) {
DelegateExecution execution = delegateTask.getExecution();
delegateTask.setVariable("mswf_package", execution.getVariable("mswf_package"));
}
}
This solved my issue.

CodeFluent BOM Producer select namespace

I would like to know if there is a way to select namespaces generated by one BOM producer. I would like to chose the target project for every namespace one by one in my model.
The Business Object Model (BOM) Producer does not allow you to select namespaces to generate. However you can create a CodeDom SubProducer which "remove" generated class before they are written to the disk. Here are some examples about sub-producers:
http://www.softfluent.com/documentation/webframe.html?CustomSubProducer_Topic.html
http://blog.codefluententities.com/2013/05/16/creating-a-custom-sub-producer-to-only-generate-resources/
The following subproducer allows to select namespaces to produce:
using System;
using System.CodeDom;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Xml;
using CodeFluent.Model;
using CodeFluent.Model.Common.Design;
using CodeFluent.Model.Design;
using CodeFluent.Producers.CodeDom;
using CodeFluent.Runtime.Utilities;
namespace SelectNamespacesSubProducer
{
[Category("Business Layer Producers")]
[DisplayName("SubProducer Select Namespaces")]
[Producer(Constants.SubNamespaceUri, Constants.SubProducerNamespacePrefix)]
public class SelectNamespacesSubProducer : ICodeDomSubProducer
{
private CodeDomBaseProducer _baseProducer;
private CodeFluent.Producers.CodeDom.SubProducer _subProducer;
[Description("Determines if sub producer is enabled.")]
[Category("Configuration")]
[DefaultValue(true)]
[DisplayName("Is Enabled")]
[ModelLevel(ModelLevel.Normal)]
public virtual bool Enabled
{
get
{
return XmlUtilities.GetAttribute(Element, "enabled", true);
}
set
{
XmlUtilities.SetAttribute(Element, "enabled", value.ToString().ToLowerInvariant());
}
}
[Description("Determines the list of namespaces to produce.")]
[Category("Configuration")]
[DefaultValue(null)]
[DisplayName("Namespaces")]
[ModelLevel(ModelLevel.Normal)]
public virtual string Namespaces
{
get
{
return XmlUtilities.GetAttribute(Element, "namespaces", (string)null);
}
set
{
XmlUtilities.SetAttribute(Element, "namespaces", value);
}
}
public virtual void Initialize(CodeDomBaseProducer baseProducer, CodeFluent.Producers.CodeDom.SubProducer subProducer, IDictionary context)
{
_baseProducer = baseProducer;
_subProducer = subProducer;
baseProducer.CodeDomProduction += OnCodeDomProduction;
}
public virtual void Produce(IDictionary context, CodeCompileUnit unit)
{
}
public virtual void Terminate(IDictionary context)
{
}
private void OnCodeDomProduction(object sender, CodeDomProductionEventArgs e)
{
if (!Enabled)
return;
if (Namespaces == null)
return;
List<string> namespaces = ConvertUtilities.SplitToList<string>(Namespaces, ',', ';', '|');
switch (e.EventType)
{
case CodeDomProductionEventType.EntityCreating:
case CodeDomProductionEventType.SetCreating:
case CodeDomProductionEventType.EnumerationCreating:
var baseType = (BaseType)e.Argument;
if (!namespaces.Contains(baseType.Namespace))
{
e.Cancel = true;
FakeProduceUnit(baseType);
}
break;
}
}
private void FakeProduceUnit(BaseType baseType)
{
if (baseType == null)
throw new ArgumentNullException("baseType");
if (!_baseProducer.MustProduce(baseType, CodeFluent.Producers.CodeDom.Constants.ModelProducerNamespaceUri))
return;
Set set = baseType as Set;
if (set != null)
{
if (!_baseProducer.MustProduce(set.ItemEntity, CodeFluent.Producers.CodeDom.Constants.ModelProducerNamespaceUri))
return;
}
// determine the final target path where the file should have gone, and pretend it's been generated
string path = ((CodeDomProducer)_baseProducer).GetTargetPath(baseType);
path = _baseProducer.GetProductionTargetPath(baseType, path, false, false);
if (File.Exists(path))
{
_baseProducer.AddToGeneratedFiles(path);
}
}
public XmlElement Element
{
get
{
if (_subProducer == null)
throw new CodeFluentCodeDomProducerException(GetType().FullName);
return _subProducer.Element;
}
set
{
}
}
}
}
You can use it this way:
<cf:producer name="Business Object Model (BOM)" typeName="CodeFluent.Producers.CodeDom.CodeDomProducer, CodeFluent.Producers.CodeDom">
<cf:configuration compileWithVisualStudio="true" compile="false" codeDomProviderTypeName="CSharp" targetDirectory="..\Sample" cfx:targetProject="..\Sample\Sample.csproj" cfx:targetProjectLayout="Update">
<subProducer typeName="SelectNamespacesSubProducer.SelectNamespacesSubProducer, SelectNamespacesSubProducer"
namespaces="Sample.NS1, Sample.NS2" />
</cf:configuration>
</cf:producer>
<cf:entity name="Customer" namespace="Sample.NS1">
<cf:property name="Id" key="true" />
</cf:entity>
<cf:entity name="Order" namespace="Sample.NS2">
<cf:property name="Id" key="true" />
</cf:entity>