I need to use fetch xml in a CRM plugin, and I found here an example on how to do that:
string groupby1 = #"
<fetch distinct='false' mapping='logical' aggregate='true'>
<entity name='opportunity'>
<attribute name='name' alias='opportunity_count' aggregate='countcolumn' />
<attribute name='ownerid' alias='ownerid' groupby='true' />
<attribute name='createdon' alias='createdon' />
<attribute name='customerid' alias='customerid' />
</entity>
</fetch>";
EntityCollection groupby1_result = orgProxy.RetrieveMultiple(new FetchExpression(groupby1));
but there's something else I don't know how to use, or where is it to use.. it's the part which says:
orgProxy.RetrieveMultiple(new FetchExpression(groupby1));
I Know it's an object of the OrganizationServiceProxy, but where is it in the plugin class? I couldn't find out.
In the politest way possible, you probably need to take a few steps backwards to go forwards.
So to write a plugin, you need to implement IPlugin, which has just the one method
public void Execute(IServiceProvider serviceProvider)
The IServiceProvider is your window into CRM and the context of the event that you are hooking into.
Typically, you would do something like:
var context = (IPluginExecutionContext) serviceProvider.GetService(typeof (IPluginExecutionContext));
var factory = (IOrganizationServiceFactory) serviceProvider.GetService(typeof (IOrganizationServiceFactory));
var service = factory.CreateOrganizationService(context.UserId);
In the example above, service is of type IOrganizationService. This gives you all the methods you would expect
service.Execute(foo);
service.RetrieveMultiple(bar);
service.Update(... /* etc
Might be worth reviewing some of the guides around this - as I've given in a previous answer here
Related
I'm desperately trying to adhere to the MVVM design pattern in my App. So I'm trying to use the EventToCommandBehavior behavior from the MCT. (I'm also using the CommunityToolkit.Mvvm for [RelayCommand]) I've attached it to an Entry and am trying to forward the TextChanged event to my command. However, my command doesn't execute.
XAML:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:Class="MyApp.View.Accounts.AddAccountPage"
xmlns:viewmodel="clr-namespace:MyApp.ViewModel.AccountsViewModel"
Title="Add Account">
<VerticalStackLayout>
<Entry x:Name="entryAccountName"
Placeholder="Account Name"
PlaceholderColor="Black"
TextColor="{StaticResource Tertiary}"
BackgroundColor="{StaticResource Primary}"
WidthRequest="125"
HorizontalOptions="Center"
Keyboard="Text"
ClearButtonVisibility="WhileEditing"
ReturnType="Next">
<Entry.Behaviors>
<toolkit:EventToCommandBehavior
EventName="TextChanged"
Command="{Binding AccountTextChangedCommand}" />
</Entry.Behaviors>
</Entry>
....More XAML....
AccountsViewModel code:
[RelayCommand]
public void AccountTextChanged()
{
Application.Current.MainPage.DisplayAlert("Text changed", "Account Text Changed", "OK");
}
I've got a breakpoint set to the Method and it just never gets called. Any ideas as to what am I doing wrong?
If by "reference in the header", you mean the line xmlns:viewmodel=...,
all that can do is declare a namespace that can be used in the following xaml. It can't refer to a class in that namespace - the line you show is ignored because it is not a valid namespace - you need:
<ContentPage
xmlns:viewmodel="clr-namespace:MyApp.ViewModel" <--- NAMESPACE, NOT CLASS!
/>
<ContentPage.BindingContext>
<viewmodel:AccountsViewModel/>
</ContentPage.BindingContext>
lines in your xaml, to say which viewmodel is the binding context. This is equivalent to the c# code you put in constructor.
OR There is an alternative technique, using Dependency Injection.
In MauiProgram.CreateMauiApp(), add lines similar to:
mauiAppBuilder.Services.AddTransient<MyApp.ViewModel.AccountsViewModel>();
mauiAppBuilder.Services.AddTransient<MyApp.View.Accounts.AddAccountPage>();
Exact details of those lines depend on your existing CreateMauiApp code, and your namespaces.
I finally figured it out. I had forgotten to set the BindingContext to the AccountsViewModel in the constructor of the page:
using MyApp.Services;
using MyApp.ViewModel.AccountsViewModel;
namespace MyApp.View.Accounts;
public partial class AddAccountPage : ContentPage
{
public AddAccountPage()
{
InitializeComponent();
DataService dataService = new();
AccountsViewModel accountsViewModel = new(dataService);
BindingContext = accountsViewModel;
}
}
I've been having to set the BindingContext via constructors on various pages to get the data bindings to work, even though I reference the ViewModels in the XAML header. Is this intended or am I going about it wrong?
Currently using:
base install of sylius-standard, for an ecommerce website.
Here is what I would like to accomplish :
Basically we know there is an User Entity which resides in
vendor/sylius/sylius/src/Sylius/Component/Core/Model/User.php
This Entity is actually extending the FOS\UserBundle\Model\User and it also has a linked UserInterface which is implementing FOS\UserBundle\Model\UserInterface.
My goal is to create another entity UserData which can be linked via the Doctrine OneToMany field relation to the above sylius User Entity which is basically the table sylius_user.
So effectively User Entity linked to UserData Entity via oneToMany ( For each user there can be multiple UserData entity instances.)
Sylius documentation (http://sylius.readthedocs.org) does not have anything related to adding an entity - If I am mistaken, please guide me to the correct links.
I have read overriding models at - > sylius models override also have gone through another sort of related question on stackoverflow How to create a new model with Sylius via SyliusResourceBundle but it is not clear as he is using the SyliusResourceBundle while I want to extend the SyliusCoreBundle (not sure if we can extend it in the first place or not)
How do I achieve this in the sylius framework ?
I solved the problems by reading segregated posts, so putting in here the solution for the community (please let me know if this is not the recommended way and the correct way if not):
My goal was to create a oneToMany doctrine relationship between
Sylius\Component\Core\Model\User
And
Acme\Bundle\Entity\UserData
which I had created. Basically for every user there could be multiple userdata instances pointing back to that user.
To do this, at first I followed the docs at sylius documentation
to override the sylius user class which is infact an extended class of fosuserbundle.
At some stage I kept getting the error [Doctrine\DBAL\Schema\SchemaException]
The table with name 'astrohealing_dev.sylius_user' already exists.
Based on Winzou's suggestion : To fix this error I changed the user class locations in sylius.yml (Sylius/Bundle/CoreBundle/Resources/config/app/sylius.yml) which is part of the core bundle and declared my own class as the user model like this :
This resolved the sylius_user table exists error
But then I got another error :
[Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException]
Configuration path "sylius_core.driver" cannot be overwritten. You have to define all options for this path, and any of its sub
-paths in one configuration section.
Next I commented out drive : doctrine/orm line for the relevant entry in
app/config/config.yml
sylius_core:
# driver: doctrine/orm
classes:
user:
model: Acme\Bundle\Entity\User
sylius_resource:
resources:
sylius.userdata:
driver: doctrine/orm
classes:
model: Acme\Bundle\Entity\UserData
The above few lines also show :
The override for sylius_core with my new User class
The new resource - which is bascially just the userdata class
Now for the Doctrine Mapping (User.orm.xml):
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Acme\Bundle\Entity\User" table="sylius_user">
<one-to-many
field="userdata"
target-entity="Acme\Bundle\Entity\UserData"
mapped-by="userid" />
</entity>
</doctrine-mapping>
And the mapping for UserData (UserData.orm.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Acme\Bundle\Entity\UserData" table="sylius_userdata">
<id name="id" type="integer">
<generator strategy="AUTO" />
</id>
<many-to-one
field="userid"
target-entity="Acme\Bundle\Entity\User"
inversed-by="userdata"
join-column="userid">
<join-column name="user_id" referenced-column-name="id" nullable="false" />
</many-to-one>
<field name="name" type="string" length="150" />
<field name="datetime" type="datetime" />
</entity>
</doctrine-mapping>
I am trying to create a new 'Expression' in VersionOne - effectively adding a new 'comment' to a conversation.
In theory, the rest-1.v1/Data API should allow this, but I can't figure out how to do it - there is precious little documentation about using the API (using POST) to create objects.
FWIW, here's what I'm doing (after successfully accessing the server with valid credentials):
URL:
/rest-1.v1/Data/Expression
XML:
<Asset href="<Server Base URI>/rest-1.v1/Data/Expression">
<Attribute name="AssetType">Expression</Attribute>
<Relation name="InReplyTo" />
<Attribute name="AuthoredAt">2014-05-28T21:48:37.940</Attribute>
<Attribute name="Content">A new comment</Attribute>
<Attribute name="AssetState">64</Attribute>
<Relation name="Author">
<Asset href="<Server Base URI>/rest-1.v1/Data/Member/2015" idref="Member:2015" />
</Relation>
<Relation name="BelongsTo">
<Asset href="<Server Base URI>/rest-1.v1/Data/Conversation/2018" idref="Conversation:2018" />
</Relation>
<Attribute name="Author.Name">user#example.com</Attribute>
<Attribute name="Author.Nickname">User Name</Attribute>
<Relation name="Mentions">
<Asset href="<Server Base URI>/rest-1.v1/Data/Story/2017" idref="Story:2017" />
</Relation>
</Asset>
I keep getting a 400 Bad Request the following error:
<Error href="<Server Base URI>/rest-1.v1/Data/Expression">
<Message>Violation'Required'AttributeDefinition'Content'Expression</Message>
<Exception class="VersionOne.DataException">
<Message>Violation'Required'AttributeDefinition'Content'Expression</Message>
</Exception>
</Error>
I assume I'm missing something obvious - does anyone know what it is?
IF you examine the metadata for a VersionOne Expression, you will see 3 required fields (Author,AuthoredAt,Content). Logically this makes sense to be able to just create a single, zombie expression but I witnessed otherwise. This might be a mistake in the stylesheet or just my browser because it seems POSTing with only those three will return a 400 error. To get a guaranteed working payload, include the relation "inReplyTo" and that is all that you will need to create an expression within the context of a particular Conversation.
Given that you are responding to an existing expression (comment) This should work fine.
POST to rest-1.v1/Data/Expression
<Asset>
<Relation name="Author" act="set">
<Asset idref="Member:2015" />
</Relation>
<Attribute name="AuthoredAt">2014-05-02T21:48:37.940</Attribute>
<Attribute name="Content" act="set">A new comment</Attribute>
<Relation name="InReplyTo" act="set">
<Asset idref="Expression:xxxxx" />
</Relation>
</Asset>
You don't need Asset state or mentions or belongs to. AssetState is readonly, and BelongsTo is filled in automatically after your Expression is created. It inherits a reference to the containing Conversation from the Expression object entered in the InReplyTo field and the Mentions relation is optional.
FYI,
I believe that you didn't see the Legend on the right hand side of a the meta query output as seen in a browser. Real quick here, when you do a meta query, the items with * are required to Post, Bold items are Read/Write optional, the italicized items are readonly, and the bold items towards the bottom that are appended with ": operation" is the operation that you are allow to do against that particular asset.
I am writing a simple PlugIn for Dynamics CRM 2011 which should retrieve a custom attribute which I customized in the pricelist entity.
When I execute the following code, all standard attributes are returned, but not the one custom attribute which I customized (and published of course). I don't get an error, but simply the attribute 'axi_submarketid' is not returned. If I read data from a completely custom entity, I have no problem.
string fetch = string.Format(#"<fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
<entity name='pricelevel'>
<attribute name='name' />
<attribute name='transactioncurrencyid' />
<attribute name='enddate' />
<attribute name='begindate' />
<attribute name='statecode' />
<attribute name='pricelevelid' />
<attribute name='axi_submarketid' />
<order attribute='name' descending='false' />
<filter type='and'>
<condition attribute='pricelevelid' operator='eq' uiname='10 Stromlieferung' uitype='pricelevel' value='{0}' />
</filter>
</entity>
</fetch>", pricelevelid);
EntityCollection pricelevel = _service.RetrieveMultiple(new FetchExpression(fetch));
What am I doing wrong?
Thanks for help,
Peter
I solved my issue myself and it is a bit embarassing :-)
The expected attribute was not deliverd, because it had no content. Meaning, not only in the direct context of a PlugIn, but also when requesting data in general: if in an attribute there is no data, the attribute is not sent back, for example if the attribute is NULL or has no value in it.
Thanks anyway, hope this question and answer helps someone else.
Regards,
Peter
Using Struts 2, I have this interceptor:
public class AccessInterceptor extends AbstractInterceptor {
#Override
public String intercept(ActionInvocation ai) throws Exception {
ActionContext actionContext = ai.getInvocationContext();
Map<String, Object> session = actionContext.getSession();
Boolean logged = (Boolean) session.get("logged");
if(logged == null) {
session.put("logged", false);
logged = false;
}
if (!logged) {
// If exception is here, it IS thrown, so I am sure "login" is returned on /homepage
return "login";
}
return ai.invoke();
}
}
And this struts.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<package name="loginInterceptor" namespace="/" extends="struts-default">
<interceptors>
<interceptor name="access"
class="logininterceptor.interceptors.AccessInterceptor"/>
<interceptor-stack name="appDefault">
<interceptor-ref name="access" />
<interceptor-ref name="defaultStack" />
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="appDefault" />
<global-results>
<result name="login">/WEB-INF/content/login.jsp</result>
</global-results>
</package>
</struts>
I have also /WEB-INF/content/homepage.jsp JSP and /WEB-INF/content/login.jsp and mypackage.LoginAction.java class. There is not an acton class for homepage
The problem is, that when I go to the /homepage page, it is NOT redirected to login.jsp and I see the homepage.jsp content. What is wrong?
PS: I use struts2-convention-plugin
This is happening because the conventions plugin uses the "convention-default" package which does not know about your interceptor or interceptor stack.
If you could create a package which extends conventions-default doing just what your interceptor stack does (defines the interceptor, interceptor stack and defines the default result type) and then make the conventions plugin apply that stack as the default for all actions wouldn't that be great? You can.
After the <struts> tag define a constant like so:
<constant name="struts.convention.default.parent.package" value="loginInterceptor"/>
The other suggestion, was to use struts.xml to override the conventions, which is fine but a little verbose.
Another option is to use the parent package annotation, which is good when you need to override the default behavior but as you pointed out you want this to be the default behavior so you should reserve annotations for overriding this, not to implement it.
It may be worth browsing the following list of struts2 conventions plugin constants for the future: http://struts.apache.org/2.1.8/docs/convention-plugin.html#ConventionPlugin-Overwritingpluginclasses
PS: Just naming preference but I would call your package login-package rather than loginInterceptor... not a big deal.
If you want to force to execute the interceptor every time anyone is accessing to homepage.jsp, the correct way of doing this is to put an action that returns the jsp. For example:
<struts>
...
<package name="loginInterceptor" namespace="/" extends="struts-default">
<interceptors>
...
</interceptors>
<default-interceptor-ref name="appDefault" />
<global-results>
...
</global-results>
<action name="homepage">
<result>/WEB-INF/content/homepage.jsp</result>
</action>
</package>
</struts>
After that, when a user tries to access to http://.../homepage.action, the interceptor will be executed and will be redirected to the login page if the user is not logged in.