How to use bitmap icons - softwareloop/alfresco-inboxes GitHub Wiki
To start, let's see a screenshot of what we want to achieve:
Download the Silk icon set.
The distribution file is called famfamfam_silk_icons_v013.zip
.
In the source code of alfresco-inboxes locate the folder
src/main/amp/web/components/softwareloop/inboxes/
and uncompress the zip file
there.
To be sure the paths are correct, check that the PNG files are located at
src/main/amp/web/components/softwareloop/inboxes/famfamfam_silk_icons_v013/icons/
Locate the folder src/main/amp/web/js/softwareloop/inboxes/templates/
. It
contains the html templates for the components of the alfresco-inboxes plugin.
The one that defines an inbox is Inbox.html
:
<div class="inboxes-inbox"
data-dojo-attach-event="click:clickHandler">
<div class="inboxes-inbox-counter" data-dojo-attach-point="counterNode"> </div>
<div class="inboxes-inbox-text">
<i class="inboxes-inbox-icon ${iconClass}"></i>
<span class="inboxes-inbox-title">${title}</span>
</div>
</div>
The structure of the html is simple:
- a top div container containing:
- an inboxes-inbox-counter div for the numeric counter on the right side of an inbox
- an inboxes-inbox-text containing:
- an icon element
- a title span
The icon element implemented as an i element with a css class is the typical
approach used by icon fonts. To use bitmap icons we're going to replace it with
a plain img element. To do so, copy Inbox.html
to a new file BitmapInbox.html
in the same directory, and populate it with the following content:
<div class="inboxes-inbox"
data-dojo-attach-event="click:clickHandler">
<div class="inboxes-inbox-counter" data-dojo-attach-point="counterNode"> </div>
<div class="inboxes-inbox-text">
<img src="${absoluteIconPath}" style="vertical-align: bottom"/>
<span class="inboxes-inbox-title">${title}</span>
</div>
</div>
This file is an html template, so ${absoluteIconPath}
is a parameter that will
be replaced automatically when the template is instantiated. Instantiated when
and replaced with what? This is where we need a new JavaScipt component.
The standard component that implements an inbox is called Inbox.js
and is
located at src/main/amp/web/js/softwareloop/inboxes/
. When customising a
component, there are two approaches that can be taken: one is to modify the
component's code directly, the other is to extend the standard component by
creating a new one.
The latter approach is arguably better to increase encapsulation, separation, and re-use.
So create a file called BitmapInbox.js
in the same directory as Inbox.js
and
start with the following content:
define([
'dojo/_base/declare',
'./Inbox'
], function (declare, Inbox) {
return declare([Inbox], {
});
});
These seven lines of code are the bread and butter of many Dojo/Aikau
customisations. The global function define()
takes two arguments. The first is
an array of dependencies, the second is a function that uses those dependencies.
The function returns a class declaration using declare()
. The first argument is
an array of parent classes that will be inherited. Here we're declaring that
BitmapInbox
inherits from Inbox
.
The second argument of declare()
is a "mix-in", an object that will be mixed
into the declaration by taking its properties and adding them to the new class,
along with those of the inherited classes.
A mix-in is a powerful tool, as its properties can override those of an inherited class. Let's use this feature right away:
define([
'dojo/_base/declare',
'./Inbox',
'dojo/text!./templates/BitmapInbox.html'
], function (declare, Inbox, template) {
return declare([Inbox], {
templateString: template
});
});
Here templateString
is a property of the mix-in, but it's also a property of the
parent class Inbox. The templateString
of the mix-in takes precedence,
effectively overriding the same property of Inbox.
dojo/text!./templates/BitmapInbox.html
is the syntax used to load the html we
created in the previous section and make it available through the template
dependency. template
is simply a string containing the html and its value is
assigned to templateString
.
Now it's time to look after the absoluteIconPath
parameter.
define([
'dojo/_base/declare',
'dojo/_base/lang',
'./Inbox',
'dojo/text!./templates/BitmapInbox.html'
], function (declare, lang, Inbox, template) {
return declare([Inbox], {
templateString: template,
postMixInProperties: function () {
this.inherited(arguments);
this.absoluteIconPath = lang.replace(
"{urlRescontext}components/softwareloop/inboxes/famfamfam_silk_icons_v013/icons/{iconPath}",
{
urlRescontext: Alfresco.constants.URL_RESCONTEXT,
iconPath: this.iconClass
}
);
}
});
});
The postMixInProperties
function is executed automatically when the component
is initialised. This is a good place to set the absoluteIconPath
property. Its
value is calculated using the lang.replace()
function that provides a simple
string templating mechanism. Notice that the last part of the path is provided
by this.iconClass
where iconClass
is one of the attributes in
inboxes.get.config.xml
.
Locate the inboxes.get.config.xml
file in
src/main/amp/config/alfresco/web-extension/site-webscripts/softwareloop/inboxes/
.
This is the main web script configuration, as explained in a previous page.
In the file, an inbox definition may look like this:
<inbox id="drafts" iconClass="foundicon-paper-clip">
... where foundicon-paper-clip
is a css class specific to the ZURB Foundation Icon
font. In the BitmapInbox component we have to use iconClass
with a different
meaning, i.e. to indicate the name of an icon image. For example:
<inbox id="drafts" iconClass="attach.png">
For the list of available icons, browse the contents of:
src/main/amp/web/components/softwareloop/inboxes/famfamfam_silk_icons_v013/icons/
The BitmapInbox component is in place but nobody is using it yet. We need to make the alfresco-inboxes plugin aware of the new component so it can be used instead of the default Inbox.
Alfresco-inboxes supports this sort of extension through the inboxClass attribute:
<inbox id="drafts" iconClass="attach.png" inboxClass="softwareloop/inboxes/BitmapInbox">
inboxClass
takes the fully qualified name of a dojo/Aikau component to be used
as the inbox implementation. Its default value is softwareloop/inboxes/Inbox
,
i.e., the standard inbox component.
Notice that each inbox has its own iconClass
meaning that each inbox can have a
different implementation. For example it is possible to have some inboxes use
icon fonts through the default Inbox implementation and other inboxes use bitmap
icons through the custom BitmapInbox we just created.
BitmapInbox.html:
<div class="inboxes-inbox"
data-dojo-attach-event="click:clickHandler">
<div class="inboxes-inbox-counter" data-dojo-attach-point="counterNode"> </div>
<div class="inboxes-inbox-text">
<img src="${absoluteIconPath}" style="vertical-align: bottom"/>
<span class="inboxes-inbox-title">${title}</span>
</div>
</div>
BitmapInbox.js:
define([
'dojo/_base/declare',
'dojo/_base/lang',
'./Inbox',
'dojo/text!./templates/BitmapInbox.html'
], function (declare, lang, Inbox, template) {
return declare([Inbox], {
templateString: template,
postMixInProperties: function () {
this.inherited(arguments);
this.absoluteIconPath = lang.replace(
"{urlRescontext}components/softwareloop/inboxes/famfamfam_silk_icons_v013/icons/{iconPath}",
{
urlRescontext: Alfresco.constants.URL_RESCONTEXT,
iconPath: this.iconClass
}
);
}
});
});
Inbox.html:
<div class="inboxes-inbox"
data-dojo-attach-event="click:clickHandler">
<div class="inboxes-inbox-counter" data-dojo-attach-point="counterNode"> </div>
<div class="inboxes-inbox-text">
<i class="inboxes-inbox-icon ${iconClass}"></i>
<span class="inboxes-inbox-title">${title}</span>
</div>
</div>
inboxes.get.config.xml:
<inboxes>
<group id="my-documents">
<inbox id="drafts" iconClass="attach.png" inboxClass="softwareloop/inboxes/BitmapInbox">
<query><![CDATA[
SELECT d.*, t.*
FROM cmis:document AS d
JOIN cm:titled AS t on d.cmis:objectId = t.cmis:objectId
WHERE contains(d, 'PATH:"/app:company_home/st:sites/cm:swsdp/cm:documentLibrary/cm:Meeting_x0020_Notes/*"')
]]></query>
</inbox>
<inbox id="for-my-approval" iconClass="accept.png" inboxClass="softwareloop/inboxes/BitmapInbox">
<query><![CDATA[
SELECT d.*, t.*
FROM cmis:document AS d
JOIN cm:titled AS t on d.cmis:objectId = t.cmis:objectId
WHERE contains(d, 'PATH:"/app:company_home/st:sites/cm:swsdp/cm:documentLibrary/cm:Budget_x0020_Files/cm:Invoices/*"')
]]></query>
</inbox>
<inbox id="overdue" iconClass="clock.png" inboxClass="softwareloop/inboxes/BitmapInbox">
<query><![CDATA[
SELECT d.*, t.*
FROM cmis:document AS d
JOIN cm:titled AS t on d.cmis:objectId = t.cmis:objectId
WHERE contains(d, 'PATH:"/app:company_home/st:sites/cm:swsdp/cm:documentLibrary/cm:Agency_x0020_Files/cm:Contracts/*"')
]]></query>
</inbox>
<inbox id="high-priority" iconClass="flag_red.png" inboxClass="softwareloop/inboxes/BitmapInbox">
<query><![CDATA[
SELECT d.*, t.*
FROM cmis:document AS d
JOIN cm:titled AS t on d.cmis:objectId = t.cmis:objectId
WHERE contains(d, 'PATH:"/app:company_home/st:sites/cm:swsdp/cm:documentLibrary/cm:Agency_x0020_Files/cm:Mock-Ups/*"')
]]></query>
</inbox>
</group>
<group id="archive">
<inbox id="invoices" iconClass="page.png" inboxClass="softwareloop/inboxes/BitmapInbox">
<query><![CDATA[
SELECT d.*, t.*
FROM cmis:document AS d
JOIN cm:titled AS t on d.cmis:objectId = t.cmis:objectId
WHERE d.cmis:objectTypeId='cmis:document'
AND d.cmis:createdBy = 'mjackson'
]]></query>
</inbox>
<inbox id="purchase-orders" iconClass="arrow_left.png" inboxClass="softwareloop/inboxes/BitmapInbox">
<query><![CDATA[
SELECT d.*, t.*
FROM cmis:document AS d
JOIN cm:titled AS t on d.cmis:objectId = t.cmis:objectId
WHERE d.cmis:objectTypeId='cmis:document'
AND d.cmis:createdBy = 'abeecher'
]]></query>
</inbox>
<inbox id="quotations" iconClass="arrow_right.png" inboxClass="softwareloop/inboxes/BitmapInbox">
<query><![CDATA[
SELECT d.*, t.*
FROM cmis:document AS d
JOIN cm:titled AS t on d.cmis:objectId = t.cmis:objectId
WHERE contains(d, 'PATH:"/app:company_home/st:sites/cm:swsdp/cm:documentLibrary/cm:Presentations/*"')
]]></query>
</inbox>
<inbox id="marketing-documents" iconClass="world.png" inboxClass="softwareloop/inboxes/BitmapInbox">
<query><![CDATA[
SELECT d.*, t.*
FROM cmis:document AS d
JOIN cm:titled AS t on d.cmis:objectId = t.cmis:objectId
WHERE contains(d, 'PATH:"/app:company_home/st:sites/cm:swsdp/cm:documentLibrary/cm:Agency_x0020_Files/cm:Images/*"')
]]></query>
</inbox>
</group>
</inboxes>
Despite the simplicity of the task at hand, this tutorial shows a customisation pattern that has broad applicability:
- identify the component that needs customisation,
- create a new component that inherits from the standard one,
- extend and override in the new component,
- let the framework know about the new component.
The pattern will prove useful to customise the inbox results on the right side of the plugin's page as discussed in this other page.