-
-
Notifications
You must be signed in to change notification settings - Fork 134
Interactive Resource Editing
Westwind.Globalization supports resource editing on source HTML pages by providing optional links in these pages to link directly to the referenced resources in the Web based resource editor. Essentially resource icons are added to all HTML elements that include a data-resource-id
element and a data-resource-set
attribute on any parent element, which in turn highlights the specified element with a clickable icon, that when clicked opens the localization admin form with the specified resource highlighted:
In this screen shot the Web application has resource editing enabled which is indicated by the clickable red flags. When a flag is clicked, the resource editor form is popped up with the relevant topic pre-selected.
If no match is found the Add Resource window is opened instead so you can add a new resource immediately. This makes for a very productive enter/edit cycle for resources.
Note: Resource editing can also be extremely useful for CMS like features where content can be dynamically edited and updated, especially when coupled with the MarkDown support in this library.
Resource editing is enabled with the following steps:
- Add the required JavaScript references to your page
- Add CSS to provide the Resource Link display
- Enable Resource Editing by calling
showResourceIcons()
- Mark up elements to edit with
data-resource-id
anddata-resource-set
The idea is that you markup any elements you want to edit with the data-resource-id
attribute. The showResourceIcons()
function when called, then finds each of these marked up elements and renders clickable resource icons using client side script code. The entire process is controlled from the client side, so this works both in ASP.NET Pages/Views as well as plain HTML pages or pure client side SPA style applications.
First you need to add jQuery and the wwResourceEditor.js scripts to any page where you want to edit resources (or in a Layout/Master page if you want this on all pages):
<script src="LocalizationAdmin/bower_components/jquery/dist/jquery.min.js"></script>
Then add the actual ww.resourceEditor.js script:
<script src="localizationAdmin/scripts/ww.resourceEditor.js"></script>
The resource links depend on CSS to render the highlight icon into the page. The icons are rendered ontop of existing controls using absolute positioning. By default a FontAwesome flag symbol is used for the icon, but you can change that to any character by modifying the CSS (see comments in CSS).
<!-- optional - if you don't use change the CSS to display plain character -->
<link href="./LocalizationAdmin/bower_components/fontawesome/css/font-awesome.min.css"
rel="stylesheet"/>
<style>
.resource-editor-icon, .resource-editor-icon:hover, .resource-editor-icon:visited {
position: absolute;
display: inline;
height: 13px;
width: 13px;
text-decoration: none;
zIndex: 999999;
margin: -14px 0 0 -2px;
cursor: pointer;
opacity: 0.45;
}
.resource-editor-icon:hover {
opacity: 1;
}
.resource-editor-icon:before {
font-family: fontawesome;
content: "\f024"; /*flag*/
/*font-family: Arial;
content: "#";*/
font-size: 9pt;
color: red;
}
</style>
You can add these style to individual pages or to a global style sheet.
To activate resource editing call the showResourceIcons()
function along with any options (all are optional):
ww.resourceEditor.showResourceIcons({
adminUrl: "./LocalizationAdmin/", // default
// this causes a popup window. Use "" to show as tabbed view
editorWindowOpenOptions: "height=600, width=900, left=30, top=30",
editorWindowName: "_localization-resource-editor" // default
});
To turn off resource editing:
ww.resourceEditor.removeResourceIcons();
A common way you handle activation is based on a toggle button which looks like this:
<script>
var toggleResourceEditMode = false;
$("#btnEditResources").click(function() {
toggleEditMode = !toggleResourceEditMode;
if(toggleResourceEditMode)
ww.resourceEditor.showResourceIcons({
adminUrl: "./localizationAdmin/"
editorWindowOpenOptions: "height=600, width=900, left=30, top=30",
});
else
ww.resourceEditor.removeResourceIcons();
});
</script>
In order for elements in your Web Page to be linked for resource editing you need to mark them up with two supported attributes:
-
data-resource-id
The specific resource id of the element to link to. This attribute needs to be attached to any element that is associated with the resource. -
data-resource-set
The resource set that that the resource belongs to. This can be declared on the actual element or anywhere in the parent element hierarchy. The library searches up the tree to find the nearest data-resource-set attribute and uses its value.
In practice you typically declare data-resource-set
at the <body>
or some wrapper 'view-level' element and data-resource-id
on each element to display the icon on.
Here's what this might look like in an ASP.NET MVC based view using both DbRes.T() or strongly typed resources if you've exported them:
<body data-resource-set="LocalizationForm">
<div class="page-title"
data-resource-id="PageTitle">
@DbRes.T("PageTitle","LocalizationForm")
</div>
<-- if you have non-wrapped text you can add a <span> tag -->
<span data-resource-id="HelloWorld">@LocalizationForm.HelloWorld</span>
</body>
Clicking on any of these resource links causes a lookup for the LocalizationForm resource set and the PageTitle and HelloWorld resource ids respectively.
For WebForms there's a special DbResourceControl that automatically generates the resource link for most Web Controls so you don't have to explicitly set the attributes.
The resource editing tags are pure client side HTML attributes driven through JavaScript code. The resource editing features are not tied to any ASP.NET server side code.
For example the Resource Editor Form in the screen shot is a SPA application that runs of an Index.html page and uses AngularJs for all of its UI. All resources are embedded into the page using the JavaScript Resource Handler which serves JavaScript resources from the ASP.NET server to the client.
You can still use the data-resource-id
and data-resource-set
tags on the client side content:
<div>
<p data-resource-id="CreateClassInfo">
{{::view.resources.CreateClassInfo}}
</p>
<p data-resource-id="CreateClassInfo2">
{{::view.resources.CreateClassInfo2}}
</p>
</div>
In this example, the database resources are fed to JavaScript using strongly typed resources from the JavaScriptResourceHandler and it works just as well for these resources as long as the id's correlate to the database resource ids.
This resource editing mechanism is based on a client side JavaScript implementation that checks for specific attributes on DOM elements and then injects icon overlays into the HTML document to provide clickable links that open the Resource Editor with the selected resources active.
The icons are attached to HTML elements that have a specific data-resource-id
attribute. In addition a data-resource-set
attribute has to exist to identify the resource set, either on the same HTML element or at an element along the parent DOM tree.
On typical pages you'll have a set up like this:
<body data-resource-set="LocalizationForm">
<div class="page-title"
data-resource-id="PageTitle">
@DbRes.T("PageTitle","LocalizationForm")
</div>
<-- if you have non-wrapped text you can add a <span> tag -->
<span data-resource-id="HelloWorld">@LocalizationForm.HelloWorld</span>
</body>
The data-resource-id
attribute can be attached to any element on an HTML page. In the example above the first element is already wrapped into a <div>
tag and so I can simply attach the attribute to the parent. In the second example the text originally was just plain free standing text without any wrapping element - I added a <span>
tag around the element to allow attaching of the data-resource-id
attribute.
Since the logic is based on pure HTML and JavaScript, this mechanism works with any server side as well as any client side technology. Above I'm using DbRes.T()
and strongly typed resources as an example here to embed the actual values.
In ASP.NET WebForms pages that use meta:resourcekey
tags you can use a DbResourceControl that can automatically generate the data-resource-id
and data-resource-set
attributes for any WebControls on the page so you don't have to manually create the attributes.
For client side code the usage is still the same. For example the following uses AngularJs binding expressions and the usage of the attributes is identical:
<li>
<a ng-show="view.resourceSets && view.resourceSets.length > 0"
data-toggle="modal" data-target="#CreateClassDialog"
title="{{view.resources.CreateClass_Title}}"
data-resource-id="CreateClass">
<i class="fa fa-code" style="font-weight: bold;"></i> {{::view.dbRes('CreateClass')}}
</a>
</li>
<li>
<a ng-show="view.resourceSets && view.resourceSets.length > 0"
ng-click="view.onReloadResourcesClick()"
title="{{view.dbRes('ReloadResources_Title')}}"
data-resource-id="ReloadResources">
<i class="fa fa-refresh"></i> {{::view.dbRes('ReloadResources')}}
</a>
</li>
Again notice the data-resource-id
attributes in both of the elements. Note that in this case both elements have two resources exposed - the value and the title and you can only link to one of them. However the UI shows the CreateClass.Title as a subitem of CreateClass so it's easy to jump to this subitem.
Behind the scenes the client-side library runs through the document and essentially injects DOM elements into the page that represent the icons. The icons are overlaid ontop of the existing DOM element as a small semi-transparent icon that is clickable.
The actual rendered runtime HTML for one of the icons and it's host element looks like this:
<!-- injected element -->
<res-edit class="resource-editor-icon"
target="resourceEditor"
title="Edit resource"></res-edit>
<!-- original element -->
<button class="btn btn-sm btn-default ng-binding"
title="Add new ResourceSet"
ng-click="view.onAddResourceClick()"
data-resource-id="Add">
<i class="fa fa-plus"></i> Add
</button>
Why a <res-edit>
tag? I used a custom tag name to avoid any CSS bleed issues with links or div tags. I had originally used an anchor tag, but I ran into formatting problems as CSS formatting for links would affect the rendering. Using a custom tag minimizes - but doesn't entirely remove - these problems. The CSS uses absolute positioning with a high zIndex value and opacity to overlay existing content so the impact of the icons should be minimal.
In typical applications you'll want to provide resource edit links only in special admin modes and even then only when you explicitly decide to edit resources. Otherwise the icons just get in the way, as well as causing overhead in their generation into the page.
For this reason the icons are not visible by default and you have to explicitly call the showResourceIcons()
function.
For example, in the LocalizationAdmin edit form I enable and disable resource editing with two menu options which are alternately displayed or hidden depending on the state of a view variable in the AngularJs model.
Here's what the click code that is called from either of those buttons looks like to enable and disabling resource editing:
// initial state
vm.showResourceIcons = false;
// toggle handler from menu option or button typically
vm.showResourceIcons = function () {
vm.resourceEditMode = !vm.resourceEditMode; // toggle mode
if (vm.resourceEditMode)
ww.resourceEditor.showResourceIcons({ adminUrl: "./" });
else
ww.resourceEditor.removeResourceIcons();
};
You can check out this behavior in the actual Localization Admin form which is localizable and then links to itself in edit mode.
You will also want to determine whether you want to allow resource editing at all from the server side by not even embedding the library link or startup code based on whether the user has rights.
For example in a Razor view you might want to do:
@{
bool allowResourceEditing = user.IsAdmin && App.Configuration.AllowResourcEditing
}
And then in your template where you load the script and startup code:
@if (allowResourceEditing)
{
<script src="~/localizationAdmin/scripts/ww.resourceEditor.js"></script>
<script>
var toggleEditMode = false;
$("#btnEditResources").click(function() {
toggleEditMode = !toggleEditMode;
if(toggleEditMode)
ww.resourceEditor.showResourceIcons({ adminUrl: "./localizationAdmin/" });
else
ww.resourceEditor.removeResourceIcons();
});
</script>
}
This avoids loading the library when the user isn't authorized to access the resource editing features in the first place.
Resource editing with linkable resources is very useful in dynamic applications that allow editing of content for localization (as well as basic content editing). With the MarkDown support for resources this too can be extremely useful for CMS like features in applications where you want to allow editing of content even if it isn't localizable.
Although it requires a little bit of setup, plus adding extra attributes onto content, this feature provides an easy way to make applications dynamically editable for localization. The DbResourceControl for WebForms provides an even easier approach as most WebControls can automatically be linked for resource editing.
-
Interactive Resource Editing with WebForms
for more information on how you can automate generation of the data-resource-id tags without explicitly having to enter the attributes. -
Server Side Resources for JavaScript with JavaScriptResourceHandler
lets you use ASP.NET Resources in client side only JavaScript application by serving Server side resources as JavaScript resource objects to the client. -
MarkDown Support
lets you enter complex text and HTML markup using the popular Markdown language.