CML to JDL file using JHipster is not working and the file that is generated using the templates are incomplete
below the details of the code
The CML file that I used for this JDL generation is here
https://github.com/ContextMapper/context-mapper-standalone-example/blob/master/src/main/cml/Insurance-Example-Model.cml
GenericContentGenerator genericContentGenerator = new GenericContentGenerator();
genericContentGenerator.setFreemarkerTemplateFile(getTemplateFile(projectType)); genericContentGenerator.setTargetFileName(inputFileName + ".jdl");
contextMapper.callGenerator(cmlResource, genericContentGenerator);
logger.info("JDL Files Successfully created");
the Freemarker template that i used for this below
<#--
This template generates a JHipster JDL file.
Please consult our online tutorial https://contextmapper.org/docs/jhipster-microservice-generation/ to learn how to use it.
-->
<#--
variables to collect entity names and references:
-->
<#assign allEntityNames = [] />
<#assign oneToManyRefs = [] />
<#assign oneToOneRefs = [] />
<#--
counter to give microservices different ports
-->
<#assign portCounter = 8083 />
<#--
loop to collect entity data per Bounded Context (BC) and create application plus microservice for each BC
-->
<#list filterStructuralBoundedContexts(boundedContexts) as bc>
<#assign entities = [] />
<#assign entityNames = [] />
<#list bc.aggregates as agg>
<#assign entities = entities + agg.domainObjects?filter(dob -> instanceOf(dob, Entity) || instanceOf(dob, ValueObject))>
</#list>
<#assign entityNames = entities?map(e -> e.name)>
<#assign allEntityNames = allEntityNames + entityNames>
<#if entities?has_content>
/* Bounded Context ${bc.name} */<#lt>
<#list entities as entity>
entity ${entity.name} {
<#list entity.attributes as attribute>
${attribute.name} ${mapAttributeType(attribute.type)}
</#list>
}
<#list entity.references as reference>
<#if reference.domainObjectType?has_content && (instanceOf(reference.domainObjectType, Entity) || instanceOf(reference.domainObjectType, ValueObject)) && entityNames?seq_contains(reference.domainObjectType.name)>
<#if reference.collectionType?has_content && reference.collectionType.name() != "NONE">
<#assign oneToManyRefs = oneToManyRefs + [ entity.name + "{" + reference.name + "} to " + reference.domainObjectType.name ]>
<#else>
<#assign oneToOneRefs = oneToOneRefs + [ entity.name + "{"+ reference.name + "} to " + reference.domainObjectType.name ]>
</#if>
</#if>
</#list>
</#list>
microservice ${entityNames?join(", ")} with ${bc.name}<#lt>
</#if>
<#assign portCounter++ />
application {
config {
baseName ${bc.name}
packageName ${bc.name?lower_case}
applicationType microservice
serverPort ${portCounter?int?c}
enableSwaggerCodegen true
clientFramework react
prodDatabaseType postgresql
}
<#if entityNames?has_content>
entities ${entityNames?join(", ")}
</#if>
}
</#list>
<#--
here we print the collected references as relationships:
-->
/* relationships */
<#if oneToManyRefs?has_content>
relationship OneToMany {<#lt>
<#list oneToManyRefs as reference>
${reference}
</#list>
}<#lt>
</#if>
<#if oneToOneRefs?has_content>
relationship OneToOne {<#lt>
<#list oneToOneRefs as reference>
${reference}
</#list>
}<#lt>
</#if>
<#--
create a microservice gateway (user interface)
-->
/* microservice gateway app */
application {
config {
baseName gateway
packageName gateway
applicationType gateway
serverPort 8080
}
<#if allEntityNames?has_content>
entities ${allEntityNames?join(", ")}
</#if>
}
<#--
additional configurations for the JHipster generator:
-->
/* additional options */
dto * with mapstruct
service * with serviceImpl
<#-- Data type mapping: -->
<#function mapAttributeType inputType>
<#if inputType == "String">
<#return "String">
<#elseif inputType == "int" || inputType == "Integer">
<#return "Integer">
<#elseif inputType == "long" || inputType == "Long">
<#return "Long">
<#elseif inputType == "boolean" || inputType == "Boolean">
<#return "Boolean">
<#elseif inputType == "Date" || inputType == "DateTime" || inputType == "Timestamp">
<#return "LocalDate">
<#elseif inputType == "BigDecimal" || inputType == "BigInteger">
<#return "BigDecimal">
<#elseif inputType == "double" || inputType == "Double">
<#return "Double">
<#elseif inputType == "float" || inputType == "Float">
<#return "Float">
<#elseif inputType == "Key">
<#return "UUID">
<#elseif inputType == "Blob" || inputType =="Object[]">
<#return "Blob">
<#elseif inputType == "Clob">
<#return "TextBlob">
<#else>
<#return "Blob">
</#if>
</#function>
now when I generate the JDL file , the jdl file is getting created but seems to be incomplete
O/P of the jdl file that is created is below
/* Bounded Context CustomerManagementContext */
entity Customer {
firstname String
lastname String
}
entity Address {
street String
postalCode Integer
city String
}
entity SocialInsuranceNumber {
sin String
}
microservice Customer, Address, SocialInsuranceNumber with CustomerManagementContext
application {
config {
baseName CustomerManagementContext
packageName customermanagementcontext
applicationType microservice
serverPort 8084
enableSwaggerCodegen true
clientFramework react
prodDatabaseType postgresql
}
entities Customer, Address, SocialInsuranceNumber
}
/* Bounded Context CustomerSelfServiceContext */
entity CustomerAddressChange {
}
entity UserAccount {
username String
}
microservice CustomerAddressChange, UserAccount with CustomerSelfServiceContext
application {
config {
baseName CustomerSelfServiceContext
packageName customerselfservicecontext
applicationType microservice
serverPort 8085
enableSwaggerCodegen true
clientFramework react
prodDatabaseType postgresql
}
entities CustomerAddressChange, UserAccount
}
/* Bounded Context PrintingContext */
entity PrintingJob {
printingId Integer
}
entity Document {
source Blob
template String
}
entity Template {
templateId Integer
templateName String
}
microservice PrintingJob, Document, Template with PrintingContext
application {
config {
baseName PrintingContext
packageName printingcontext
applicationType microservice
serverPort 8086
enableSwaggerCodegen true
clientFramework react
prodDatabaseType postgresql
}
entities PrintingJob, Document, Template
}
/* Bounded Context PolicyManagementContext */
entity Offer {
offerId Integer
price BigDecimal
}
entity Contract {
}
entity ContractId {
contractId Integer
}
entity Policy {
policyNr Integer
price BigDecimal
}
microservice Offer, Contract, ContractId, Policy with PolicyManagementContext
application {
config {
baseName PolicyManagementContext
packageName policymanagementcontext
applicationType microservice
serverPort 8087
enableSwaggerCodegen true
clientFramework react
prodDatabaseType postgresql
}
entities Offer, Contract, ContractId, Policy
}
/* Bounded Context RiskManagementContext */
entity CustomerRiskFactor {
totalRiskFactor Integer
}
entity Risk {
likelihood Integer
risk String
}
microservice CustomerRiskFactor, Risk with RiskManagementContext
application {
config {
baseName RiskManagementContext
packageName riskmanagementcontext
applicationType microservice
serverPort 8088
enableSwaggerCodegen true
clientFramework react
prodDatabaseType postgresql
}
entities CustomerRiskFactor, Risk
}
/* Bounded Context DebtCollection */
entity Debt {
debtNr Integer
creationDate LocalDate
paymentDate LocalDate
paymentDeadline LocalDate
price BigDecimal
status Blob
}
entity Dunning {
dunningNr Integer
dunningDate LocalDate
paymentDeadline LocalDate
}
microservice Debt, Dunning with DebtCollection
application {
config {
baseName DebtCollection
packageName debtcollection
applicationType microservice
serverPort 8089
enableSwaggerCodegen true
clientFramework react
prodDatabaseType postgresql
}
entities Debt, Dunning
}
/* Bounded Context NewBoundedContext1 */
entity Product {
productName String
}
entity ProductId {
productId Integer
}
microservice Product, ProductId with NewBoundedContext1
application {
config {
baseName NewBoundedContext1
packageName newboundedcontext1
applicationType microservice
serverPort 8090
enableSwaggerCodegen true
clientFramework react
prodDatabaseType postgresql
}
entities Product, ProductId
}
/* relationships */
relationship OneToMany {
Customer{addresses} to Address
CustomerRiskFactor{risks} to Risk
Debt{dunnigs} to Dunning
}
relationship OneToOne {
Customer{sin} to SocialInsuranceNumber
CustomerAddressChange{issuer} to UserAccount
PrintingJob{document} to Document
PrintingJob{template} to Template
Contract{identifier} to ContractId
Policy{contract} to Contract
Dunning{debt} to Debt
Product{identifier} to ProductId
}
/* microservice gateway app */
application {
config {
baseName gateway
packageName gateway
applicationType gateway
serverPort 8080
}
entities Customer, Address, SocialInsuranceNumber, CustomerAddressChange, UserAccount, PrintingJob, Document, Template, Offer, Contract, ContractId, Policy, CustomerRiskFactor, Risk, Debt, Dunning, Product, ProductId
}
/* additional options */
dto * with mapstruct
service * with serviceImpl
**g{debt} to Debt
Product{identifier} to ProductId
}
/* microservice gateway app */
application {
config {
baseName gateway
packageName gateway
applicationType gateway
serverPort 8080
}
entities Customer, Address, SocialInsuranceNumber, CustomerAddressChange, UserAccount, Pr**
In the above o/p, the fonts that are marked in bold are wrongly generated as an extra.
Not sure why
the Maven tags refered for this work is listed below
<!-- CML TO JDL Dependencies -->
<dependency>
<groupId>org.contextmapper</groupId>
<artifactId>context-mapper-dsl</artifactId>
<!-- we hv to validate the vulnerability-->
<version>6.3.0</version>
</dependency>
<!--<dependency>
<groupId>org.contextmapper</groupId>
<artifactId>context-map-generator</artifactId>
<version>1.2.1</version>
</dependency>-->
<dependency>
<groupId>org.eclipse.emf</groupId>
<artifactId>org.eclipse.emf.common</artifactId>
<version>2.26.0</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.sourceforge.plantuml/plantuml -->
<dependency>
<groupId>net.sourceforge.plantuml</groupId>
<artifactId>plantuml</artifactId>
<version>1.2022.8</version>
</dependency>
Need Help
Related
I want to create a plugin that can set the scheduled task interval. Shopware has database field called run_interval in Schedule_task table. The admin will enter a value, this value will change the run_interval in the scheduled_task table. Is this possible? How can I do that?
I'm assuming you want to use a plugin configuration for the input field. You could then set up a subscriber to listen for changes of the config field and update the scheduled task entity.
Service definition:
<service id="Foo\MyPlugin\Subscriber\SystemConfigChangedSubscriber">
<argument type="service" id="scheduled_task.repository"/>
<tag name="kernel.event_subscriber"/>
</service>
Subscriber:
class SystemConfigChangedSubscriber implements EventSubscriberInterface
{
private EntityRepositoryInterface $repository;
public function __construct(EntityRepositoryInterface $repository)
{
$this->repository = $repository;
}
public static function getSubscribedEvents(): array
{
return [
SystemConfigChangedEvent::class => 'onSystemConfigChanged',
];
}
public function onSystemConfigChanged(SystemConfigChangedEvent $event): void
{
if ($event->getKey() !== 'MyPluginName.config.nameOfMyField') {
return;
}
$context = Context::createDefaultContext();
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('name', 'name_of_the.task'));
$taskId = $this->repository->searchIds($criteria, $context)->firstId();
if (!$taskId) {
return;
}
$this->repository->update([
[
'id' => $taskId,
'runInterval' => $event->getValue(),
],
], $context);
}
}
I'm having a problem being able to get the list of items within a folder, it keeps giving me the list of folders in the library, and not the list of files within the specified folder.
I'm using a C# program, using the following method to call the web service GetListItems, and it all seems to work up to a point:
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://schemas.microsoft.com/sharepoint/soap/GetListItems", RequestNamespace="http://schemas.microsoft.com/sharepoint/soap/", ResponseNamespace="http://schemas.microsoft.com/sharepoint/soap/", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
public System.Xml.XmlNode GetListItems(string listName, string viewName, System.Xml.XmlNode query, System.Xml.XmlNode viewFields, string rowLimit, System.Xml.XmlNode queryOptions, string webID) {
object[] results = this.Invoke("GetListItems", new object[] {
listName,
viewName,
query,
viewFields,
rowLimit,
queryOptions,
webID});
return ((System.Xml.XmlNode)(results[0]));
}
The values of the parameters are as follows:
listName = "Letters"
viewName = null
query = null
viewFields.OuterXml = "<ViewFields>
<FieldRef Name=\"ID\" />
<FieldRef Name=\"Title\" />
<FieldRef Name=\"Modified\" />
<FieldRef Name=\"Status\" />
<FieldRef Name=\"_UIVersion\" />
<FieldRef Name=\"_UIVersionString\" />
<FieldRef Name=\"EpisodeId\" />
</ViewFields>"
rowLimit = null
queryOptions.OuterXml = "<QueryOptions>
<IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns>
<Folder>http://D3TVCAPP-APP02.test.local/Letters/ABBOTT, Nash _489611</Folder>
<DateInUtc>TRUE</DateInUtc>
</QueryOptions>"
webID = null
And it actually returns a result, it is hitting the service ok, the result looks like this:
results.OuterXml =
"<listitems xmlns:s=\"uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882\" xmlns:dt=\"uuid:C2F41010-65B3-11d1-A29F-00AA00C14882\" xmlns:rs=\"urn:schemas-microsoft-com:rowset\" xmlns:z=\"#RowsetSchema\" xmlns=\"http://schemas.microsoft.com/sharepoint/soap/\">
<rs:data ItemCount=\"2\">
<z:row
ows_ID=\"84\"
ows_Modified=\"2021-06-09T09:50:11Z\"
ows__UIVersion=\"1\"
ows__UIVersionString=\"0.1\"
ows__ModerationStatus=\"2\"
ows__Level=\"2\"
ows_UniqueId=\"84;#{C8E8EC38-6DD5-4275-873A-B829BA850A54}\"
ows_owshiddenversion=\"1\"
ows_FSObjType=\"84;#1\"
ows_Created_x0020_Date=\"84;#2021-06-09T09:50:11Z\"
ows_ProgId=\"84;#\"
ows_FileLeafRef=\"84;#ABBOTT, Nash _489611\"
ows_PermMask=\"0x7fffffffffffffff\"
ows_FileRef=\"84;#Letters/ABBOTT, Nash _489611\"
ows_Editor=\"1;#John Smith\"
ows_MetaInfo=\"84;#\"
ows_Last_x0020_Modified=\"84;#2021-06-09T09:50:14Z\"
/>
<z:row
ows_ID=\"3\"
ows_Modified=\"2020-11-04T13:02:42Z\"
ows__UIVersion=\"1\"
ows__UIVersionString=\"0.1\"
ows__ModerationStatus=\"2\"
ows__Level=\"2\"
ows_UniqueId=\"3;#{1CA7B690-BF90-41EC-A5EA-E910C8D72376}\"
ows_owshiddenversion=\"1\"
ows_FSObjType=\"3;#1\"
ows_Created_x0020_Date=\"3;#2020-11-04T13:02:42Z\"
ows_ProgId=\"3;#\"
ows_FileLeafRef=\"3;#SMITH, JOHN _483835\"
ows_PermMask=\"0x7fffffffffffffff\"
ows_FileRef=\"3;#Letters/SMITH, JOHN _483835\"
ows_Editor=\"1;#John Smith\"
ows_MetaInfo=\"3;#\"
ows_Last_x0020_Modified=\"3;#2020-11-06T16:59:03Z\"
/>
</rs:data>
</listitems>"
Apologies for the wall of code, I just hope that someone can help me and want to provide as much info as possible. I can't figure this one out, I feel like I'm doing everything right. Anyone got any idea what I 'm doing wrong?
C# Programming give for developers two options to consumes SharePoint data; seeing your code, I think you try to consume SharePoint data using REST API approach. The REST API approach is commonly used to make interfaces with SharePoint and external Systems written by other programming languages (different of C#) and not used a lot by C# developer community in frequency; but, it's need to be clear: it's not a rule and it's not impossible to consumes SharePoint data using REST API from C#, but this is more "difficultly" comparing with CSOM approach.
Now, I will show you an example using CSOM approach to get files from specific folder, and you get your conclusions:
First of all, download and install SharePoint 2013 Client SDK;
After installed SDK, create some standalone app, like a Console App using .NET Framework 4.5 or later;
Add in References (search in Framework) two DLLs: Microsoft.SharePoint.Client.dll and Microsoft.SharePoint.Client.Runtime.dll;
Add this code-file bellow in your Console App Project:
using System;
using System.Net;
using System.Security;
using System.Collections.Generic;
using Microsoft.SharePoint.Client;
namespace SharePoint.Csom.Approach
{
public class SharePointDocumentModel
{
public string FileName { get; set; }
public string Url { get; set; }
public string Version { get; set; }
}
public class SharePointDocuments : IDisposable
{
private bool _disposed = false;
protected readonly ClientContext _clientContext;
public SharePointDocuments(string sharepointSiteUrl, string user, SecureString password)
{
if (string.IsNullOrWhiteSpace(sharepointSiteUrl) || !sharepointSiteUrl.StartsWith("http"))
{
throw new InvalidOperationException("Impossible to connect with invalid URL.");
}
this._clientContext = new ClientContext(sharepointSiteUrl);
this._clientContext.AuthenticationMode = ClientAuthenticationMode.Default;
this._clientContext.Credentials = new NetworkCredential(user, password);
}
public IEnumerable<SharePointDocumentModel> GetFiles(string folderRelativeURL)
{
Web web = this._clientContext.Web;
this._clientContext.Load(web);
this._clientContext.ExecuteQuery();
Folder folder = web.GetFolderByServerRelativeUrl(web.ServerRelativeUrl + folderRelativeURL);
this._clientContext.Load(folder);
this._clientContext.ExecuteQuery();
FileCollection fileCol = folder.Files;
this._clientContext.Load(fileCol);
this._clientContext.ExecuteQuery();
foreach (File file in fileCol)
{
string fileVersion = null;
FileVersionCollection oFileVersionCollection = file.Versions;
this._clientContext.Load(oFileVersionCollection);
this._clientContext.ExecuteQuery();
foreach (FileVersion oFileVersion in oFileVersionCollection)
{
fileVersion = oFileVersion.VersionLabel;
}
yield return new SharePointDocumentModel()
{
FileName = file.Name,
Url = file.ServerRelativeUrl,
Version = fileVersion
};
}
}
public void Dispose()
{
this.Dispose(true);
}
private void Dispose(bool disposing)
{
if (this._disposed)
{
return;
}
if (disposing)
{
if (null != this._clientContext)
{
this._clientContext.Dispose();
}
}
this._disposed = true;
}
~SharePointDocuments()
{
this.Dispose(false);
}
}
}
At Program class and in Main method, add this code:
List<SharePoint.Csom.Approach.SharePointDocumentModel> files = new List<SharePoint.Csom.Approach.SharePointDocumentModel();
using (SharePoint.Csom.Approach.SharePointDocuments sharepoint = new SharePoint.Csom.Approach.SharePointDocuments("http://D3TVCAPP-APP02.test.local", "user", "password"))
{
files = sharepoint.GetFiles("/Letters/ABBOTT, Nash _489611").ToList();
}
Voilá!
After made your tests, give us the feedback.
Thanks.
I have a general need to maintain a reference to my ancestors as I traverse down the sitemap.
Mvc.sitemap
<mvcSiteMap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-4.0"
xsi:schemaLocation="http://mvcsitemap.codeplex.com/schemas/MvcSiteMap-File-4.0 MvcSiteMapSchema.xsd">
<mvcSiteMapNode title="Home" controller="Home" action="Index" >
<mvcSiteMapNode title="Products" url="~/Home/Products" roles="*">
<mvcSiteMapNode title="Harvest MAX" url="~/Home/Products/HarvestMAX" >
<mvcSiteMapNode title="Policies" url="~/Home/Products/HarvestMAX/Policy/List" productType="HarvestMax" type="P" typeFullName="AACOBusinessModel.AACO.HarvestMax.Policy" roles="*">
<mvcSiteMapNode title="Policy" controller="Object" action="Details" typeName="Policy" typeFullName="AACOBusinessModel.AACO.HarvestMax.Policy" preservedRouteParameters="id" roles="*">
<mvcSiteMapNode title="Counties" controller="Object" action="List" collection="Counties" roles="*">
<mvcSiteMapNode title="County" controller="Object" action="Details" typeName="County" typeFullName="*" preservedRouteParameters="id" roles="*">
<mvcSiteMapNode title="Land Units" controller="Object" action="List" collection="LandUnits" roles="*">
<mvcSiteMapNode title="Land Unit" controller="Object" action="Details" typeName="LandUnit" typeFullName="AACOBusinessModel.AACO.LandUnit" preservedRouteParameters="id" roles="*">
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMapNode>
</mvcSiteMap>
Controller
[SiteMapTitle("Label")]
public ActionResult Details(string typeFullName, decimal id)
{
return View(AACOBusinessModel.AACO.VersionedObject.GetObject( typeFullName?.ToType() ?? Startup.CurrentType,
ApplicationSignInManager.UserCredentials.SessionId,
id));
}
There are many reasons I want this, but here are some specific examples.
Example 1: Vanishing ID's
Let's say the url that got me to the Policy node is http://localhost:36695/AACOAgentPortal/details/Policy/814861364767412.
Once I navigate down past that to the County node, my breadcrumbs looks like this:
However if I hover over the Policy breadcrumb, the url given is http://localhost:36695/AACOAgentPortal/Object/Details?typeName=Policy&typeFullName=AACOBusinessModel.AACO.HarvestMax.Policy. As you can see, the id is gone.
Example 2: Vanishing Titles
As you can see in my controller, I'm telling mvc sitemap that I want to use the Label property to display the node title. It does that when it's the leaf node:
But once I go past that, it disappears:
Both of these issues may have a common cause. There are other reasons why I may want a reference to an ancestor along the breadcrumb trail, but these are two concrete ones to exemplify the issue.
I solve this by keeping my objects in a hierarchy in session, and each object has the same key that it's node has so that it can find the node upon processing each request.
MenuItems.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using AtlasKernelBusinessModel;
using MvcSiteMapProvider;
using CommonBusinessModel.Commands;
using CommonBusinessModel.Extensions;
using CommonBusinessModel.Security;
namespace AtlasMvcWebsite.Code
{
[Serializable]
public class MenuItems : Dictionary<string, MenuItem>
{
#region Properties
public IEnumerable<Command> AvailableCommands
{
get
{
return CurrentItem?.Commandable?.AvailableCommands() ?? new List<Command>();
}
}
/// <summary>
/// Each User has his own copy because it has to track his travel through the hierarchy
/// </summary>
public static MenuItems Current
{
get
{
return (MenuItems)(HttpContext.Current.Session["MenuItems"] =
HttpContext.Current.Session["MenuItems"] ??
new MenuItems());
}
}
private MenuItem currentItem;
public MenuItem CurrentItem
{
get
{
return currentItem =
CurrentNode == null ?
null :
this[CurrentNode.Key] =
ContainsKey(CurrentNode.Key) ?
this[CurrentNode.Key] :
new MenuItem(CurrentNode,
CurrentNode.ParentNode != null ? this[CurrentNode.ParentNode.Key] : null);
}
}
public ISiteMapNode CurrentNode { get; private set; }
#endregion
#region Methods
private void Build()
{
Build(SiteMaps.Current.RootNode);
}
private void Build(ISiteMapNode node)
{
foreach (var childNode in node.ChildNodes)
{
foreach (var att in node.Attributes.Where(a => !childNode.Attributes.Any(na => na.Key == a.Key)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value))
{
switch (att.Key)
{
case "productType":
case "typeFullName":
case "typeName":
childNode.Attributes[att.Key] = att.Value;
childNode.RouteValues[att.Key] = att.Value;
break;
}
}
Build(childNode);
}
}
/// <summary>
/// We finally have an object from the details controller and we want to set it to the current menu item
/// </summary>
/// <param name="versionedObject"></param>
/// <returns></returns>
public AtlasKernelBusinessModel.VersionedObject Set(AtlasKernelBusinessModel.VersionedObject versionedObject)
{
((ICommandable)versionedObject).UserAccess = User.Credentials.BusinessObjectAccessFor(versionedObject.ObjectType());
if (CurrentItem != null)
this[CurrentItem.Node.Key].Object = versionedObject;
//else
// for commands
//SiteMapNodeObjects[SiteMapNodeObjects.Last().Key] = versionedObject;
return versionedObject;
}
public void Sync()
{
//Build();
CurrentNode = SiteMaps.Current.CurrentNode;//cache value of current node
Values.ToList().ForEach(m => m.Sync());
}
#endregion
}
}
MenuItem.cs
using AtlasKernelBusinessModel;
using MvcSiteMapProvider;
using System;
using CommonBusinessModel.Commands;
namespace AtlasMvcWebsite.Code
{
[Serializable]
public class MenuItem
{
#region Constructors
public MenuItem(ISiteMapNode node, MenuItem parent)
{
Node = node;
Parent = parent;
}
#endregion
public ISiteMapNode Node;
public readonly MenuItem Parent;
private ICommandable commandable;
public ICommandable Commandable
{
get
{
return commandable;
}
}
private VersionedObject #object;
public VersionedObject Object
{
get
{
return #object;
}
set
{
#object = value;
Type = #object.GetType();
commandable = (ICommandable)#object;
Sync();
}
}
public Type Type;
public void Sync()
{
// sync the node to the object
if (Object == null) return;
Node = SiteMaps.Current.FindSiteMapNodeFromKey(Node.Key);
Node.Title = Type.GetProperty("Label").GetValue(Object).ToString();
Node.RouteValues["id"] = Object.Id;
}
public override string ToString()
{
return $"{Parent?.Node?.Title}.{Node.Title}";
}
}
}
Usage Example
var menuItem = MenuItems.Current.CurrentItem; // ensure current item exists
if (menuItem != null)
{
<!-- CHILD ITEM MENU -->
#Html.MvcSiteMap().Menu(menuItem.Node, true, false, 1)
<!-- COMMAND BUTTONS -->
if (!viewModel.ReadOnly)
{
#Html.DisplayFor(m => menuItem.Commandable.BusinessOperations.Commands)
}
When using preservedRouteParameters, the source of the values it retrieves is from the current request. Therefore, you cannot reuse id for a different purpose if you expect to navigate up the hierarchy. Also, you must ensure that all ancestral preservedRouteParameters are included in the current request or the URLs will not be built correctly.
There is a demo of how to use preservedRouteParameters here: https://github.com/NightOwl888/MvcSiteMapProvider-Remember-User-Position.
I want to add the custom configsection in the app.config file as follows
<Companies>
<Company name="" code=""/>
<Company name="" code=""/>
</Companies>
<Employees>
<Employee name="" Des="" addr="" sal=""/>
<Employee name="" Des="" addr="" sal=""/>
</Employeess>
<Departments>
<Department Id="" Projects=""/>
</Departments>
<Projects>
<Project Path=""/>
</Projects>
In the Department section it is referring to Projects section.
Can anybody tell me way to do it? And how to access it in my code?
#Bhaskar: Please find the code for your comment.
public class RegisterCompaniesConfig : ConfigurationSection
{
public static RegisterCompaniesConfig GetConfig()
{
return (RegisterCompaniesConfig)System.Configuration.ConfigurationManager.GetSection("RegisterCompanies")?? new RegisterCompaniesConfig();
}
[System.Configuration.ConfigurationProperty("Companies")]
public Companies Companies
{
get
{
object o = this["Companies"]; return o as Companies;
}
}
}
public class Companies : ConfigurationElementCollection
{
public Company this[int index]
{ get { return base.BaseGet(index) as Company; }
set
{
if (base.BaseGet(index) != null)
{
base.BaseRemoveAt(index);
}
this.BaseAdd(index, value);
}
}
protected override System.Configuration.ConfigurationElement CreateNewElement()
{ return new Company();
}
protected override object GetElementKey(System.Configuration.ConfigurationElement element)
{ return ((Company)element).Name; }
}
public class Company : ConfigurationElement
{
[ConfigurationProperty("name", IsRequired = true)]
public string Name { get { return this["name"] as string; } }
[ConfigurationProperty("code", IsRequired = true)]
public string Code { get { return this["code"] as string; } }
}
You should check out Jon Rista's three-part series on .NET 2.0 configuration up on CodeProject.
Unraveling the mysteries of .NET 2.0 configuration
Decoding the mysteries of .NET 2.0 configuration
Cracking the mysteries of .NET 2.0 configuration
Highly recommended, well written and extremely helpful! I've learned how to deal with custom config sections from those excellent articles.
I base all my configuration management code on the classes I collected here. This is an example, and here's some documentation. Note that this is code I personally refactored from a blog post that isn't available on-line any more.
This will help you
http://msdn.microsoft.com/en-us/library/2tw134k3(v=vs.80).aspx
In Sharepoint designer's workflow editor I wish to retrieve the username/name of the work flow initiator (i.e. who kicked it off or triggered the workflow) - this is relatively easy to do using 3rd party products such as Nintex Workflow 2007 (where I would use something like {Common:Initiator}) - but I can't seem to find any way out of the box to do this using share point designer and MOSS 2007.
Update
It does not look like this rather obvious feature is supported OOTB, so I ended up writing a custom activity (as suggested by one of the answers). I have listed the activities code here for reference though I suspect there are probably a few instances of this floating around out there on blogs as it's a pretty trivial solution:
public partial class LookupInitiatorInfo : Activity
{
public static DependencyProperty __ActivationPropertiesProperty =
DependencyProperty.Register("__ActivationProperties",
typeof(Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties),
typeof(LookupInitiatorInfo));
public static DependencyProperty __ContextProperty =
DependencyProperty.Register("__Context", typeof (WorkflowContext),
typeof (LookupInitiatorInfo));
public static DependencyProperty PropertyValueVariableProperty =
DependencyProperty.Register("PropertyValueVariable", typeof (string),
typeof(LookupInitiatorInfo));
public static DependencyProperty UserPropertyProperty =
DependencyProperty.Register("UserProperty", typeof (string),
typeof (LookupInitiatorInfo));
public LookupInitiatorInfo()
{
InitializeComponent();
}
[Description("ActivationProperties")]
[ValidationOption(ValidationOption.Required)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties __ActivationProperties
{
get { return ((Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties)(base.GetValue(__ActivationPropertiesProperty))); }
set { base.SetValue(__ActivationPropertiesProperty, value); }
}
[Description("Context")]
[ValidationOption(ValidationOption.Required)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public WorkflowContext __Context
{
get { return ((WorkflowContext)(base.GetValue(__ContextProperty))); }
set { base.SetValue(__ContextProperty, value); }
}
[Description("UserProperty")]
[ValidationOption(ValidationOption.Required)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string UserProperty
{
get { return ((string) (base.GetValue(UserPropertyProperty))); }
set { base.SetValue(UserPropertyProperty, value); }
}
[Description("PropertyValueVariable")]
[ValidationOption(ValidationOption.Required)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public string PropertyValueVariable
{
get { return ((string) (base.GetValue(PropertyValueVariableProperty))); }
set { base.SetValue(PropertyValueVariableProperty, value); }
}
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
// value values for the UserProperty (in most cases you
// would use LoginName or Name)
//Sid
//ID
//LoginName
//Name
//IsDomainGroup
//Email
//RawSid
//Notes
try
{
string err = string.Empty;
if (__ActivationProperties == null)
{
err = "__ActivationProperties was null";
}
else
{
SPUser user = __ActivationProperties.OriginatorUser;
if (user != null && UserProperty != null)
{
PropertyInfo property = typeof (SPUser).GetProperty(UserProperty);
if (property != null)
{
object value = property.GetValue(user, null);
PropertyValueVariable = (value != null) ? value.ToString() : "";
}
else
{
err = string.Format("no property found with the name \"{0}\"", UserProperty);
}
}
else
{
err = "__ActivationProperties.OriginatorUser was null";
}
}
if (!string.IsNullOrEmpty(err))
Common.LogExceptionToWorkflowHistory(new ArgumentOutOfRangeException(err), executionContext,
WorkflowInstanceId);
}
catch (Exception e)
{
Common.LogExceptionToWorkflowHistory(e, executionContext, WorkflowInstanceId);
}
return ActivityExecutionStatus.Closed;
}
}
And then wire it up with the following .action xml file:
<?xml version="1.0" encoding="utf-8"?>
<WorkflowInfo Language="en-us">
<Actions>
<Action Name="Lookup initiator user property"
ClassName="XXX.ActivityLibrary.LookupInitiatorInfo"
Assembly="XXX.ActivityLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XXX"
AppliesTo="all"
Category="WormaldWorkflow Custom Actions">
<RuleDesigner Sentence="Lookup initating users property named %1 and store in %2">
<FieldBind Field="UserProperty" DesignerType="TextArea" Id="1" Text="LoginName" />
<FieldBind Field="PropertyValueVariable" DesignerType="ParameterNames" Text="variable" Id="2"/>
</RuleDesigner>
<Parameters>
<Parameter Name="__Context" Type="Microsoft.Sharepoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions" Direction="In"/>
<Parameter Name="__ActivationProperties" Type="Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties, Microsoft.SharePoint" Direction="In"/>
<Parameter Name="UserProperty" Type="System.String, mscorlib" Direction="In" />
<Parameter Name="PropertyValueVariable" Type="System.String, mscorlib" Direction="Out" />
</Parameters>
</Action>
</Actions>
</WorkflowInfo>
For those that google into this article and are now using SharePoint 2010, the workflow initiator variable is now supported OOTB in SharePoint Designer.
The datasource would be "Workflow Context" and the field is, of course, "Initiator" and you can choose to return it as the "Display Name", "Email", "Login Name" or the "User ID Number"
I don't think this is possible to do in SharePoint Designer out of the box. You could probably write a custom action to get the originator, but I don't believe it is exposed through the SPD workflow interface at all.
The best you could probably do is get the user who created or modified the item in the list, but this wouldn't handle cases where the workflow was manually run.
I can think about a simple but not very sophisticated solution for this one by using just SPD. Just in workflow steps create a test item in a secondary list (probably a task list which stores the workflowId and itemId properties for refrence back) and then do a lookup in your workflow on that list to see who is the creator of that item, that value would be the current workflow initiator.
The custom activity solution only work if you are working with moss, if you only have wss 3.0 you can put one step more in your workflow and set a custom comment field with any information, this make the last modified person to change and become the same as the workflow initiator, then you can use the ModifiedBy field to make any decision that you need.