It is unclear to me from the compiler warning which fields I should be using in this code:
Sitecore.Data.Fields.ImageField imgField = item.Fields[FieldName];
if (imgField != null)
{
//Finally, save the actual values for our intended Image into the item
imgField.Src = Sitecore.Resources.Media.MediaManager.GetMediaUrl(mediaItem);
imgField.MediaID = mediaItem.ID;
imgField.MediaPath = mediaItem.MediaPath;
imgField.Alt = mediaItem.Alt;
}
I get a compiler warning about imgField.Src and imgField.MediaPath. The Src warning is 'Use MediaItem property' instead... which makes little sense, because those are entirely different property types. The MediaPath warning says 'You can retrive[sic] Path from MediaItem." Well... Again, this makes very little sense because what I'm actually doing here is setting the necessary properties for MediaItem. It would be empty or NULL otherwise. Should these four lines of code just be changed to imgField.MediaItem = mediaItem? I am skeptical.
What it's trying to transition you away from doing is getting properties from the Imagefield and instead just getting the MediaItem that is referenced by the ImageField and then calling the properties that way..since it's the actual Sitecore item in the media library. Once you do that, you've got access to all the properties just like you would any other Sitecore item.
Related
How can we avoid this behavior so that if Link Description is set on the General Link Field then it takes that value and not fallback to Item name ? Glass Mapper is using the Item name of the target item for type of Internal Link in General Link Field. This behavior is because of the below
case "internal":
var urlOptions = Utilities.CreateUrlOptions(config.UrlOptions);
if (linkField.TargetItem == null) link.Url = string.Empty;
else link.Url = LinkManager.GetItemUrl(linkField.TargetItem, urlOptions);
link.Type = LinkType.Internal;
link.TargetId = linkField.TargetID.Guid;
link.Text = linkField.Text.IsNullOrEmpty() ? (linkField.TargetItem == null ? string.Empty : linkField.TargetItem.Name) : linkField.Text;
break;
in the SitecoreFieldLinkMapper.cs class. I am able to override this class and then register the Component to use the new overridden class to avoid this behavior. However this doesn't work in Page Editor and it doesn't use the new overridden class which has been registered using below code
container.Register(Component.For<Glass.Mapper.AbstractDataMapper>().ImplementedBy<MyProjectSitecoreFieldLinkMapper>().LifestyleCustom<NoTrackLifestyleManager>());
The page editor is not honoring this one. It looks like it is using different technique for resolving the dependency.
This is actually the default behavior of the Sitecore. So Glass is actually doing the right thing. I have to do some other tricks to make it work with my situation.
Is there any way to parametise the Datasource for the 'source' field in the Template Builder?
We have a multisite setup. As part of this it would save a lot of time and irritation if we could point our Droptrees and Treelists point at the appropriate locations rather than common parents.
For instance:
Content
--Site1
--Data
--Site2
--Data
Instead of having to point our site at the root Content folder I want to point it at the individual data folders, so I want to do something like:
DataSource=/sitecore/content/$sitename/Data
I can't find any articles on this. Is it something that's possible?
Not by default, but you can use this technique to code your datasources:
http://newguid.net/sitecore/2013/coded-field-datasources-in-sitecore/
You could possibly use relative paths if it fits with the rest of your site structure. It could be as simple as:
./Data
But if the fields are on random items all over the tree, that might not be helpul.
Otherwise try looking at:
How to use sitecore query in datasource location? (dynamic datasouce)
You might want to look at using a Querable Datasource Location and plugging into the getRenderingDatasource pipeline.
It's really going to depend on your use cases. The thing I like about this solution is there is no need to create a whole bunch of controls which effectively do he same thing as the default Sitecore ones, and you don't have to individually code up each datasource you require - just set the query you need to get the data. You can also just set the datasource query in the __standard values for the templates.
This is very similar to Holger's suggestion, I just think this code is neater :)
Since Sitecore 7 requires VS 2012 and our company isn't going to upgrade any time soon I was forced to find a Sitecore 6 solution to this.
Drawing on this article and this one I came up with this solution.
public class SCWTreeList : TreeList
{
protected override void OnLoad(EventArgs e)
{
if (!String.IsNullOrEmpty(Source))
this.Source = SourceQuery.Resolve(SContext.ContentDatabase.Items[ItemID], Source);
base.OnLoad(e);
}
}
This creates a custom TreeList control and passes it's Source field through to a class to handle it. All that class needs to do is resolve anything you have in the Source field into a sitecore query path which can then be reassigned to the source field. This will then go on to be handled by Sitecore's own query engine.
So for our multi-site solution it enabled paths such as this:
{A588F1CE-3BB7-46FA-AFF1-3918E8925E09}/$sitename
To resolve to paths such as this:
/sitecore/medialibrary/Product Images/Site2
Our controls will then only show items for the correct site.
This is the method that handles resolving the GUIDs and tokens:
public static string Resolve(Item item, string query)
{
// Resolve tokens
if (query.Contains("$"))
{
MatchCollection matches = Regex.Matches(query, "\\$[a-z]+");
foreach (Match match in matches)
query = query.Replace(match.Value, ResolveToken(item, match.Value));
}
// Resolve GUIDs.
MatchCollection guidMatches = Regex.Matches(query, "^{[a-zA-Z0-9-]+}");
foreach (Match match in guidMatches)
{
Guid guid = Guid.Parse(match.Value);
Item queryItem = SContext.ContentDatabase.GetItem(new ID(guid));
if (item != null)
query = query.Replace(match.Value, queryItem.Paths.FullPath);
}
return query;
}
Token handling below, as you can see it requires that any item using the $siteref token is inside an Site Folder item that we created. That allows us to use a field which contains the name that all of our multi-site content folders must follow - Site Reference. As long at that naming convention is obeyed it allows us to reference folders within the media library or any other shared content within Sitecore.
static string ResolveToken(Item root, string token)
{
switch (token)
{
case "$siteref":
string sRef = string.Empty;
Item siteFolder = root.Axes.GetAncestors().First(x => x.TemplateID.Guid == TemplateKeys.CMS.SiteFolder);
if (siteFolder != null)
sRef = siteFolder.Fields["Site Reference"].Value;
return sRef;
}
throw new Exception("Token '" + token + "' is not recognised. Please disable wishful thinking and try again.");
}
So far this works for TreeLists, DropTrees and DropLists. It would be nice to get it working with DropLinks but this method does not seem to work.
This feels like scratching the surface, I'm sure there's a lot more you could do with this approach.
Hello StackOverflow experts,
I would like to know if it would be possible to use Ember.js' computed properties to modify the value of the property before returning to whatever object requests it.
Imagine this simple example:
I have a User object with mail property
When I set the property, I want the email address to change from first.last#example.com to first.last#anotherexample.com, then return it
When I request the property ( via User.get ) I want to get the modified property back.
I think it should be pretty simple by utilising another 'helper' property, like formatted_mail, where I would store and retrieve the formatted value, but I wonder if something like this can be done without additional model properties.
So far, I have this coffescript code, but I always get 'undefined' when reading the property, even though I set it before, so I suspect the value does not get saved by Ember anywhere:
mail: ( ( key, value ) ->
if arguments.length == 1
return this.get 'mail'
else
return value.split( '#' )[0] + '#anotherexample.com'
).property 'mail'
Thank you for your assistance!
You are close to solution.
As computed properties are always cached by default in Ember (you could disable this behaviour using .volatile()), you do not have to specify what to do when arguments.length is 1, except if you want to specify a default value.
So here it should looks like:
App.User = Ember.Object.extend({
mail: function(key, value) {
if (arguments.length === 2) {
return value.split('#')[0] + "#tieto.com";
}
return null;
}.property()
});
The return null just specify the default value.
When you set the mail property, it will cache the returned value and always returns it without recomputing this property.
Note that you can do that only because the mail property does not depend on other properties. If you were declaring it with .property('anotherProperty'), the mail property will be recomputed any time anoterProperty changes. So in the example above it will reset it to null.
You can try it in this JSFiddle.
Anyone knows how to generate links in sitecore with ID instead of item path?
If you use GetMediaUrl method from the API, I can get this URL:
/~/media/Images/Archive/content/News and Events/News_and_Events_Level2/20070419162739/iwhiz3.jpg
The problem with this approach is that if someone changes the media item name, removes it somewhere or deletes it, the above link will break.
I notice if I insert a media link from rich text editor, I get the link as below:
/~/media/14BDED00E4D64DFD8F74019AED4D74EB.ashx
The second link is better because it's using the item id, so if the actual media item is renamed, removed, or deleted, all related links will be updated too. On top of that, when Sitecore renders the page, it will actually convert the above link and display the item path so it's readable.
I'm using Sitecore 6.5 and currently doing content migration so I need to make sure all internal links are updated properly.
May I know if there is a method to generate the second link by using sitecore API?
Thanks!
The GetMediaItemUrl extension method seems to give you what you want.
public static class ItemExtensions
{
public static string GetMediaItemUrl(this Item item)
{
var mediaUrlOptions = new MediaUrlOptions() { UseItemPath = false, AbsolutePath = true };
return Sitecore.Resources.Media.MediaManager.GetMediaUrl(item, mediaUrlOptions);
}
}
[TestFixture]
public class when_using_items_extensions
{
[Test]
public void a_url_based_on_media_item_id_can_be_generated()
{
// Arrange
Database db = global::Sitecore.Configuration.Factory.GetDatabase("master");
Item item = db.GetItem("/sitecore/media library/Images/MyImage");
// Act
var mediaUrl = item.GetMediaItemUrl();
// Assert
Assert.That(mediaUrl, Is.EqualTo("/~/media/17A1341ABEEC46788F2159843DCEAB03.ashx"));
}
}
These are called dynamic links and you can normally generate them using the LinkManager e.g:
Sitecore.Links.LinkManager.GetDynamicUrl(item)
.. but I'm not sure of the method to do this with Media links (there probably is one but I cant seem to find it and its not on MediaManager) but the basic syntax is:
"/~/media/" + item.ID.ToShortID() + ".ashx"
If you always want to use ID's instead of paths, you can change this setting in webconfig to false (like this):
<setting name="Media.UseItemPaths" value="false"/>`
Here is what the webconfig describes about it:
MEDIA - USE ITEM PATHS FOR URLS
This setting controls if item paths are used for constructing media URLs.
If false, short ids will be used.
Default value: true
Then you can use the default implementation (without additional parameters):
Sitecore.Resources.Media.MediaManager.GetMediaUrl(item);
This is what I use:
var imgField = ((Sitecore.Data.Fields.ImageField)currentItem.Fields["Icon"]);
MediaUrlOptions opt = new MediaUrlOptions();
opt.AlwaysIncludeServerUrl = true;
// Absolute Path works as well. So either use AbsolutePath or AlwaysIncludeServerUrl
opt.AbsolutePath = true;
string mediaUrl = MediaManager.GetMediaUrl(imgField.MediaItem, opt);
I am testing subsonic 3, i can query my database but when i am inserting a record i have an exception. Here is my code:
Client lClient = new Client();
lClient.Name = "Peter";
lClient.FullName = "Richards";
lCliente.Save();
And i have a null reference exception on this generated code:
var newKey=_repo.Add(this,provider);
Any help is appreciated.
I am using ActiveRecords
I had a similar problem, with code not unlike the following:
var pending = myTable.SingleOrDefault(x => x.Id == 1);
if (pending == null)
pending = new myTable();
pending.Id = 1;
pending.MyDate = DateTime.Now;
pending.MyString = someString;
pending.Save();
This worked the first time I ran it, on an empty table, but not the second time when updating. I got a nullreference exception somewhere inside the subsonic repository. The solution was to add a primary key, as Rob suggested.
Just thought I'd mention it (thanks Rob).
Where does the null reference exception actually happen? Is _repo null?
Try and double check that you don’t have any columns in the “Client” table which are not nullable and you don’t set any value.
Also make sure you PK is handled correctly (check you have the [Property.IsForeignKey=true;] attribute)
Do you have NullReferenceException Checked in (file menu) Debug -> Exceptions (press Find and type NullReferenceException, press OK)?
I downloaded the SubSonic code and used it as reference for my project, the exception is thrown in SubSonic.Core\Repository\SubSonicRepository.cs:209 because prop is not checked for null at line 208, and any exceptions are swallowed as evidenced by line 213.
What happens is visual studio breaks on all the exceptions checked in the mentioned dialog. So in one way, this is the expected behaviour from the code.
What actually causes prop to become null, in my case, the table field name is lower-case, but the property is actually cased properly.
From Immediate:
tbl.PrimaryKey.Name
"playerinformationid"
item.GetType().GetProperties()
{System.Reflection.PropertyInfo[11]}
[0]: {System.Collections.Generic.IList`1[SubSonic.Schema.IColumn] Columns}
// *snip*
[5]: {Int32 PlayerInformationid}
// *snip*
item.GetType().GetProperty("PlayerInformationid")
{Int32 PlayerInformationid} // Works!
item.GetType().GetProperty(tbl.PrimaryKey.Name)
null
I hacked this together to "just make it work" (Warning: newbie at 3.5 stuff)
-var prop = item.GetType().GetProperty(tbl.PrimaryKey.Name);
+var lowerCasedPrimaryKeyName = tbl.PrimaryKey.Name.ToLowerInvariant();
+var prop = item.GetType().GetProperties().First<System.Reflection.PropertyInfo>(x => x.Name.ToLowerInvariant() == lowerCasedPrimaryKeyName);
Does your table have a PrimaryKey? It doesn't sound like it does. If not - this is your problem.