Short description
A new concept for accesskey support in Moin 1.8
Having accesskeys is Moin is an often requested feature: Accesskeys speed up working with the wiki and are especially very helpful for disabled persons. However one big problem is, that you can't hardcode accesskeys into Moin code since every language has its own shortcuts for different actions. But there are also known conflicts between assistive technologies and browser/webpage shortcuts. That's why Moin has never implemented accesskeys for standard wiki operations.
The solution to the above mentioned problem is, that Moin ships with a basic accesskey support funtionality instead of built-in, working accesskeys. This means: it is finally up to the user and/or wikiadmin, to define the accesskeys he/she wants. Moin therefore ships without accesskeys but with the ability to define accesskeys as needed.
This is done as follows:
The Accesskey-Augmentation-Javascript-Function
First we have to modify common.js and provide there two new functions:
function addAccessKeys(){ // Add 'accesskey=".."' attribute to the elments specified in var shortcut_list for (i=0; i < shortcut_list.length; i++){ // Split up the values which are stored like this: // Variant 1: (elementtype[name/id])#(elementname)=(accesskey) // Variant 2: (elementtype[name/id])#(elementname)=(accesskey)!(descriptiontext) var short1 = shortcut_list[i].split("#"); var short2 = short1[1].split("="); var short3 = short2[1].split("!"); var type = short1[0]; var name = short2[0]; var key = short3[0]; if (short3.length > 1) { var desc = short3[1]; } else { var desc = "" } //alert("type: " + type + " name: " + name + " key: " + key + " desc:" + desc); // Add keys if (type == "id") { var element = document.getElementById(name); if (element != null) { //alert("OK!!!!!!!!!! name:" + name + " element:" + element) var ak = document.createAttribute("accesskey"); ak.nodeValue = key; element.setAttributeNode(ak); addAccessKeyDescription(document.getElementById('accesskeys'), key, name, desc); } else { //alert("ERROR!!!!!! type:" + type + " name:" + name +" not found") } } else { var elements = document.getElementsByName(name); for (j=0; j < elements.length; j++) { if (elements[j] != null) { //alert("OK!!!!! name:" + name + " elements[" + j + "]:" + elements[j]) var ak = document.createAttribute("accesskey"); ak.nodeValue = key; elements[j].setAttributeNode(ak); addAccessKeyDescription(document.getElementById('accesskeys'), key, name, desc); } else { //alert("ERROR!!!!!!!! type:" + type + " name:" + name +" not found") } } } } } function addAccessKeyDescription(div, key, name, desc){ // If there is a 'div id="accesskeys"' in the page, add a list of available shortcuts so users get a clue // which keys to hit for a certain action if (div != null) { //alert("div is there"); key_list = document.getElementById('accesskey_list'); if (key_list == null) { key_list = document.createElement("ul"); div.appendChild(key_list); var id = document.createAttribute("id"); id.nodeValue = "accesskey_list"; key_list.setAttributeNode(id); //alert("Created ul keylist"); } if (desc == ""){ var text = document.createTextNode(key + " = " + name) } else { var text = document.createTextNode(key + " = " + desc) } key_list.appendChild(document.createElement("li")).appendChild(text); } else { //alert("No div"); } }
The function addAccessKeys() is called by the load-Function when loading/displaying a page ("onload event")
function load() { // Do not name this "onload", it does not work with IE :-) // TODO: create separate onload for each type of view and set the // correct function name in the html. // e.g <body onlod='editor_onload()'> // Page view stuff update_edit_links(); add_gui_editor_links(); // Editor stuff show_switch2gui(); addAccessKeys(); }
The addAccessKeys()-Function does look for an javascript array "var shortcut_list" which holds the definition of the shortcut-keys, analyses/searches the page for matches and adds the respective accesskey attribute to the node. After that, the function calls the function "addAccessKeyDescription". This is not necessary to get accesskeys really work. It is mainly done for usablility sake's and to help users. The addAccessKeyDescription-Function looks for a 'div id="accesskeys"' and adds to that div a list of available accesskeys on the current page. Normally you would put that div in the footer of a page. Simply by looking there you can now see, which accesskeys you can use at the moment on the current page to trigger some actions.
Putting the "var shortcut_list" into the page header
The javascript array "var shortcut_list" is an array of strings with a special format:
"name#nav=1!Navigation Menu", "id#searchinput=2!Search", "name#texteditlink=E!Edit Page"
or more general
"[name/id]#[element]=[shortcut key]![description text]"
[name/id] specifies whether the node in the DOM tree has a name or id. [element] is the name/id of the element. [shortcut key] defines the accesskey for the element. [description text] is the description text which is displayed by the addAccessKeyDescription-Function in the footer of a page.
This string array is put into the page header by a modified headerscript-Function in theme/_init_.py:
1 def headscript(self, d):
2 """ Return html head script with common functions
3
4 Changed: Added support for customizable accesskeys
5
6 @param d: parameter dictionary
7 @rtype: unicode
8 @return: script for html head
9 """
10 # Don't add script for print view
11 if self.request.action == 'print':
12 return u''
13
14 _ = self.request.getText
15 script = u"""
16 <script type="text/javascript">
17 <!--
18 var search_hint = "%(search_hint)s";
19 //-->
20 </script>
21 """ % {
22 'search_hint': _('Search'),
23 }
24
25 # Accesskey customization
26 user = self.request.user
27 content = ''
28 if user.valid and user.name:
29 homewiki, homepage = wikiutil.getInterwikiHomePage(self.request)
30 # We don't support interwiki homepages at the moment.
31 # In the long run better move accesskey customization to the userprefs menu instead
32 # of using an attached file 'shortcuts.js" to the user's homepage.
33 # This will solve most security, perfomance and interwiki homepage problems
34 if homewiki == 'Self':
35 from MoinMoin.action import AttachFile
36 import os
37 pagename, filename = AttachFile.absoluteName('shortcuts.js', homepage)
38 fname = wikiutil.taintfilename(filename)
39 fpath = AttachFile.getFilename(self.request, pagename, fname)
40 base, ext = os.path.splitext(filename)
41 try:
42 # Try to get the user's shortcut list
43 content = file(fpath, 'r').read()
44 content = wikiutil.decodeUnknownInput(content)
45 # Escape malicious code
46 ## Turned off due to i18n problems with regex checking
47 ## paras = []
48 ## try:
49 ## paras = content.split(',')
50 ## except:
51 ## paras[0] = content
52 ## import re
53 ## pattern1 = re.compile('^"(name|id)\#[-_a-zA-Z0-9]+\=[a-zA-Z0-9]+"$')
54 ## pattern2 = re.compile('^"(name|id)\#[-_a-zA-Z0-9]+\=[a-zA-Z0-9]+\![ -_a-zA-Z0-9]+"$')
55 ## for para in paras:
56 ## fail1 = fail2 = False
57 ## if re.search(pattern1, para.strip()) == None:
58 ## fail1 = True
59 ## if re.search(pattern2, para.strip()) == None:
60 ## fail2 = True
61 ## if fail1 and fail2:
62 ## content = ''
63 ## break
64
65 content = content.replace(')', '')
66 content = content.replace(';', '')
67
68 except:
69 # User hasn't specified a shortcut list
70 pass
71
72 if not content:
73 # If there is no user shortcut list: Do we have some global shortcut lists
74 # set in wikiconfig.py?
75 lang_keydefaults = 'accesskey_defaults_%s' % self.request.lang
76 # Check whether there is a shortcut list fitting to the request.lang object
77 if hasattr(self.request.cfg, lang_keydefaults):
78 content = getattr(self.request.cfg, lang_keydefaults)
79 # Otherwise check if 'accesskey_defaults' is set
80 elif hasattr(self.request.cfg, 'accesskey_defaults'):
81 content = self.request.cfg.accesskey_defaults
82
83 script += u"""
84 <script type="text/javascript">
85 <!--
86 var shortcut_list = new Array (%(shortcut_list)s);
87 //-->
88 </script>
89 """ % { 'shortcut_list': content,}
90
91 return script
The function looks first whether we have a kown user which has provided a "shortcuts.js" file as an attachment to his wikihomepage. The shortcut.js file defines the acceesskeys. If not, the function tries to retrieve from the wikiconfig.py the accesskeys the wikiadmin has set for a specific language, e.g. for de
accesskey_defaults_de = u'"name#nav=1!Navigation MenĂ¼", "id#searchinput=2!Volltextsuche", "name#texteditlink=E!Seite editieren"'
If there is no language specific shortcut definition, the function tries to get the general, language independent definition
accesskey_defaults = u'"name#nav=1!Navigation Menu", "id#searchinput=2!Search", "name#texteditlink=E!Edit Page"'
If there is no general shortcut definition, there are no accesskeys added to the page.
Adding the 'div id="accesskeys"' to the page
To get the little accesskey cheat-sheet work, one needs to add the div somewhere in the page, e.g. somewhere in the footer:
1 def footer(self, d, **keywords):
2 """ Assemble wiki footer
3
4 @param d: parameter dictionary
5 @keyword ...:...
6 @rtype: unicode
7 @return: page footer html
8 """
9 page = d['page']
10 html = [
11 # End of page
12 self.pageinfo(page),
13 self.endPage(),
14
15 # Pre footer custom html (not recommended!)
16 self.emit_custom_html(self.cfg.page_footer1),
17
18 # Footer
19 u'<div id="footer">',
20 self.editbar(d),
21 self.credits(d),
22 self.showversion(d, **keywords),
23 self.showaccesskeys(d),
24 u'</div>',
25
26 # Post footer custom html
27 self.emit_custom_html(self.cfg.page_footer2),
28 ]
29 return u'\n'.join(html)
with self.showaccesskeys(d):
1 def showaccesskeys(self, d):
2 """ Create accesskey html
3
4 New helper function.
5
6 If you put a <div id="accesskeys></div> somewhere in the page (e.g. by setting in wikiconfig.py
7 "page_footer2 = u'<div id="accesskeys"></div>'"), the javascript function responsible for
8 accesskeys augmentation of the page will add here a list of available shortcuts for the current
9 page.
10
11 However since we want to have also some translated text before this list, we have to do it here so as to be
12 able to use the built-in getText.
13
14 @param d: parameter dictionary
15 @rtype: unicode
16 @return: accesskey list html
17 """
18 _ = self.request.getText
19 html = ''
20 show_keys = False
21 if hasattr(self.request.cfg, 'show_accesskeys'):
22 show_keys = self.request.cfg.show_accesskeys
23 if show_keys:
24 html = u'<div id="accesskeys"><h1 class="screenreader_info">%s</h1>%s</div>' % (_('Accesskeys'),_('Available accesskeys on this page:'))
25 return html
so you can turn of cheat-sheet in wikiconfig.py as follows
show_accesskeys = True
Accesskey description is added by a Javascript function (see also above):
function addAccessKeyDescription(div, key, name, desc){ // If there is a 'div id="accesskeys"' in the page, add a list of available shortcuts so users get a clue // which keys to hit for a certain action if (div != null) { //alert("div is there"); key_list = document.getElementById('accesskey_list'); if (key_list == null) { key_list = document.createElement("ul"); div.appendChild(key_list); var id = document.createAttribute("id"); id.nodeValue = "accesskey_list"; key_list.setAttributeNode(id); //alert("Created ul keylist"); } if (desc == ""){ var text = document.createTextNode(key + " = " + name) } else { var text = document.createTextNode(key + " = " + desc) } key_list.appendChild(document.createElement("li")).appendChild(text); } else { //alert("No div"); } }
That's the core of the new accesskey support in Moin.
What else to do?
The get this accesskey support really work in Moin, Moin development needs to add "name"/"id" attribs to various html-elements.
Add name-attribs to theme/_init_.py / common.js
Problem: A lot of "id" were removed from the theme/_init_.py because and id must occur only once in a page otherwise this will trigger an invalid html-error by validation services. Therefore "id" was changed into "css-class". For our accesskey-support we need the "name" attrib (not css class) to get it work. Therefore we have to add it at the appropriate place (for gui-edit-link also in common.js). E.g. it would be nice to add for each element of the page trail a name with a number (name=trail01...). So blind users can easily set accesskeys on this an navigate within the wiki (see further down for a suggestion where to add name-elements).
Disable/Remove accesskey
The new possibility in Moin to set accesskeys with a link definition (since Moin 1.6.1 with [[target|label|accesskey=1]] would interfere with our new approach. It should thus be removed/disabled IMHO.
Add help pages
We need a help page summarizing important id/name and elment-names to set accesskeys e.g. like this:
name#page_content : for the beginning of the page content (including pagetitle and potential message boxes) id#top : for the top of the page id#bottom : for the bottom of the page name#edit : for the edit and actions menu (name#texteditlink, name#guieditlink, name#info, name#attachments, name#action for the "More actions" box) name#nav : for the navigation menu (name#PageName for the pages there, name#navbar_current_page for the current page to quickly return to the page after e.g. viewing a diff) name#user : for the users menu (login/logoff, Homepage, user preferences) name#trail : for the page trail (and name#trail00, name#trail01... for the pages in the trail) id#searchinput : for the searchinput field name#save_button : for the edit save name#preview_button : for the page preview name#button_spellcheck : for the spell check name#button_cancel : for cancel id#editor-textarea : for the large input field to quickly set focus back on the edit box and return to editing
We need also a list on the help page telling the user how to activate accesskeys in his browser:
Alt + accesskey (followed by Enter to execute a link) Internet Explorer for Windows Alt + accesskey Firefox for Windows <=1.5 Mozilla for Windows Netscape 6+ for Windows Alt + Shift + accesskey Firefox for Windows 2 Shift + Esc + accesskey Opera for Windows or Mac Ctrl + accesskey Internet Explorer 5.2 for Mac Safari 1.2 (Mac only) Firefox for Mac (including 2!) Mozilla for Mac Netscape 6+ for Mac Not supported Camino (Mac only) IE<4, Netscape<6
Maybe it is also a good idea to provide a sample file/template for "shortcuts.js" to ease customization for new users.
That's it! By using a html-function to augment a wikipage with accesskeys you can do lot and have a quite flexible approach. Even macro/parser developper (Third-Party) can provide id/names for certain actions, tell them to the user and the user can set accesskeys on this (e.g. for slideshow, Arnica/Gallery2)! This kind of accesskey support is already fully implemented in http://www.moinmo.in/ThemeMarket/SimpleMente. You can try it there. However this theme is hard to maintain, since a lot of functions of ThemeBase were overridden (e.g. simply by changing css-class in name). Therefore it would be better to move that in ThemeBase. I would appreciate it very much, if Moin core developpment could discuss this approach for feasibility and - provided it is ok - to implement it in some way in the standard distribution. This would be a great accessibility feature and would set Moin apart from other wiki engines.
Related Pages
MoinMoinExtensions/HotKeys ThemeMarket/SimpleMente
Discussion
This is no solution IMHO, it just creates a even bigger problem: every moin wiki will have other shortkeys (even if they use same default language). The wiki admin won't know conflicts with user's browser shortcuts, he won't even know which browser his users use beforehand. The user won't know which keys the admin configured. Even if the admin documents it, the user won't be able to remember it, because every wiki uses different one, even every moin wiki when done like this. -- ThomasWaldmann 2008-06-14 22:21:55 See also: http://en.wikipedia.org/wiki/Access_keys
- It is no problem dropping the ability that the wikiadim is able to define shortcut keys (however I wouldn't recommend that). It is just a mere possibility of the above approach, not to hardcode accesskeys in the code but to augment them at runtime by javascript. Instead on focussing only on the wikiadmin topic, it should be rather discussed what to think of the whole above proposal.
- As you are concerned about accessability: how many vision impaired users use browsers with Javascript capabilities? Currently most of moin runs quite ok without javascript, so I am not sure I want javascript for configurability of a feature that maybe should not be / needs not be configurable, but just once done in a good way.
- Most vision impaired users use IE, often still IE6
- As you are concerned about accessability: how many vision impaired users use browsers with Javascript capabilities? Currently most of moin runs quite ok without javascript, so I am not sure I want javascript for configurability of a feature that maybe should not be / needs not be configurable, but just once done in a good way.
- I know the UK government accesskeys standard, however it is not widely used. It is not a real standard. If you consider it as standard: the wikiadmin could easily set it as standard in the wikiconfig.py - or turn off accesskeys completely if he dislikes them for some reason.
- I didn't suggest using UK government standard (and I don't think that standard is good aside from being some sort of standard).
- We won't find a solution, which accesskeys to hardcode in Moin code, so that everybody is happy. Not being able to agree on which accesskeys to use is the reason, why Moin still hasn't accesskeys. But instead of deciding now which accesskeys to use, why not leave it open and provide a basic mechanism for it? Why not make it more "soft" and give some recommondations on standards for the wiki admin in setting up the accesskeys?
- I didn't suggest using UK government standard (and I don't think that standard is good aside from being some sort of standard).
- "The user won't know which keys the admin configured. Even if the admin documents it, the user won't be able to remember it, because every wiki uses different one, even every moin wiki when done like this.". This is wrong. The above solution always displays as a cheat-sheat in the page's footer the availalbe accesskeys.
- OK, so the user can know it. It still would be a pain if every admin would define something else and the user would have to adjust to that. Your car's steering wheel and position of the break pedal is also not configurable, it works the same way in every car.
- That's wrong. In Britain the steering wheel is on the right side, in France on the left.
- OK, so the user can know it. It still would be a pain if every admin would define something else and the user would have to adjust to that. Your car's steering wheel and position of the break pedal is also not configurable, it works the same way in every car.
- "...the user won't be able to remember it". That's the reason why the main aim of the above solution proposes that every user can define accesskeys like he wants. Simply by uploading a shortcut definition file on his (interwiki) homepage. These setting could also be stored - as Johannes Berg suggested - at the open id provider. So every Moin wiki would have the same accesskeys.
- How many users do you expect to configure acesskeys for every moin wiki they use? Using openid is a nice idea, but I don't think usability should depend on that or should only be easy if there is openid. So if you don't have some configuration exchange, you would have to configure every moin wiki you use (and have an account on every moin wiki you use). What's with accesskeys when you are not logged in? None? Different?
If you are not logged in, Moin tries to retrieve the accesskeys the wikiadmin has set in the wikiconfig.py. If there are none, there are no accesskeys available. With the above approach I have just tried to bring some discussions form some accessibility experts to Moin, see http://www.accessify.com/preferences/accesskeys/ and http://golem.ph.utexas.edu/~distler/blog/archives/000723.html. I wanted to create a feature which sets Moin apart from other engines. A nice thing of the above approach is also: every third party macro/parser can now have accesskeys (simply by providing a name-attrib to a link).
- How many users do you expect to configure acesskeys for every moin wiki they use? Using openid is a nice idea, but I don't think usability should depend on that or should only be easy if there is openid. So if you don't have some configuration exchange, you would have to configure every moin wiki you use (and have an account on every moin wiki you use). What's with accesskeys when you are not logged in? None? Different?
- It is no problem dropping the ability that the wikiadim is able to define shortcut keys (however I wouldn't recommend that). It is just a mere possibility of the above approach, not to hardcode accesskeys in the code but to augment them at runtime by javascript. Instead on focussing only on the wikiadmin topic, it should be rather discussed what to think of the whole above proposal.