Tridion 2009 SP1: Broker how to get Binary Url? - tridion

I am trying to retrieve the Binary Url of a multimedia component's file that is published as a dynamic Component Presentation.
I can see the Url in the Binaries table within the Broker database but I can't seem to get the binary url using either of the following bits of code:
using SQLBinaryMetaHome:
using (var sqlBinMetaHome = new Com.Tridion.Broker.Binaries.Meta.SQLBinaryMetaHome())
{
int componentItemId = int.Parse(queryStringId.Split('-')[1]);
var binaryMeta = sqlBinMetaHome.FindByPrimaryKey(new TCDURI(publicationId, 16, componentItemId));
if (binaryMeta != null)
{
VideoBinaryUrl = binaryMeta.GetURLPath();
}
else
{
Logger.Log.ErrorFormat("Failed ot load via SQL Binary Meta {0}", queryStringId);
}
}
Using Binary Meta factory:
using (var b = new BinaryMetaFactory())
{
var binaryMeta = b.GetMeta(queryStringId);
if (binaryMeta != null)
{
VideoBinaryUrl = binaryMeta.UrlPath;
}
else
{
Logger.Log.ErrorFormat("Failed to load binary meta {0}", queryStringId);
}
}
I can load the Component Meta data using the ComponentMetaFactory.
Any ideas on why I can't load the Binary Meta? Am I on the right track?
Rob

It looks like your first example is importing (auto-generated) methods from an internal DLL (Tridion.ContentDelivery.Interop.dll). Please don't use those and stick to the ones in the Tridion.ContentDelivery namespace (Tridion.ContentDelivery.dll).
You can find the official documentation for the Content Delivery .NET API in CHM format on SDL Tridion World (click the link, log in to the site and click the link again). From that documentation comes this example:
//create a new BinaryMetaFactory instance:
BinaryMetaFactory binaryMetaFactory = new BinaryMetaFactory();
//find the metadata for the specified binary
BinaryMeta binaryMeta = binaryMetaFactory.GetBinaryMeta("tcm:1-123");
//print the path to the output stream:
if(binaryMeta!=null) {
Response.Write("Path of the binary: " + binaryMeta.UrlPath);
}
//Dispose the BinaryMetaFactory
binaryMetaFactory.Dispose();
The factory class is Tridion.ContentDelivery.Meta.BinaryMetaFactory from Tridion.ContentDelivery.dll. I indeed also can't find a GetBinaryMeta method in that class, so it seems there is a mistake in the code sample. The most likely method that you should use is GetMeta.

Is there a reason you are not using a Binary Link to get a Link object to the specific Variant of the binary you want? Keep in mind that any DCP may render multiple variations of your multimedia component. From the Link object you can then get the URL to the binary.
Look for BinaryLink in the documentation for more details.

Try this:-
BinaryMeta binaryMeta = b.GetBinaryMeta(queryStringId);
if(binaryMeta != null) {
VideoBinaryUrl = binaryMeta.URLPath;
}

I did a SQL Profiler on the code and noticed that it was because I deployed my test app it wasn't calling the broker. Running the code within the actual Tridion Published site did hit the database but it was passing the value "[#def#]" for the variantId column.
I have now got it working with the following code:
IComponentMeta cm = cmf.GetMeta(queryStringId);
if (cm != null)
{
TcmId = queryStringId;
Title = cm.TryGetValue("title");
Summary = cm.TryGetValue("summary");
Product = cm.TryGetValue("product");
if (cm.SchemaId == StreamingContentSchemaId)
{
VideoId = cm.TryGetValue("video_url");
IsVimeo = true;
}
else if (cm.SchemaId == WebcastSchemaId)
{
using (var b = new BinaryMetaFactory())
{
var binaryMeta = b.GetMeta(queryStringId, "tcm:0-" + cm.OwningPublicationId + "-1");
if (binaryMeta != null)
{
VideoBinaryUrl = binaryMeta.UrlPath;
}
else
{
Logger.Log.ErrorFormat("Failed to load binary meta {0}", queryStringId);
}
}
}

Related

ASPNET Core 3.1 RedirectResult no longer works in Production

I have an asp.net core application that is hosted at SmarterAsp.net. Basically, I'm inserting data into a sql db with an IActionResult method (see below). After the db insert, I redirect to a page which shows the inserted data. Everything previously worked in production. Everything still works locally.
Now, when I run in Production, the NewValueArray fires, the db is updated but then I get back a "Page not found 404 error" for www.mydomain.com/SiteAssessmentValue/Index?id=##" (where ## is the route value for the assessment).
Two things have changed.
One, I updated from Core 2.2 to Core 3.1.
I also believe there have been changes by SmarterAsp.net on their servers. It only does this in production. I don't know how to troubleshoot this. Even if I change the Redirect to something innocuous like www.google.com, I get the same message.
For some reason, the RedirectResult no longer works. Any idea why that could be? I don't know how to fix something that I can't see what's failing since it only happens in production.
public IActionResult NewValueArray(int? assessmentId)
{
if (Convert.ToInt32(assessmentId) != 0)
{
var data = (
from oa in _context.OutcomeAreas
join oat in _context.OutcomeAttributes on oa.Id equals oat.OutcomeAreaId
join oas in _context.OutcomeAttributeScales on oat.Id equals oas.AttributeId
select new
{
AreaActive = oa.Active,
AttributeActive = oat.Active,
ScaleActive = oas.Active,
ScaleDescription = oas.Description,
oas.ScaleType,
oa.OrganizationId,
oas.Id
}).ToList().Where(z => z.OrganizationId == HttpContext.Session.GetInt32("curOrgId")
&& z.AreaActive == true && z.AttributeActive==true & z.ScaleActive==true);
foreach (var SiteId in data)
{
decimal curScaleFactor;
if (SiteId.ScaleType == "AA")
{
curScaleFactor = 0.2m;
}
else
{
curScaleFactor = 1.0m;
}
var entityValue = new SiteAssessmentValue
{
AssessmentId = Convert.ToInt32(assessmentId),
ItemId = SiteId.Id,
Description = SiteId.ScaleDescription,
Active = true,
ScaleFactor = curScaleFactor,
ActiveScaleFactor = 1.0m,
ModBy = HttpContext.User.Identity.Name.ToString(),
ModDate = DateTime.Now
};
// Add the entity.
_context.SiteAssessmentValue.Add(entityValue);
// Insert the entities in the database.
_context.SaveChanges();
}
// Update the SiteAssessmentValue table with the current ActiveScaleFactor
var id = new SqlParameter("#assessmentId", assessmentId);
_context.Database.ExecuteSqlRaw("EXEC savActiveScaleFactor_upd #assessmentId", id);
}
return Redirect(Url.Content("~/SiteAssessmentValues/Index?id=")+assessmentId);
}
Turns out it was an issue at the host site. They reinstalled core 3.1. Sorry for the bother.

Changelog method based on project tracker template

Based on the project tracker I have integrated a changelog into my app that relates my UserSettings model to a UserHistory model. The latter contains the fields FieldName, CreatedBy, CreatedDate, OldValue, NewValue.
The relation between both models works fine. Whenever a record is modified, I can see the changes in a changelog table. I now want add an "undo"-button to the table that allows the admin to undo a change he clicks on. I have therefore created a method that is handled by the widget that holds the changelog record:
function undoChangesToUserRecord(changelog) {
if (!isAdmin()) {
return;
}
var fieldName = changelog.datasource.item.FieldName;
var record = changelog.datasource.item.UserSettings;
record[fieldName] = changelog.datasource.item.OldValue;
}
In theory method goes the connection between UserHistory and UserSettings up to the field and rewrites its value. But when I click on the button, I get a "Failed due to circular reference" error. What am I doing wrong?
I was able to repro the issue with this bit of code:
google.script.run.ServerFunction(app.currentPage.descendants.SomeWidget);
It is kinda expected behavior, because all App Maker objects are pretty much complex and Apps Script RPC has some limitations.
App Maker way to implement it would look like this:
// Server side script
function undoChangesToUserRecord(key) {
if (!isAdmin()) {
return;
}
var history = app.models.UserHistory.getRecord(key);
if (history !== null) {
var fieldName = history.FieldName;
var settings = history.UserSettings;
settings[fieldName] = history.OldValue;
}
}
// Client side script
function onUndoClick(button) {
var history = widget.datasource.item;
google.script.run
.undoChangesToUserRecord(history._key);
}

Visual Studio Extension: Get the path of the current selected file in the Solution Explorer

I'm trying to create my first extension for visual studio and so far I've been following this tutorial to get me started (http://www.diaryofaninja.com/blog/2014/02/18/who-said-building-visual-studio-extensions-was-hard).
Now I have a custom menu item appearing when I click on a file in the solution explorer.
What I need now for my small project is to get the path of the file selected in the solution explorer but I can't understand how can I do that.
Any help?
---------------------------- EDIT ------------------------------
As matze said, the answer is in the link I posted. I just didn't notice it when I wrote it.
In the meanwhile I also found another possible answer in this thread: How to get the details of the selected item in solution explorer using vs package
where I found this code:
foreach (UIHierarchyItem selItem in selectedItems)
{
ProjectItem prjItem = selItem.Object as ProjectItem;
string filePath = prjItem.Properties.Item("FullPath").Value.ToString();
//System.Windows.Forms.MessageBox.Show(selItem.Name + filePath);
return filePath;
}
So, here are two ways to get the path to the selected file(s) :)
For future reference:
//In your async method load the DTE
var dte2 = await ServiceProvider.GetGlobalServiceAsync(typeof(SDTE)) as DTE2;
var selectedItems = dte2.SelectItems;
if(selectedItems.MultiSelect || selectedItems.Count > 1){ //Use either/or
for(short i = 1; i <= selectedItems.Count; i++){
//Get selected item
var selectedItem = selectedItems[i];
//Get associated project item (selectedItem.ProjectItem
//If selectedItem is a project, then selectedItem.ProjectItem will be null,
//and selectedItem.Project will not be null.
var projectItem = selectedItem.ProjectItem;
//Get project for ProjectItem
var project = projectItem.ContainingProject;
// Or get project object if selectedItem is a project
var sproject = selectedItem.Project;
//Is selectedItem a physical folder?
var isFolder = projectItem.Kind == EnvDTE.Constants.vsProjectItemKindPhysicalFolder;
//Else, get item's folder
var itemFolder = new FileInfo(projectItem.Properties.Item("FullPath").ToString()).Directory;
//Find config file
var configFiles itemFolder.GetFiles("web.config");
var configfile = configFiles.length > 0 ? configFiles[0] : null;
//Turn config file into ProjectItem object
var configItem = dte2.solution.FindProjectItem(configFile.FullName);
}
}
I hope someone finds this helpful...
The article you mentioned already contains a solution for that.
Look for the menuCommand_BeforeQueryStatus method in the sample code. It uses the IsSingleProjectItemSelection method to obtain an IVsHierarchy object representing the project as well as the id of the selected item. It seems that you can safely cast the hierarchy to IVsProject and use it´s GetMkDocument function to query the item´s fullpath...
IVsHierarchy hierarchy = null;
uint itemid = VSConstants.VSITEMID_NIL;
if (IsSingleProjectItemSelection(out hierarchy, out itemid))
{
IVsProject project;
if ((project = hierarchy as IVsProject) != null)
{
string itemFullPath = null;
project.GetMkDocument(itemid, out itemFullPath);
}
}
I don´t want to copy the entire code from the article into this answer, but it might be of interest how the IsSingleProjectItemSelection function obtains the selected item; so I just add some notes instead which may guide into the right direction... The method uses the GetCurrentSelection method of the global IVsMonitorSelection service to query to the current selected item.

ASP.Net MVC 3.0 C# How to find if a property Exists?

Hi i would like to check if a property exists?
This is my code
string abpath=null;
var hc= HttpContext.Current.Request.UrlReferrer;
if (hc.AbsolutePath !=null)
{
var _temp = HttpContext.Current.Request.UrlReferrer.AbsolutePath;
abpath = _temp.ToString();
}
I would like to Find out if AbsolutePath exists
can any one help me how to check.
right now it throws error as AbsolutePath doesn't exists to check
Thanks for your time
First you have to map the absolute path (url) to a local file system path. Then you can check whether the file exists:
var localPath = Server.MapPath(hc.AbsolutePath);
var exists = System.IO.File.Exists(localPath);
Update:
I guess I misunderstood the question. The problem is, that if your page/action/etc is called directly (e.g. by entering its URL in the browser), then there is no Referrer (previous page). So you have to first check Request.UrlReferrer for null:
if (hc != null && hc.AbsolutePath != null)
{
// ...
}
BTW: since AbsolutePath is already a string, there is no need to call ToString()on it. So you can simplify your code some more:
if (hc != null)
{
abpath = hc.AbsolutePath;
}
Check for url referrer first.
So try using code as below,
string abpath=null;
var hc= HttpContext.Current.Request.UrlReferrer;
if (hc !=null && !string.isNullOrEmpty(hc.AbsolutePath))
{
var _temp = HttpContext.Current.Request.UrlReferrer.AbsolutePath;
abpath = _temp.ToString();
}

Accessing the object/row being edited in Dynamic Data

I'm modifying the "Edit.aspx" default page template used by ASP.NET Dynamic Data and adding some additional controls. I know that I can find the type of object being edited by looking at DetailsDataSource.GetTable().EntityType, but how can I see the actual object itself? Also, can I change the properties of the object and tell the data context to submit those changes?
Maybe you have found a solution already, however I'd like to share my expresience on this.
It turned out to be a great pita, but I've managed to obtain the editing row. I had to extract the DetailsDataSource WhereParameters and then create a query in runtime.
The code below works for tables with a single primary key. If you have compound keys, I guess, it will require modifications:
Parameter param = null;
foreach(object item in (DetailsDataSource.WhereParameters[0] as DynamicQueryStringParameter).GetWhereParameters(DetailsDataSource)) {
param = (Parameter)item;
break;
}
IQueryable query = DetailsDataSource.GetTable().GetQuery();
ParameterExpression lambdaArgument = Expression.Parameter(query.ElementType, "");
object paramValue = Convert.ChangeType(param.DefaultValue, param.Type);
Expression compareExpr = Expression.Equal(
Expression.Property(lambdaArgument, param.Name),
Expression.Constant(paramValue)
);
Expression lambda = Expression.Lambda(compareExpr, lambdaArgument);
Expression filteredQuery = Expression.Call(typeof(Queryable), "Where", new Type[] { query.ElementType }, query.Expression, lambda);
var WANTED = query.Provider.CreateQuery(filteredQuery).Cast<object>().FirstOrDefault<object>();
If it's a DD object you may be able to use FieldTemplateUserControl.FindFieldTemplate(controlId). Then if you need to you can cast it as an ITextControl to manipulate data.
Otherwise, try using this extension method to find the child control:
public static T FindControl<T>(this Control startingControl, string id) where T : Control
{
T found = startingControl.FindControl(id) as T;
if (found == null)
{
found = FindChildControl<T>(startingControl, id);
}
return found;
}
I found another solution, the other ones did not work.
In my case, I've copied Edit.aspx in /CustomPages/Devices/
Where Devices is the name of the table for which I want this custom behaviour.
Add this in Edit.aspx -> Page_Init()
DetailsDataSource.Selected += entityDataSource_Selected;
Add this in Edit.aspx :
protected void entityDataSource_Selected(object sender, EntityDataSourceSelectedEventArgs e)
{
Device device = e.Results.Cast<Device>().First();
// you have the object/row being edited !
}
Just change Device to your own table name.

Resources