I have two Resource files under the app_GlobalResources folder in my Website project, (CaptionsA.resx and CaptionsB.resx), for CustomerA and CustomerB,respectively.
For example, in CaptionsA.resx, I have:
MyButtonText ------> Click me!
And in CaptionsB.resx, I have:
MyButtonText ------> Click Here
I have to use captions on multiple pages in my Website. But, when CustomerA uses the website all the captions from CaptionsA.resx should be visible and when CustomerB uses the website all the captions from CaptionsB.resx should be visible. Keep in mind that both customers use English as the website language, So I can't use the culture/language localization thingy.
What I want to ask is:
How to programmatically tell my website which Resource file to use when?
What to write in my VB.net code?
How to access the Resource File in my code?
If CustomerType = CustomerA
//RETRIEVE DATA FROM CaptionsA.resx (How to do this?)
else If CustomerType = CustomerB
//RETRIEVE DATA FROM CaptionsB.resx (How to do this?)
And what shall I write in the aspx source file?
<asp:Label ID="LblButtonText" runat="server" Text="<%$ Resources:**WHAT-TO-WRITE-HERE?**,MyButtonText %>"></asp:Label>
I have been searching a lot and have tried to find the answer on a gazillion forums, but threads related to this topic were mostly unanswered or were not helpful.
here is how you do it..
Dim resourceFileBaseName As String = "WebApplicationNamespace.app_GlobalResources.CaptionsA"
Dim isCustomerB As Boolean = True
If isCustomerB Then
resourceFileBaseName = "WebApplicationNamespace.app_GlobalResources.CaptionsB"
End If
Dim customerBasedResourceManager = New System.Resources.ResourceManager(resourceFileBaseName, GetType(CaptionsA).Assembly)
Dim resourceManagerField = GetType(CaptionsA).GetField("resourceMan", BindingFlags.[Static] Or BindingFlags.NonPublic)
resourceManagerField.SetValue(Nothing, customerBasedResourceManager)
All ResX files generate an equivalent class (e.g. CaptionsA) which have an underlying ResourceManager which points to the CaptionsA resource containing all the strings. Based on the customer type, we can make this resource manager point to the right underlying resx file. but this Resource Manager is internal to the class, hence we need to reflect and set the value. Also, the CaptionsA and CaptionsB have no relation to each other, otherwise we could have leveraged some pattern/casting to access their members.
what we're doing in the above code is:
set the right resource file base name based on the customer type. (ensure you're using the right namespace path for the classes)
create a custom resourcemanager which points to our actual resx file.
set the CaptionsA class' resourcemanager to our custom one by reflection.
now whenever you try to access a resource, based on the underlying resx it'll access captionsA.resx or CaptionsB.resx.
one thing you'll notice is that you'll be accessing resources of CaptionsB.resx too via CaptionsA class. this is unavoidable and is the closest to the culture based seamless resource access we can get via non-culture based varying resources.
for the fun of it, here is the C# code as well.
string resourceFileBaseName = "WebApplicationNamespace.app_GlobalResources.CaptionsA";
bool isCustomerB = true;
if (isCustomerB)
{
resourceFileBaseName = "WebApplicationNamespace.app_GlobalResources.CaptionsB";
}
var customerBasedResourceManager = new System.Resources.ResourceManager(resourceFileBaseName,
typeof(CaptionsA).Assembly);
var resourceManagerField = typeof(CaptionsA).GetField("resourceMan", BindingFlags.Static | BindingFlags.NonPublic);
resourceManagerField.SetValue(null, customerBasedResourceManager);
CaptionsA.MyButtonText will point to the value based on the customer type's resx file.
Related
I have been struggling with this issue for some time now.
I have a web interface where I create projects, with this you should be able to upload multiple documents to the server, which is attached to the project they belong to.
So far I made it work with only uploading one file at a time.
My idea was maybe to add the "file" objects in an arralylist and then bind it to a gridview and then upload it to the server?
I know this sound a bit confusing, but I'm confused also!
This is how my method look like when I upload a single file to the server and also save the file's ID, name and context in the database:
File file = new File();
Projects_Overview po = new Projects_Overview();
po.Name = TextBoxName.Text;
po.Time = time;
po.Owner = TextBoxOwner.Text;
po.Responsibility = TextBoxResp.Text;
po.Created = datecreate;
po.StartDate = datestart;
po.FinishDate = datefinish;
po.ETC = dateETC;
po.Description = TextBoxDesc.Text;
po.Comments = TextBoxComments.Text;
po.Remember = TextBoxRemember.Text;
ie.Projects_Overview.AddObject(po);
ie.SaveChanges();
if (uploadFile.HasFile)
{
string filepath = uploadFile.PostedFile.FileName;
string fileName = Path.GetFileName(filepath);
file.Context = Path.GetFileNameWithoutExtension(uploadFile.PostedFile.FileName);
file.Extension = Path.GetExtension(uploadFile.PostedFile.FileName).Replace(".", "").Substring(0, 4);
file.FileName = Guid.NewGuid();
string fileSavePath = Server.MapPath("") + "\\Wordfiles\\" + Convert.ToString(file.FileName) + ".upl";
uploadFile.PostedFile.SaveAs(fileSavePath);
file.ProjectID = po.ID;
ie.File.AddObject(file);
ie.SaveChanges();
Can someone tell me how to "attach" multiple files and then save them in the folder along with the other data as I posted above?
Cheers
I believe that HTML 5 has the built-in capability to handle multi-file uploads (where HTML4 does not), but I have not worked with HTML5 yet. With HTML4, you must use an external control (typically either javascript or flash), as there is no built-in mechanism to allow multi-file uploads.
I did write a multi file upload page that works with HTML4 or HTML5 utilizing PLupload (an external control) to perform the file picking. I had to find a generic file handler to interface the control to the .aspx upload page. I think that I found that here.
It took a couple hours to implement, but it's worked great.
We have a requirement where the url of a page needs to be localizable/translated. Our existing mechanism relies on the actual published url to retrieve the page via oData. To clarify with a simplified example: we have some logic in the front end that takes the request url (which doesn't have a file extension, appends a .html extension, e.g.:
/my-awesome-path/my-awesome-page
now becomes
/my-awesome-path/my-awesome-page.html
the logic then pulls the page from oData using the query
/odata.svc/Pages?$filter=url eq '/my-awesome-path/my-awesome-page.html'
There is much more logic that we have around this to parse this SEO-friendly url and get MVC controller function parameters and other whatnots, but that's not relevant here.
Our requirement is that we cannot localize the page to give it a translated url since this would mean the entire page can't be managed in the parent web publication.
To get the localized path leading up to the page filename we simply localize the SGs. The difficulty is with the page filename. On the page's metadata we have a linked "localizable metadata" component with a field for providing a localized page filename.
What we'd like to do is update the page's URL property during the publishing/deployment process to update the page's published url with the localized page filename from this linked metadata component (assume that we have access to the localized filename field's value at any stage between start of publishing to commitment of deployment).
I've tried doing this via a custom resolver, however, at this level it appears that the page.PublishedUrl property is already established by the CM and cannot be overridden. So updating the page.FileName property doesn't do anything useful.
I've also tried directly updating the URL column in the PAGE table in Broker DB to a different name and it appears that everything continues to work, including dynamic linking and unpublishing of the page. Obviously writing a storage extension or a deployer extension to directly update the DB via jdbc is unacceptable.
Here are the options I'm thinking of:
1) try a deployer extension and use the Tridion API to update the url property
2) try writing a custom renderer that executes the url replace logic without actually updating the url in the broker. I don't favour this since request-time processing is required each time.
My question is: what is the most appropriate way to update the page url property? Will writing a custom deployer using Tridion APIs to update the URL property lead me to a dead end just like the Resolver did?
Following the points in Nuno's comment above I decided against using a custom deployer and have solved the problem using 2 event subscriptions of the Event System. On page publish I first localize the page, grab the localized filename from the localized linked metadata component and save the page. Then in a subsequent event I simply unlocalize the page. Here is my working code:
[TcmExtension("Publish or Unpublish Events")]
public class PublishOrUnpublishEvents : TcmExtension
{
public PublishOrUnpublishEvents()
{
EventSystem.Subscribe<Page, PublishEventArgs>(SetLocalizedPageFileName, EventPhases.Initiated);
EventSystem.Subscribe<Page, SetPublishStateEventArgs>(UnlocalizePageOncePublished, EventPhases.Initiated);
}
public void SetLocalizedPageFileName(Page page, PublishEventArgs args, EventPhases phase)
{
string localFilename = GetLocalilizedFileNameFromPageMetadata(page);
if (!string.IsNullOrEmpty(localFilename))
{
page.Localize();
if (page.TryCheckOut())
{
page.FileName = localFilename;
page.Save(true);
}
}
}
public void UnlocalizePageOncePublished(Page page, SetPublishStateEventArgs args, EventPhases phase)
{
string localFilename = GetLocalilizedFileNameFromPageMetadata(page);
if (!string.IsNullOrEmpty(localFilename))
page.UnLocalize();
}
private string GetLocalilizedFileNameFromPageMetadata(Page page)
{
string localFilename = string.Empty;
if (page.Metadata != null)
{
ItemFields fields = new ItemFields(page.Metadata, page.MetadataSchema);
if (fields.Contains("LocalizableMeta"))
{
ComponentLinkField localMetaField = fields["LocalizableMeta"] as ComponentLinkField;
Component component = localMetaField.Value;
ItemFields compFields = new ItemFields(component.Content, component.Schema);
if (compFields.Contains("LocalizedPageFilename"))
{
SingleLineTextField fileNameTextField = compFields["LocalizedPageFilename"] as SingleLineTextField;
localFilename = fileNameTextField.Value;
}
}
}
return localFilename;
}
}
Perhaps another option:
Store the localised URL has an additional metadata field for the page, keeping the same physical URL for the published pages.
I see your requirement is to avoid localisation of child pages, I like the way it's possible in wordpress to enter globally how URLs work, for example:
/mysite/%postname%/
It would be cool to build something similar to this within SDL Tridion, where the content title could be extracted and used at the content URL.
Either way, if you'd have to write a system that takes the 'Friendly URL' and does a look up for the actual URL, which I think would be pretty simple.
I'm developing an ASP.NET webapp that has a multilanguage feature allowing the webmaster to create new languages at runtime.
The approach that I was thinking is the following:
The user selects one available (not created) language.
When the user confirms, the application automatically copies a set of existing resources, replacing the filename with the new culture. For example: default.aspx.en-us.resx to default.aspx.es-ar.resx.
The user edits the recently created resources.
Currently I'm having troubles with step number 2. I've achieved to copy the resources, but then these new resources are ignored. I think that this happens because the new resources are not included in the running assembly, and therefore are being ignored.
When I test the following code in my local project, I would have to manually add the new resources to the solution and then recompile to make it work.
Does anyone know how to make this work?
This is the code of the mentioned copy.
string _dir = path_ + "App_LocalResources\\\\";
DirectoryInfo _dirInfo = new DirectoryInfo(_dir);
foreach (FileInfo _file in _dirInfo.GetFiles("*en-us.resx")) {
_file.CopyTo(_dir + _file.Name.Replace("en-us", idioma_.Cultura));
}
string _dir2 = path_ + "App_GlobalResources\\\\";
_dirInfo = new DirectoryInfo(_dir2);
foreach (FileInfo _file in _dirInfo.GetFiles("*en-us.resx")) {
_file.CopyTo(_dir2 + _file.Name.Replace("en-us", idioma_.Cultura));
}
Thank you very much.
Creating or editing Resource files is not possible the same way as reading data.
In order to create or edit a resource file, you should do it the same way you create or edit XML files because resource files have with a specific structured XML elements.
Maybe this article will help you...
First, the overall description:
There are two Component Templates, NewsArticle and NewsList. NewsArticle is a Dreamweaver Template, and is used to display the content of a news article. NewsList is an xml file that contains aggregated information about all of the news articles.
Currently, a content author must publish the news article, and then re-publish the newslist to regenerate the xml.
Problem:
I have been tasked with having the publish of a news article also regenerate and publish the newslist. Through C#, I am able to retrieve the content of the newslist component, generate the updated xml from the news article, and merge it into the xml from the newslist. I am running into trouble getting the newslist to publish.
I have limited access to documentation, but from what I do have, I believe using the static PublishEngine.Publish method will allow me to do what I need. I believe the first parameter (items) is just a list that contains my updated newslist, and the second parameter is a new PublishInstruction with the RenderInstruction.RenderMode set to Publish. I am a little lost on what the publicationTargets should be.
Am I on the right track? If so, any help with the Publish method call is appreciated, and if not, any suggestions?
Like Quirijn suggested, a broker query is the cleanest approach.
In a situation if a broker isn't available (i.e. static publishing model only) I usually generate the newslist XML from a TBB that adds the XML as a binary, rather than kicking off publishing of another component or page. You can do this by calling this method in your C# TBB:
engine.PublishingContext.RenderedItem.AddBinary(
Stream yourXmlContentConvertedToMemoryStream,
string filename,
StructureGroup location,
string variantId,
string mimeType)
Make the variantId unique per the newslist XML file that you create, so that different components can overwrite/update the same file.
Better yet, do this in a Page Template rather than Component Template so that the news list is generated once per page, rather than per component (if you have multiple articles per page).
You are on the right tracks here with the engine.Publish() method:
PublishEngine.Publish(
new IdentifiableObject[] { linkedComponent },
engine.PublishingContext.PublishInstruction,
new List() { engine.PublishingContext.PublicationTarget });
You can just reuse the PublishInstruction and Target from the current context of your template. This sample shows a Component, but it should work in a page too.
One thing to keep in mind is that this is not possible in SDL Tridion 2011 SP1, as the publish action is not allowed out of the box due to security restrictions. I have an article about this here http://www.tridiondeveloper.com/the-story-of-sdl-tridion-2011-custom-resolver-and-the-allowwriteoperationsintemplates-attribute
I have 2 different project. One is supposed to upload images (admin) and the other is supposed to show them.
I was writing something like "/Contents/images/image path"... But wait! I will I upload the images from the application into that address?
Any help and suggestions please.
If you have two applications that will interact with the same files, it's probably better to have an ImageController with an action that allows you to upload/download the image rather than storing them directly as content. That way both applications can reference the same file location or images stored in a database and manipulate them. Your download action would simply use a FileContentResult to deliver the bytes from the file. You can derive the content type from the file extension.
Example using a database. Note that I assume that the database table contains the content type as determined at upload time. You could also use a hybrid approach that stores the image metadata in a database and loads the actual file from a file store.
public class ImageController : Controller
{
public ActionResult Get( int id )
{
var context = new MyDataContext();
var image = context.Images.SingleOrDefault( i => i.ID == id );
if (image != null)
{
return File( image.Content, image.ContentType );
}
// or you could return a placeholder image here if appropriate.
throw new HttpException( 404, "The image does not exist" );
}
}
An alternative would be to incorporate your administrative interface in an area of the same application rather than in a separate project. This way you could reuse the content/images directory if you wanted. I find that when you have dynamic images the database or a hybrid approach works better from a programming perspective since it's more consistent with the rest of your data model.
you could try like this..
Let's assume that all of your images are in Project A and you want to use the same images in Project B.
Open Project B with Visual Studio. In the Solution Explorer, right click your Project Name and select "Add Existing Item...".
Browse to the physical location on disc where your images in Project A are stored and select the files that you want to import.
You'll then be able to access those images from project A in Project B.