Today I came across a small issue with the language xml files that developers create for each project.
The customer just wants to edit the text translations, not adding elements or changing anything in the xml schema.
This seems like a pretty useful thing for an editor, but there isn't anything available in EPi for this. I looked at the EPiCode's ManageLanguages but it was not capable of editing single files in a simple way (you must always compare 2 xml files), and I also couldn't get it to load up the custom lang files.
So I built this little feature, very easy to use.
Area: PluginArea.ActionWindow
Folder: it is now looking for the files in /lang folder (easily changed in .cs file)
The xml is parsed and the dropdownlist is populated with all elements containing a value in elements innertext
Requirements:
in the codebehind in function 'LoadFiles()', resides the check for which file to add to the list, change that to your filenames, now it is "lang_" since all files are called "lang_EN.xml" etc
Change the Edit.This and Edit/This in both markup and codebehind to your own paths/namespace.
Then you are done!
Create the LanguageManager.ascx
Markup:
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="LanguageManager.ascx.cs"
Inherits="Edit.This.Plugins.LanguageManager" %>
<script language="javascript" src="/util/javascript/common.js" type="text/javascript"></script>
<b>Language manager</b>
<br />
<br />
Handle the translations in the language xml files.
<br />
<br />
<asp:dropdownlist runat="server" id="ddl_files" autopostback="true" onselectedindexchanged="ddl_selected"
width="300"> <asp:ListItem>Select language file:</asp:ListItem> </asp:dropdownlist>
<br />
<br />
<asp:placeholder runat="server" id="ph_nodebox" visible="false"> <asp:DropDownList runat="server" ID="ddl_nodes" AutoPostBack="true" OnSelectedIndexChanged="ddl_nodes_changed" Width="300"></asp:DropDownList> <br /> <br /> <asp:PlaceHolder runat="server" ID="ph_editing" Visible="false"> <asp:TextBox runat="server" ID="EditNodePreviewInnerText" ForeColor="Teal" TextMode="MultiLine" Rows="5" Width="297"></asp:TextBox> <br /><br> <asp:Button runat="server" ID="Button1" Text="Update text" OnClick="Button1_Click" /> </asp:PlaceHolder> </asp:placeholder>
<br />
<asp:label runat="server" id="msg" forecolor="Red"></asp:label>
<script type="text/javascript" language="javascript"> if (window.parent != null && window.parent.parent != null && window.parent.parent.document.all['epCustomDIV'] != null) window.parent.parent.document.all['epCustomDIV'].parentElement.style.pixelWidth = 400;</script>
Codebehind C#:
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using System.Xml;
using System.Xml.XPath;
using EPiServer;
using EPiServer.Core;
using EPiServer.PlugIn;
namespace Edit.This.Plugins
{
[GuiPlugIn(DisplayName = "Language manager", Area = PlugInArea.ActionWindow, Description = "Manage language xml files", Url = "~/Edit/This/Plugins/LanguageManager.ascx", SortIndex = 50)]
public partial class LanguageManager : System.Web.UI.UserControl
{
Stack st = new Stack();
Queue q = new Queue();
protected void Page_Load(object sender, EventArgs e)
{
msg.Text = "";
if (!IsPostBack)
LoadFiles();
}
protected void ddl_selected(object sender, EventArgs e)
{
if (ddl_files.SelectedIndex == 0)
{
ph_nodebox.Visible = false; return;
}
EditNodePreviewInnerText.Text = "";
ph_editing.Visible = false;
XmlDocument xDoc = new XmlDocument();
try
{
xDoc.Load(ddl_files.SelectedItem.Value);
XPathNavigator navigator = xDoc.CreateNavigator();
navigator.MoveToRoot(); navigator.MoveToFirstChild();
SearchForText(navigator);
if (q.Count != 0)
FillNodeBox(q);
}
catch(Exception ex)
{
msg.Text = "Could not load from path: " + ddl_files.SelectedItem.Value + "<br>" + ex.Message;
}
}
protected void ddl_nodes_changed(object sender, EventArgs e)
{
if (ddl_nodes.SelectedIndex == 0)
{
ph_editing.Visible = false; return;
}
ph_editing.Visible = true;
XmlDocument xDoc = new XmlDocument(); xDoc.Load(ddl_files.SelectedItem.Value);
XmlNode testnode = xDoc.SelectSingleNode(ddl_nodes.SelectedItem.Value); EditNodePreviewInnerText.Text = testnode.InnerText; } protected void Button1_Click(object sender, EventArgs e) { if (!CheckXmlIsOk(EditNodePreviewInnerText.Text.Trim())) { msg.Text = "Text contains characters not valid for a xml document. Nothing was saved."; return; } XmlDocument xDoc = new XmlDocument(); xDoc.Load(ddl_files.SelectedItem.Value); XmlNode nodeEditing = xDoc.SelectSingleNode(ddl_nodes.SelectedItem.Value); nodeEditing.InnerText = EditNodePreviewInnerText.Text.Trim(); xDoc.Save(ddl_files.SelectedItem.Value); msg.Text = "Language file '" + ddl_files.SelectedItem.Text + "' was successfully updated."; } private void FillNodeBox(Queue nodequeue) { ph_nodebox.Visible = true; ddl_nodes.Items.Clear(); ddl_nodes.Items.Add(new ListItem("Select a translation to edit:")); while (q.Count > 0) { string p = Convert.ToString(q.Dequeue()); p = p.TrimEnd('/'); string[] arr = p.Split('/'); string text = ""; int i = 0, total = arr.Length - 1; foreach (string segment in arr) { if (i == total || i == (total - 1)) text += segment + "/ "; i++;
}
//string t = p.Substring(p.LastIndexOf("/"));
//t = t.Trim('/');
text = text.Replace("[1]", "");
text = text.Trim();
text = text.Trim('/');
ddl_nodes.Items.Add(new ListItem(text, p));
}
}
private bool CheckXmlIsOk(string text)
{
if (String.IsNullOrEmpty(text))
return true;
if (text.Contains("&")) return false;
if (text.Contains("<")) return false;
if (text.Contains(">")) return false;
return true;
}
private XPathNavigator SearchForText(XPathNavigator navigator)
{
//If node type is element, put in stack and move inside of current element
if (navigator.NodeType == XPathNodeType.Element)
{
PushInStack(st, (XPathNavigator)navigator.Clone());
if (navigator.HasChildren)
{
navigator.MoveToFirstChild();
navigator = SearchForText(navigator);
}
}
//If node type is text then match with required value
else if (navigator.NodeType == XPathNodeType.Text)
{
if (navigator.Value.Trim().ToLower().Length >= 0)
{
Stack st2 = new Stack();
st2 = (Stack)st.Clone();
string val = "";
//Retrive stack values and Build XPath
while (st2.Count != 0)
val = st2.Pop().ToString() + "/" + val;
//Put in Queue
q.Enqueue(val);
}
//If sibling is available, then check if it contains other nodes /* e.g. node : * <PARENT>text <CHILD>text</CHILD></PARENT> * */
if (navigator.MoveToNext())
navigator = SearchForText(navigator);
//come to immediate parent
else { navigator.MoveToParent();
return navigator;
}
}
//Pop one element from stack and move to next sibling
if (st.Count > 0)
st.Pop();
if (navigator.MoveToNext())
navigator = SearchForText(navigator);
else if (navigator.MoveToParent())
return navigator;
return navigator;
}
private void PushInStack(Stack st, XPathNavigator navigator)
{ string currentName = navigator.Name;
int index = 1;
while (navigator.MoveToPrevious())
{ if (navigator.Name.Trim() == currentName)
//Same named element node exists
index++;
}
st.Push(currentName + "[" + index.ToString() + "]");
}
private DirectoryInfo GetDirectory()
{
return new DirectoryInfo(Server.MapPath("/") + @"lang\");
}
private void LoadFiles()
{
foreach (FileInfo f in GetDirectory().GetFiles())
{
if(f.Name.StartsWith("lang_"))
ddl_files.Items.Add(new ListItem(f.Name,f.FullName));
}
}
}
}
Inga kommentarer:
Skicka en kommentar