Create record in custom module and will automatically create new event record that will display in calendar vtiger crm - vtiger

Did anyone try this?
I have a custom module I created a field next visit, I want to make an event after saving my record in my custom module based on next visit field date. here's the following code tried.
public function process(Vtiger_Request $request) {
try {
$CustomSaveEvents= $this->saveAppointmentRecord();
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
public function saveAppointmentRecord() {
try {
$linkModule ="Events";
$recordModel1 = Vtiger_Record_Model::getCleanInstance($linkModule);
$recordModel1->set('subject', "Sample");
$recordModel1->set('mentorid',"6411");
$recordModel1->set('apprenticeid',"10849");
$recordModel1->set('due_date', '2022-10-24');
$recordModel1->set('time_end','2022-10-24');
$recordModel1->set('mode', 'create');
$recordModel1->save();
$this->savedRecordId = $recordModel1->getId();
return $recordModel1;
}catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
But it didn't work. any can help me?
Thank you in advance!
calendar will show new record

I have recently done something similar, but with the "Products" and "Documents" modules.
The crm was hanging when executing save(), until I realized that it was caused by some aftersave events, the ones related to VTEntitydelta.
My solution was to disable these events before save():
$desactivaHandlers = "UPDATE vtiger_eventhandlers SET is_active = 0 WHERE dependent_on LIKE '[\"VTEntityDelta\"]'";
$result = $adb->pquery($desactivaHandlers);
And reactivate it again after the save():
$reactivaHandlers = "UPDATE vtiger_eventhandlers SET is_active = 1 WHERE dependent_on LIKE '[\"VTEntityDelta\"]'";
$result = $adb->pquery($reactivaHandlers);
Hope it helps!

Why you are coding when it's possible to do it without coding? You can use Workflows to create an Event on save the event. Here is the link to which you can refer to configure the workflow in CRM. This Video will not have full-fill your exact requirements, but you can refer to understand how Workflow configuration works.
https://www.youtube.com/watch?v=x2j-ZQTzXps
https://www.youtube.com/watch?v=jwipywUzlzo

Related

Rally Web Services API: How do I get the URL link of the user story? (getDetailUrl() method)

Please be patient and Do Not flag this as duplicate: Using the Rally REST API, how can I get the non-API (website) URL for a user story?
I want to be able to generate a link for the user story.
Something like this: https://rally1.rallydev.com/#/-/detail/userstory/*********
As opposed to this: https://rally1.rallydev.com/slm/webservice/v2.0/hierarchicalrequirement/88502329352
The link will be integrated into another application for the managers to see the user story.
I did read about the getDetailUrl() method, but in my case I am creating the user stories by parsing email and linking that to a notification service in Slack.
I am aware of the formattedID and (_ref), but I would have to query for it again, and I am creating batches of userstories through a loop. I need the actual web site link to the user story.
Here is my sample code:
public void CreateUserStory(string workspace, string project, string userstoryName){
//authenticate with Rally
this.EnsureRallyIsAuthenticated();
//DynamicJsonObject for HierarchicalRequirement
DynamicJsonObject toCreate = new DynamicJsonObject();
toCreate[RallyConstant.WorkSpace] = workspace;
toCreate[RallyConstant.Project] = project;
toCreate[RallyConstant.Name] = userstoryName;
try
{
//Create the User Story Here
CreateResult createUserStory = _api.Create(RallyConstant.HierarchicalRequirement, toCreate);
Console.WriteLine("Created Userstory: " + "URL LINK GOES HERE");
}
catch (WebException e)
{
Console.WriteLine(e.Message);
}
}
We don't have a method in the .NET toolkit for doing this, but it's easy to create.
The format is this:
https://rally1.rallydev.com/#/detail/<type>/<objectid>
Just fill in the type (hierarchicalrequirement turns into userstory, but all the others are the same as the wsapi type) and the objectid from the object you just created.
var parameters = new NameValueCollection();
parameters["fetch"] = "FormattedID";
var toCreate = new DynamicJsonObject();
var createResult = restApi.create("hierarchicalrequirement", toCreate, parameters);
var type = Ref.getTypeFromRef(createResult.Reference);
var objectID = Ref.getOidFromRef(createResult.Reference);
var formattedID = createResult.Object["FormattedID"];
And you can specify fetch fields to be returned on the created object so you don't have to re-query for it.

How to add a combo drop-down user list in vtigercrm?

I'm using existing Task module under the Project module. I would like to
assign particular task to multiple workers. Meaning that group of people
will together complete the task.
I already have workers as users in my vtigercrm. So if i make a user selection
multiple for assigning single task it could be easier to handle this
use-case.
This use-case already have in Calendar module but i'm unable to understand how they implement.
I have a thorough knowledge on how to create custom user list drop-down in any module and i created lot more for my custom modules. Now stuck with the same scenario having multiple at a time.
I tried like this in module.php, but it is not working.
$users = Vtiger_Module::getInstance('Users');
$module = Vtiger_Module::getInstance('MyModule');
$module->unsetRelatedList($users, 'Users', 'get_related_list');
$module->setRelatedList($users, 'Users', array('SELECT'), 'get_related_list');
Can anyone help me to get it done?
It is possible when you understood the Invite users field in Calender envent creation. Here the field must be multi-selection.
You need to deal with vtiger_salesmanactivityrel table for inserting the multiple users and for showing in detail view.
Here is the code to handle inviteusers.
//Handling for invitees
$selected_users_string = $_REQUEST['inviteesid'];
$invitees_array = explode(';',$selected_users_string);
$this->insertIntoInviteeTable($module,$invitees_array);
function insertIntoInviteeTable($module,$invitees_array)
{
global $log,$adb;
$log->debug("Entering insertIntoInviteeTable(".$module.",".$invitees_array.") method ...");
if($this->mode == 'edit'){
$sql = "delete from vtiger_invitees where activityid=?";
$adb->pquery($sql, array($this->id));
}
foreach($invitees_array as $inviteeid)
{
if($inviteeid != '')
{
$query="insert into vtiger_invitees values(?,?)";
$adb->pquery($query, array($this->id, $inviteeid));
}
}
$log->debug("Exiting insertIntoInviteeTable method ...");
}
Hope it would give you a better idea.

Tracking a Page Event from ASHX Handler

Im currently trying to track a PageEvent within a ASHX Handler. My code basically looks like this:
public class GetProductPdf : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
if (!Tracker.IsActive)
{
Tracker.Initialize();
Tracker.StartTracking();
}
//Track PageEvent here...
}
public bool IsReusable
{
get
{
return false;
}
}
}
The Tracker is always inactive and Tracker.Current == null. On method call "Tracker.StartTracking();" the following Exception is thrown:
[InvalidOperationException: Tracker.Current is not initialized]
Sitecore.Analytics.Pipelines.StartAnalytics.StartTracking.Process(PipelineArgs args) +317
(Object , Object[] ) +83
Sitecore.Pipelines.CorePipeline.Run(PipelineArgs args) +445
Project.Web.Handler.PdfCreation.GetProductPdf.ProcessRequest(HttpContext context) in d:\Project\Website\Handler\PdfCreation\GetProductPdf.ashx.cs:69
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +913
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +165
I tried all possible solutions suggested here.
When doing the same in a mvc controller the Tracker is active and Tracker.Current != null.
Does anyone has an idea, what could cause this or are there any other suggestions for a solution?
Thanks in advance.
I am not certain that your Ashx Handler can be executed within the necessary Sitecore Context so that Tacker.Current will not be valid nor can be started via Tracker.StartTracking(). Someone might be able to confirm but I have another solution you can try which works for me.
As nice as it would be for the Ashx Handler to register the Event for you, instead you can fire a JavaScript function on the link to the file. So that when the link is clicked the JavaScript makes a web request to a MVC Controller and the controller registers the event for you.
I have implemented this myself using WebApi Controllers. Data Attributes were on the a tag, JavaScript posted those attributes to the controller, the controller used those attributes to determine which Event to register and the description to use on the Event.
<asp:HyperLink runat="server" data-goalid="{08030449-A811-428B-95F0-59FCD42B8DEB}" data-goaldescription="Product 0112 brochure">
[System.Web.Mvc.HttpPost]
public JsonResult RegisterGoal(string goalId, string goalDescription)
{
Item eventItem = Sitecore.Context.Database.GetItem(goalId);
var goal = new PageEventItem(eventItem);
var eventData = Tracker.Current.PreviousPage.Register(goal);
eventData.Data = goal["Description"] + " " + goalDescription;
Tracker.Current.Interaction.AcceptModifications();
return Json(new PageEventRequestResult()
{
Success = true,
Message = "Successfully registered goal",
});
}
It works really well. The only downside is having to add it to the various links that lead to the files you want to track.
I wrote a blog about tracking various interactions on a site and registering Sitecore Events / Goals you might want to look at, scroll down to the 'Storing custom data in xDB' section.

Sitecore|WFFM| Custom Error Message with details| on Same Page with Form

I have a Web Form for Marketer set up done for one of my Pages.
I have Custom Submit Action written for it as shown below in the code snippet -
public class **CustomFormSubmit : ISaveAction**
{
public void Execute(ID formid, AdaptedResultList fields, params object[] data)
{
try
{
**//var returnValue= Custom Logic to save form data // returns true or false**
}
catch (Exception ex)
{
Logger.Log(ex.Message + ":" + builder, ExceptionCategory.Error);
throw;
}
}
In my above Web form - Success Mode is - SuccessMode/Redirect and I have a success Page configured for it.
My requirement in above scenario is to keep user on the same Page(with Form) if returnValue is false . (as shown in above code snippet)
Can anyone Please guide me in the above scenario as - how to keep user on the same Page with values filled in the form so that user can submit it again.
Product Details - 7.2 rev. 141226 , Web Forms for Marketers 2.4 rev.140117
To add further details -
I am not sure how can I go back to my page instead of the redirection in case if return is false in the above code snippet.
As soon the submit button is clicked the above function- Execute- gets called.
How do I go back to the Page - Do I need to override any function or customize something.
If any exception comes while saving data- then the control goes back to the same Page with all values filled by user retained -with the Save Action Failed Message which is configured in Sitecore .
So my requirement will be to go to to the form as happening in case of Exception when false comes as return value while saving data and to put customised Error Messages which might change each time, so not statically configured ,rather a dynamic one.
Thanks!
Saurabh
One option will be to redirect to the original page with the Form on.
Enable your form to populate the fields via Query String using the ReadQueryString property, via Presentation Details of the Form Renderer:
So on false of your Save Action you create a collection of query strings with the name of each Field, as it appears in the Form, followed by the User's value.
The code below will loop through all your fields and arrange them into a QueryString with its FieldName and Value;
string urlOfForm = HttpContext.Current.Request.Url.AbsolutePath;
var queryString = new StringBuilder("?");
foreach (AdaptedControlResult field in fields)
{
queryString.Append(string.Format("{0}={1}&", field.FieldName, field.Value));
}
urlOfForm = urlOfForm + queryString;
HttpContext.Current.Response.Redirect(urlOfForm);
Sitecore will then automatically populate the appropriate fields with the values, achieving your requirement.
EDIT
I have found that most Exceptions thrown will take the user back to the Form with their values populated. You can then pass in the cause of the failure to write to your CRM. See below for Example
if (submitFailed)
{
throw new Exception("The email address entered already exists in our System");
}
The complexity then comes in dynamically swapping out the Save Action Failed Message to show this Exception Message. All posts I find about custom Save Action Message state the only real approach is to redirect via your Custom Save Action to a different page showing a different message. Which is not suitable to your requirements.
I have found the pipeline Args you are going to need to patch FormSubmitFailedArgs and SubmitFailedArgs. The Former will need the following change
public FormSubmitFailedArgs(ID formID, AdaptedResultList fields, ID actionFailed, Exception ex)
: base(formID, actionFailed, ex)
{
this.Fields = fields;
this.ErrorMessage = ex.Message;
}
and the Latter will need
public SubmitFailedArgs(ID formID, ID actionFailed, string errorMessage, Exception innerException)
{
this.FormID = formID;
this.ActionFailed = actionFailed;
this.ErrorMessage = innerException.Message;
this.InnerException = innerException;
}
Location and Styling of Submit Message:
You need to find the FormRender sublayout file, this is defaulted to website\sitecore modules\Web\Web Forms for Marketers\Control\SitecoreSimpleFormAscx.ascx inside there you will find a compont called SubmitSummary this renders out the submit message so move it to where you require.
Also note it references the CssClass scfSubmitSummary this is what you will need to target to change the styling of the Message. This Answer is already REALLY long so I won't give a blow by blow how to change the styling of that class, see here for example - http://www.awareweb.com/awareblog/10-1-13-wffmguide
Pipeline Patching
I've dug in deeper, in order to use the custom Args we created for using the exception error message you will need to control the Pipeline which ultimately uses those Args, this is the processor Sitecore.Form.Core.Pipelines.FormSubmit.FormatMessage, Sitecore.Forms.Core in the <errorSubmit> Pipeline.
From my investigation it shouldn't take much effort then its a matter of patching it, you can modify if the Sitecore.Forms.config directly or use patch:instead from a config file within the App_Config/Includes folder - see here for more info.
One option would be to create a Custom Form Verification Action. You could save the data here, although it would be better to verify the data against your API here and then save the data in custom save action, simply since this seems more logical as to how WFFM was meant to function.
using Sitecore.Data;
using Sitecore.Form.Core.Controls.Data;
using Sitecore.Form.Core.Submit;
using System;
using System.Collections.Generic;
namespace Custom.WFFM
{
public class CustomVerificationStep : BaseCheckAction
{
public string FailedMessage { get; set; }
public override void Execute(ID formid, IEnumerable<ControlResult> fields)
{
// Call your API
// You have access to the fields, so you can pass them through as parameters to your if needed
bool flag = ServiceAPI.ValidateUserData(param1, param2, etc);
if (!flag)
{
throw new Exception(string.Format(this.FailedMessage ?? "There was an error while verifying the data against the service call"));
}
}
public override ActionState QueryState(ActionContext context)
{
return ActionState.DisabledSingleCall;
}
}
}
Create the corresponding Verification Action under /sitecore/system/Modules/Web Forms for Marketers/Settings/Actions/Form Verification:
You can change the error message by setting it in the Parameters field as <FailedMessage>Custom Failed Error Message</FailedMessage>.
And then add your verification step to your form:
If you need a different error message per form then you can set the error message to display from the Error Messages tab.
The user will then be returned to the same without any of the save actions being called and the form fields still filled in.

Can't access a new field programmatically on a template in Sitecore

my question is basically the same as #Bob Black's Cannot access sitecore item field via API but I agree with #techphoria414 that the accepted solution is not necessary and in my case does not work.
In my own words, I have a template Departure that I have been using for about a year now creating and updating items programmatically. I have added a new field Ship to the template. When I create a new item the field comes up as null when I try to access it using departure.Fields["Ship"]. If I step over the line causing the exception then after calling departure.Editing.EndEdit() I can then see the Ship field if I call departure.Fields.ToList(). If I add the template to a content item via the Sitecore GUI I can see the field and use it, and if I look at a content item which is based on the template I can see the new field too. So it is only when I access the template/item programmatically that it is null.
I have sitecore running on my local machine with a local sqlserver, and publish to my local machine.
Here is my code
String ship = "MSDisaster";
foreach (Language language in SiteLanguages)
{
departure = departure.Database.GetItem(departure.ID, language);
departure.Editing.BeginEdit();
try
{
departure.Fields["StartDate"].Value = GetSitecoreDateString(xDep, "StartDate");
departure.Fields["EndDate"].Value = GetSitecoreDateString(xDep, "EndDate");
departure.Fields["Guaranteed"].Value = xDep.SelectSingleNode("./Guaranteed").InnerText;
departure.Fields["Status"].Value = xDep.SelectSingleNode("./Status").InnerText;
departure.Fields["Currency"].Value = ConvertLanguageToCurrency(language);
departure.Fields["Market"].Value = ConvertLanguageToMarket(language);
departure.Fields["TwinSharePrice"].Value = GetPrice(xDep, "twn", language);
departure.Fields["SinglePrice"].Value = GetPrice(xDep, "sgl", language);
if (!String.IsNullOrEmpty(ship))
departures.Fields["Ship"].Value = ship;
}
catch (Exception ex)
{
departure.Editing.CancelEdit();
log.Error(ex);
throw ex;
}
departure.Editing.EndEdit();
}
So, how do I get the field be picked up?
Thanks,
James.
Firstly do you see the field in the web database in the sitecore administration.
If you do the item has the fields, you then should check the template assigned on the item and double check that the field is actually called "ship" and check the case as ive seen this as an issue before.
Also check the security on the item and field just in case anyone changed anything.
Next try and get the data from the item but instead of using the field name, use the field ID.
Let me know how you go?
Chris
Sorry Chris, StackOverflow, and the others who looked at my questions. It was a stupid typo. It's even there in my question
departure.Fields["SinglePrice"].Value = GetPrice(xDep, "sgl", language);
if (!String.IsNullOrEmpty(ship))
departures.Fields["Ship"].Value = ship;
}
departure is the item I am working on, departures is the collection it belongs to... doh.
So what is the protocol here? Do I delete my question now because it isn't really going to help anyone code better?
James.