Using Sitecore 6.5, when images are rendered on a web page, a URL such as the one below is used
~/media/OSS/Images/MyImage
But if you add an image from the library in a content editor a path such as below is used
~/media/1CFDDC34C94E460FAA2B1518DCA22360.PNG
This makes sense as it's trying to use a meaningful path when rendered for the web.
We would like to use the first media image path to add images in the content editor in HTML view rather than the default second method. This is because we are actually taking some html files and automatically adding them in to Sitecore via a script and we can change the image paths to a location in the media library if the first image format is used by using a convention so the images should appear in the newly created items. We have now idea about a media library image ID.
The first format does appear to work as images are rendered in the content editor design editor and when the page is rendered but Sitecore marks these as broken links in the Content Editor. Are any ideas on whether we are safe to use this format?
You may want to avoid hard coding paths to media in the rich text field. The second "dynamic link" is an important feature of Sitecore in that it keeps a connection between the media and item in the Links database. This safeguards you if you ever delete or move the media.
Since it sounds like you are importing content from an external source and you already have a means of detecting the image paths, I would recommend (if possible) that you upload the images programmatically and insert the dynamic links.
Below is a function that you can call for uploading to the Media Library and getting back the media item:
Example usage:
var file = AddFile("/assets/images/my-image.jpg", "/sitecore/media library/images/example", "my-image");
The code:
private MediaItem AddFile(string relativeUrl, string sitecorePath, string mediaItemName)
{
var extension = Path.GetExtension(relativeUrl);
var localFilename = #"c:\temp\" + mediaItemName + extension;
using (var client = new WebClient())
{
client.DownloadFile("http://yourdomain.com" + relativeUrl, localFilename);
}
// Create the options
var options = new MediaCreatorOptions
{
FileBased = false,
IncludeExtensionInItemName = false,
KeepExisting = false,
Versioned = false,
Destination = sitecorePath + "/" + mediaItemName,
Database = Factory.GetDatabase("master")
};
// Now create the file
var creator = new MediaCreator();
var mediaItem = creator.CreateFromFile(localFilename, options);
return mediaItem;
}
As for generating the dynamic link to the media, I actually haven't found a Sitecore method to do this, so I resorted to the following code:
var extension = !String.IsNullOrEmpty(Settings.Media.RequestExtension)
? Settings.Media.RequestExtension
: ((MediaItem)item).Extension;
var dynamicMediaUrl = String.Format(
"{0}{1}.{2}",
MediaManager.MediaLinkPrefix,
item.ID.ToShortID(),
extension);
No it will not cause any rendering issue apart from the broken links notification as you noted. Also when you select an image in the editor and select to edit the media folder will be at the root rather than at the image itself. But as Derek has noted, the use of dynamic links is an important feature to make sure your links do not break if something is moved or deleted.
I would add to his answer that since you are adding the text via a script you can detect images in the text using HtmlAgilityPack (already used in Sitecore) or FizzlerEx (more similar to jQuery syntax), use the code he provided to upload the images to the media library, grab the GUID and replace the src. Something along the lines of:
string content = "<whatever your html to go in the rich text field>";
HtmlDocument doc = new HtmlDocument();
doc.Load(content);
foreach(HtmlNode img in doc.DocumentElement.SelectNodes("//img[starts-with(#src, '/media/')]")
{
HtmlAttribute attr = img["src"];
Item scMediaItem = UploadLocalMedia(attr.Value);
attr.Value = GetDynamicMediaUrl(scMediaItem);
}
Related
I'm writing a WinForms application. I created a Google Doc template file that contains placeholders like {{name}} for various text elements. I can successfully make a copy of this document and use the BatchUpdateDocumentRequest to modify them just fine.
However, I also have an embedded image in the document. I can obtain the objectId for this image just fine. I either want to replace this image with another or remove it from my template and then append my new image to the end of the document. In both cases, the InsertInlineImage or ReplaceImage classes require a URI of the image to insert or replace with. This is where I have an issue.
The image itself has been captured from a control on the WinForms. Its actually a chart. I've saved the image in PNG format since I know that is one of the formats supported by Google drive/docs. I figured in order to use it in the batch update, I would need to upload it first, so I did and got its file id and webcontentlink back in the response.
I'm not locked into any particular way of doing this. I originally tried creating an HTML file, uploading but then it would strip the image from it, so became useless, so I switched gears to using a Google Doc as my template and just try to replace elements in it instead. This went well until I got to the image.
Essentially no matter what I try to specify as the URI, it says the file in not in a supported format.
As far as I can tell, Google expects the URI to actually end in .png or be a real link versus a download URL you'd get from Google Drive.
Here is an example of the code I'm using to attempt to replace the image. The strImageObjectId is the objectId of the Embedded Object image in the template document copy that I want to replace. The Uri is what Google needs to pull the new image from. I'm happy to pull it from my local computer or Google Drive if only I could get it to accept it somehow.
BatchUpdateDocumentRequest batchUpdateRequest = new BatchUpdateDocumentRequest {
Requests = new List<Google.Apis.Docs.v1.Data.Request>()
};
request = new Google.Apis.Docs.v1.Data.Request {
ReplaceImage = new ReplaceImageRequest() {
ImageObjectId = strImageObjectId,
Uri = strChartWebContentLink
}
};
batchUpdateRequest.Requests.Add(request);
DocumentsResource.BatchUpdateRequest updateRequest =
sDocsService.Documents.BatchUpdate(batchUpdateRequest, strCopyFileId);
BatchUpdateDocumentResponse updateResponse = updateRequest.Execute();
I'm happy to use whatever method will get me to a point where I an end up with a Google Doc on Google Drive that was based on a template in which I can replace various text elements, but most importantly add/replace an image.
Thanks so much for the advice.
I got to the point were I believe I was specifying the URI correctly, but then I started getting an access forbidden error instead.
I didn't have time to hunt this one down, so I went back to creating an HTML template with my image, uploading as a Google Doc, exporting to PDF, and then uploading as a PDF. This ended up working because originally I was using a BMP as the file format and that is not supported by Google Docs, so I changed to a PNG instead and it worked just fine.
I think Google Docs needs to add the ability to add an image using a MemoryStream or some other programmatic base64 resource instead of purely being based on URIs and running into temporary upload or permission issues.
Hey I'm doing the same thing with you,
and I got this, by modify the download link format.
from this:
https://docs.google.com/uc?export=download&id={{YOUR GDRIVE IMAGE
ID}
to this
https://docs.google.com/uc?export=view&id={{YOUR GDRIVE IMAGE ID}
e.g :
uri: "https://docs.google.com/uc?export=view&id=1cjgyHqtYSgS0CBT4x-9eQIHRzOIfGgv-"
but the image should be set for public privilege
When using the CMS.DocumentEngine.DocumentHelper to retrieve documents of a custom page type, how would one get the path of an image attached to a Direct Uploader form control property type?
If documents/page are being retrieved in the following way:
DocumentQuery documents = DocumentHelper.GetDocuments("Custom.PageType")
.Path("/some-path", PathTypeEnum.Children)
.OnCurrentSite()
.Culture("en-us")
.Published()
.Page(page, size);
Then looping through retrieved documents to project to a custom class:
foreach(var document in documents) {
Foo foo = new Foo(document.getStringValue("SomeCustomColumnName", ""));
foos.add(foo);
}
Using getStringValue() targeting the Direct Uploader field/property of the custom page type returns a document GUID such as "123456f-5fa9-4f64-8f4b-75c52db096d5".
In transformations, I can use GetFileUrl() such as GetFileUrl("AttachmentColumnName") to get the path, how could I use that in ASPX code behind when working with retrieved documents?
The custom page type data is provided to the client via an ASMX service and Ajax. The returned JSON data is used to generate/append markup to the page, including an <img />.
Thank you for any help you can provide!
You can try something like this in your item template:
/CMSPages/GetFile.aspx?guid=<%# Eval("SomeCustomColumnName")%>
You can also check to see if SomeCustomColumnName is empty or not before generating that link too if you don't want a broken image.
The other option is to convert that file upload to a URL selector and allow the users to upload files to the media library. This is probably a better approach IMHO.
It took me a few hours to figure this out.
In my case, I was able to get the image URL using the following:
Guid pageGuid = ValidationHelper.GetGuid(this.GetValue("ProductPage"), new Guid());
// Creates a new instance of the Tree provider
TreeProvider tree = new TreeProvider(MembershipContext.AuthenticatedUser);
CMS.DocumentEngine.TreeNode doc = DocumentHelper.GetDocuments()
.Where(d => d.NodeGUID == pageGuid)
.FirstOrDefault();
CMS.DocumentEngine.TreeNode pageNode = tree
.SelectNodes()
.WithGuid(doc.DocumentGUID)
.OnCurrentSite()
var document = DocumentHelper.GetDocument(pageNode, tree);
Guid imageGuid = document.GetGuidValue("ImageProperty", new Guid());
DocumentHelper.GetAttachmentUrl(imageGuid, 0);
I'm downloading and saving images from URL. The image can be of any format.
Right click on the image, select Properties and then the Details tab. There is a Comments field. I would like to add text in that field while saving the image . Is it possible.
using (WebClient webClient = new WebClient())
{
webClient.DownloadFile("http://www.example.com/1.jpg", "1.jpg");
}
The property system in Windows has a complex story, you can find more information about it here: Extracting Windows File Properties (Custom Properties) C#
You could try it with the Microsoft.WindowsAPICodePack.Shell nuget package:
var file = ShellFile.FromFilePath("your image.jpg");
using (var writer = file.Properties.GetPropertyWriter())
{
writer.WriteProperty(file.Properties.System.Comment, "hello");
}
But, in the case of images, the shell may not be able to write extra properties on the file itself (most imaging codecs except JPG don't support such a 'comments' metadata). What I suggest in this case is use this CodeFluentRuntime nuget package's CompoundStorage class, here is an example:
static void Main(string[] args)
{
var storage = new CompoundStorage("your image.png", false); // open for write
storage.Properties.Comments = "hello"; // change well-known "comments" property
storage.CommitChanges();
}
It will work and write the property information not stricly on the file, but on NTFS (it works also on pure plain .txt files for example). If you read the file again and browse all properties, like this, you should see it:
static void Main(string[] args)
{
var storage = new CompoundStorage("consent.png"); // open for read
foreach (var prop in storage.Properties)
{
Console.WriteLine(prop.Name + "=" + prop.Value);
}
}
Now, the problem is, with recent versions of Windows, the shell property sheet you show in your question will not display the Comments property (for example, I'm running on Windows 10 and I don't see it although it's properly written). Unfortunately, there is not many other options for images files.
You can use this reference link to solved your problem.
http://www.codeproject.com/Articles/3615/File-Information-using-C
Using Sitecore 7.5, I am trying to store several html files inside of the Media Library. Then in my sublayout codebehind I am attempting to grab the inner content of those html files.
I had this working when I was storing the html file on the server. I would upload the file into the Media Library using 'upload as file', and then use the following code to read the content:
string filename = htmlMediaItem.Fields["File Path"].ToString();
string path = Server.MapPath(filename);
string content = System.IO.File.ReadAllText(path);
However I now would like to do this without storing the files on the server and instead only have them inside the media library. Is there anyway I can do this?
So far I have had a hard time trying to find information on the subject.
Thank you.
From what I understand you want to read content of an html file stored in Media Library.
Sitecore.Data.Items.Item sampleItem = Sitecore.Context.Database.GetItem("/sitecore/media library/Files/yourhtmlfile");
Sitecore.Data.Items.Item sampleMedia = new Sitecore.Data.Items.MediaItem(sampleItem);
using(var reader = new StreamReader(MediaManager.GetMedia(sampleMedia).GetStream().Stream))
{
string text = reader.ReadToEnd();
}
I've had some problem when copying content from a Word document to another Word document.
The document where the information should end up in have a header.
So far I have managed to copy the content to the second document and not affecting the header.
However I can't figure out how to bind the relationships for links and Images.
This is my code so far:
public static void AddContentToTemplateCopy(
string sourceDocumentPath, string endDocumentPath)
{
using (WordprocessingDocument sourceDoc =
WordprocessingDocument.Open(sourceDocumentPath, false))
using (WordprocessingDocument endDoc =
WordprocessingDocument.Open(endDocumentPath, true))
{
var sourceMainPart = sourceDoc.MainDocumentPart;
var sourceBody = sourceMainPart.Document.Body;
var endSection = endDoc.MainDocumentPart.Document.Body.Elements<SectionProperties>();
var endDocMainPart = endDoc.MainDocumentPart;
var sourceBodyClone = sourceBody.CloneNode(true);
sourceBodyClone.ReplaceChild(endSection.FirstOrDefault().CloneNode(true), sourceBodyClone.Elements<SectionProperties>().FirstOrDefault());
endDocMainPart.Document.ReplaceChild(sourceBodyClone, endDocMainPart.Document.Body);
foreach (HyperlinkRelationship link in sourceMainPart.HyperlinkRelationships)
{
endDocMainPart.AddHyperlinkRelationship(link.Uri, link.IsExternal, link.Id);
}
}
I get the following Error : 'rId6' ID conflicts with the ID of an existing relationship for the specified source.
And the if i have a Image in the content it can't be displayed.
If I zip the document and look at the files in the package I can find the Image but for the same reason as the links the Relation
So my question is: How do I bind the links and Images with their "_rels" references? or how do I copy them so that it works..
This is a Relationship link when I have added the link by hand.
<Relationship Target="media/image1.jpg" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Id="rId11"/>
A picture to show that the link text is copied but have no formatting and that the image can't be displayed.
Thanks to the answer by JasonPlutext i managed to use OpenXML PowerTools (Version 2.2). Keep in mind that the .Net version is 3.5 when importing the project. You Might need to change it. (Supports Open XML 2.5 as well from what I've noticed)
Very simple to create new documents and take parts from old documents.
The code here is in my case where I want the formatting and content from one and then the Header from a template document. The order matters.
Hopefully this will save time for others with the same problem.
public static void AddContentToTemplateCopy(string templateDocumentPath,
string contentDocumentPath,
List<Source> sources,
string outName)
{
sources = new List<Source>()
{
new Source(new WmlDocument(contentDocumentPath),false),
new Source(new WmlDocument(templateDocumentPath),true),
};
DocumentBuilder.BuildDocument(sources, outName);
}
You might find it easier to try Eric White's document builder.