how do I track a pdf download with sitecore page events?
I have code which tracks the event from back end but how do you determine whether the link is external, internal or a media link?
And how can you determine if media link is pdf?
public void RegisterDownload(string downloadedResourceText, ID itemId)
{
if (downloadedResourceText != null)
{
if (TrackerEnabled())
{
var page = CurrentPage();
page.Register(new PageEventData("Download", _downloadPageEventGuid) { ItemId = itemId.ToGuid(), Data = downloadedResourceText, DataKey = downloadedResourceText, Text = "Resource Downloaded" });
}
}
}
If you want to do it with Sitecore, just set the event in the Tracking field for the PDF in the media library.
Then it shows up in the experience profile or you can trigger a engagement plan, etc...
If you are looking to do it programmatically, you have to create the details of the event. You just pass in a string of "User did X" to the page event code you have posted. The itemID is the page they were on when they did it. If it was a brochure, you would have "downloaded the brochure for product XYZ".
Some good details of the properties of the page event call can be found here:
https://doc.sitecore.net/sitecore_experience_platform/82/digital_marketing/marketing_operations/events/register_a_page_event_programmatically
Related
We've built a custom front end for users to post threads and upload images on top of Sitecore 9. Recently, we moved the user generated content to it's own database, so the media files are no longer in the proper sitecore 'media gallery'. Also, file storage is on a networked share. Uploading images works just fine. It's immediately after where the problem lies.
Upon image upload, our rest api returns the media url from the MediaManager.GetMediaUrl(itemId). This also works. It returns a valid url, it is formatted correctly and should resolve. Unfortunately, for some time after, the url does the sitecore dance an 302s to our 404 page.
I can see the image through the content editor and our custom content handler injects the image folder node into both master and web databases.
Why would the link manager be able to find the url, but the image is not available? I uploaded something yesterday afternoon, an when I checked this morning, the image is now available on the site. Any information or suggestions are appreciated.
I have tried saving the image multiple times hoping that this might trigger whatever mystical Sitecore event that causes images to show. Since there isn't a publish due to the separate database, I can't try that. I've removed all versions thinking it couldn't default to a particular language. Nothing. Only time seems to make the images visible. The code below works just fine. I'm just putting it here to to show some work.
public MediaItem UploadSimpleMedia(MediaGallerySimpleUploadRequest request)
{
try
{
var destinationFolder = request.ParentItemId != null
? _content.GetItem<Item>(request.ParentItemId.Value)
: _content.GetItem<Item>(_publicLibraryPath + "/embeded");
var name = !string.IsNullOrEmpty(request.Name)
? ItemUtil.ProposeValidItemName(request.Name)
: ItemUtil.ProposeValidItemName(request.Files[0].FileName);
var creator = new MediaCreator();
var tags = request.Tags != null ? request.Tags.Split(',') : new string[0];
var tagIds = _tagService.GetTagIds(tags);
var tagIdString = string.Join(",", tagIds);
var options = new MediaCreatorOptions()
{
AlternateText = request.Name,
FileBased = true,
IncludeExtensionInItemName = false,
Versioned = false,
Destination = $"{destinationFolder.Paths.Path}/{name}",
Database = Factory.GetDatabase("content")
};
using (new SecurityDisabler())
using (new DatabaseCacheDisabler())
{
MediaItem item = creator.CreateFromStream(request.Files[0].InputStream, request.Files[0].FileName, options);
item.BeginEdit();
item.InnerItem["Title"] = request.Name;
item.InnerItem["Description"] = request.Description;
item.InnerItem["__Semantics"] = tagIdString;
// Sitecore won't calculate MIME Type or File Size automatically unless you upload directly
// to the Sitecore Media Library. We're uploading in-place for private groups for permission inheritance
// and now because of indexing, they are getting uploaded to /Community/Gallery/Files if not private.
item.InnerItem["Size"] = request.Files[0].ContentLength.ToString();
item.InnerItem["Mime Type"] = request.Files[0].ContentType;
item.EndEdit();
// Just pausing here to reflect on why Sitecore is so sexily complicated
// that it takes time to display an image on the CD, but has no problem giving
// up the media url
CacheManager.GetHtmlCache(Sitecore.Context.Site);
_searchService.RefreshIndex(item.ID.Guid);
var fromDatabase = _content.GetItem<Item>(item.ID.Guid);
return fromDatabase;
}
}
catch (Exception ex)
{
_logger.Error(this.GetType().AssemblyQualifiedName, ex);
_logger.Trace(ex.StackTrace);
}
return null;
}
I don't get any error messages either in our custom logs or the default sitecore logs. Images just aren't resolving. Is it caching? I've even nuked all caches by calling CacheManager.ClearAllCaches().
I have a query like this in my React/Apollo application:
const APPLICATIONS_QUERY = gql`
{
applications {
id
applicationType {
name
}
customer {
id
isActive
name
shortName
displayTimezone
}
deployments {
id
created
user {
id
username
}
}
baseUrl
customerIdentifier
hostInformation
kibanaUrl
sentryIssues
sentryShortName
serviceClass
updown
updownToken
}
}
`;
The majority of the items in the query are in a database and so the query is quick. But a couple of the items, like sentryIssues and updown rely on external API calls, so they make the duration of the query very long.
I'd like to split the query into the database portion and the external API portion so I can show the applications table immediately and add loading spinners for the two columns that hit an external API... But I can't find a good example of incremental/progressive querying or merging the results of two queries with Apollo.
This is a good example of where the #defer directive would be helpful. You can indicate which fields you want to defer for a given query like this:
const APPLICATIONS_QUERY = gql`
{
applications {
id
applicationType {
name
}
customer #defer {
id
isActive
name
shortName
displayTimezone
}
}
}
`
In this case, the client will make one request but receive 2 responses -- the initial response with all the requested fields sans customer and a second "patch" response with just the customer field that's fired once that resolver is finished. The client does the heavy lifting and pieces these two responses together for you -- there's no additional code necessary.
Please be aware that only nullable fields can be deferred, since the initial value sent with the first response will always be null. As a bonus, react-apollo exposes a loadingState property that you can use to check the loading state for your deferred fields:
<Query query={APPLICATIONS_QUERY}>
{({ loading, error, data, loadingState }) => {
const customerComponent = loadingState.applications.customer
? <CustomerInfo customer={data.applications.customer} />
: <LoadingIndicator />
// ...
}}
</Query>
The only downside is this is an experimental feature, so at the moment you have to install the alpha preview version of both apollo-server and the client libraries to use it.
See the docs for full details.
I need to do a simple newsletter form. This form should work like this:
User inputs an email and presses to the submit button
User recieves message on email with confirm link
After user clicks on the link his email is added to Recipient list
This form should be work with help EXM
I've created Triggered message in the EXM with link for subscription.
And I wrote this code for the Submit button for trigger the Newsletter Goal
[HttpPost]
public ActionResult NewsletterSubscribe(NewsletterViewBag model)
{
var goal = Context.Database.GetItem(newsletterGoal);
if (goal == null)
{
continue;
}
var registerGoal = new Sitecore.Analytics.Data.Items.PageEventItem(goal);
var eventData = Tracker.Current.CurrentPage.Register(registerGoal);
eventData.Data = goal[DateTime.Now.ToString(CultureInfo.InvariantCulture)];
Tracker.Submit();
}
How I can assign my triggered message to the newsletterGoal?
Also I try manually send message this way:
MessageItem message = Sitecore.Modules.EmailCampaign.Factory.GetMessage(new ID(messageId));
Sitecore.Modules.EmailCampaign.AsyncSendingManager manager = new AsyncSendingManager(message);
var contactId = ClientApi.GetAnonymousIdFromEmail(email);
var recipientId = (RecipientId) new XdbContactId(contactId);
manager.SendStandardMessage(recipientId);
And I see error in the log: The recipient 'xdb:857bbea1-1f18-4621-a798-178399cd0b54' does not exist. But Triggered Message haven't any recipient list
Goals are not assigned directly to messages. You can, however, assign engagement plans and campaigns. Each message has its own engagement plan to handle tracking the contacts actions with the message. If you create a campaign that triggers a goal, you can assign that to the message and it will be associated with the contact when they receive the message. You can also leverage the message engagement plan to trigger events as the contact proceeds through those states.
Also, you're missing some details while recording the contact data.
Look at the newsletter signup control that is included in the EXM module. The important part in there is this:
protected virtual RecipientId RecipientId
{
get
{
RecipientId recipientId = null;
var contactId = ContactId;
if (contactId != (ID)null)
{
recipientId = new XdbContactId(contactId);
}
return recipientId;
}
}
protected virtual ID ContactId
{
get
{
if (!Email.Visible || string.IsNullOrEmpty(Email.Text))
{
return new ID(Tracker.Current.Contact.ContactId);
}
var anonymousId = ClientApi.GetAnonymousIdFromEmail(Email.Text);
return anonymousId.HasValue ? new ID(anonymousId.Value) : new ID(Tracker.Current.Contact.ContactId);
}
}
protected virtual void UpdateEmailInXdb()
{
_recipientRepository.UpdateRecipientEmail(RecipientId, Email.Text);
}
It will write the email address directly to Mongo, rather than waiting for the session to end. Include this and the related RecipientId and ContactId properties in your signup code.
Once they are signed up you can register the goal programmatically or send them to a Thank You page where the goal can be registered (Advanced - Tracking), or send the message and let that register the goal. Or create an engagement plan with states for each step of the process (this is the best way).
You'll also want to add the recipient to a list that the newsletter message can use later. Actually, it looks to me like the example Subscription Form does everything you need.
When I was writing custom display template for SharePoint people search, I wanted to display the manager of the searched user. When I display the manager value returned from SharePoint people search, it displays as follows:
i:0#.f|membership|lpalmer#xyz.com
I want to show the display instead of the account name in my SharePoint display template. Let me know if this can be done either using JavaScript or just by doing some configurations on SharePoint user profile property change.
This cannot be done using just configurations. You will need to query the User Profile Service and get the Display Name using the login name the search service returns.
For obtaining any property you can use something like this:
function getProfilePropertyValueFromLoginName(loginName, propertyName, success, error) {
// Get the current client context and PeopleManager instance.
var clientContext = new SP.ClientContext.get_current();
var peopleManager = new SP.UserProfiles.PeopleManager(clientContext);
// Get user properties for the target user.
// To get the PersonProperties object for the current user, use the
// getMyProperties method.
var personProperties = peopleManager.getPropertiesFor(loginName);
// Load the PersonProperties object and send the request.
clientContext.load(personProperties);
clientContext.executeQueryAsync(
function () {
if (success) {
success(loginName, personProperties.get_userProfileProperties()[propertyName]);
}
}, function (sender, args) {
if (error) {
error(sender, args);
}
});
}
-Hope it helps
i'm attempting to provide a facility on my site that allows a user to create a facebook event for their booking.
http://developers.facebook.com/docs/reference/api/event/
now im doing the correct process:
1) first getting authorisation from the user
https://graph.facebook.com/oauth/authorize?client_id=APP_ID&redirect_uri=http://urlimredirectingto.comtype=web_server
2) requesting for an access token with the "code" that is returned in step 1
https://graph.facebook.com/oauth/access_token
3) using the access_token to create the event ...
string facebookCreateUri = string.Format("https://graph.facebook.com/{0}/events", loggedInMember.FacebookUID);
var formData = new HttpUrlEncodedForm()
{
{"access_token", accessToken},
{"owner", loggedInMember.FacebookUID},
{"description", "nice event that should be on the owners wall"},
{"name", "event on the users wall"},
{"start_time", "1272718027"},
{"end_time", "1272718027"},
{"location", "rochester"},
{"privacy","OPEN"}
};
HttpContent content = HttpContent.Create(formData);
HttpClient client = new HttpClient();
var response = client.Post(facebookCreateUri, "application/x-www-form-urlencoded", content);
but the event is posted on my app's wall, not the user's wall. It shouldn't have anything to do with the authentication/access_token elements because i use the same process to post on the user's wall. (http://developers.facebook.com/docs/reference/api/status/) and that works just fine.
I came back with a solution, after a week of working at many features with Facebook SDK, it finally works!
protected void onPostEvent(object sender, EventArgs e)
{
if (CanvasAuthorizer.Authorize())
{
var fb = new FacebookWebClient(CanvasAuthorizer.FacebookWebRequest);
dynamic parameters = new ExpandoObject();
parameters.description = txtEvDett.Text;
parameters.name = txtEvName.Text;
parameters.start_time = DateTime.Now.ToString("yyyyMMdd");
parameters.end_time = DateTime.Now.AddDays(1).ToString("yyyyMMdd");
parameters.access_token = CanvasAuthorizer.FacebookWebRequest.AccessToken;
dynamic eventDet = fb.Post("me/events", parameters);
litEvent.Text = String.Format("You have created the event with ID: {0}", eventDet.id);
lnkEvent.Visible = true;
lnkEvent.NavigateUrl = String.Format("http://www.facebook.com/event.php?eid={0}", eventDet.id);
}
}
For events, you have to request the create_event permission.
You should use /me/events to post on your events.
I user the C# SDK for Facebook from Codeplex - last version available for dld (aug 2011 - v5.2.1).
Good luck!
I don;t see in your request for Authorization any permission.. base permissions are not enough to do the postings.
i used:
https://www.facebook.com/dialog/permissions.request?app_id=MY_APP_ID&next=MY_APP_URL&display=page&response_type=code&canvas=1&perms=publish_stream,user_about_me,email
This is in the context of a canvas app. where MY_APP_URL is the url from facebook of the app:
http://apps.facebook.com/MY_APP_NAME_OR_ID
See extended permissions for events and check event's page in documentation
[EDIT] - I came back, sorry, now i did a test, and indeed, it works for me, but only of i post on my app's wall; even if i provided the 'user_events' permission i get this error:
The remote server returned an error: (403) Forbidden when posting on a user's wall.
This being said, i also subscribe to this question.