All posts by Alexander Bautz

Move site actions to the left

21.02.2010: Updated the post with the code for floating the site action menu to the right on the screen (as requested by Larry).

This one proved a bit tricky because the site action menu is part of the master page.

See codeblock below for updated code. Please let me know if you find any bugs.


After i posted the solution to Move view selector to the left, i got a request from Charlie Epes for the same solution regarding the “Site Actions-menu”.

This is the default placement of the Site Actions-menu:
IMG

Here the menu is inserted after the last toplink (dynamic):
IMG


Here is how it’s done
Add a CEWP below the list view, and add this code (alter the location of the jQuery-scipt as needed)

<script type="text/javascript" src="../../Javascript/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
$("table.ms-bannerframe table:first td:last").after($("table.ms-bannerframe td:last"));
</script>

Code for floating the menu to the right (replace the code supplied above with this one):

<script type="text/javascript" src="../../Javascript/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
// Site action float right
$(document).ready(function(){
	setTimeout(function(){
		siteActionFloatRight();
	},500);
});

function siteActionFloatRight(){
	var menuWidth = $("table.ms-siteaction").width();
	var doc = $(document).width();
	var win = $(window).width();
	var scr = $(window).scrollLeft();

	left = doc-win-scr+menuWidth;
	if(left<120){
		left=100;
	}
	// Move the site action menu to the new position
	$("table.ms-siteaction").css({'position':'absolute','top':0,'left':-left});	
}

// Handle resize and scroll
$(window).resize(function() {
	siteActionFloatRight();
}).scroll(function() {
	siteActionFloatRight();
});

// Make it adapt to changing document width due to expanding groups
$("td[class^='ms-gb']").click(function(){
	setTimeout(function(){
		siteActionFloatRight();
	},250);
});
</script>

Regards
Alexander

Move view selector to the left

06.02.2010 Added another method for floating the view selector on the right side of the page.


I got a request from “tecrms” that sounded like this:

As you know on all list views and document views, the view selector is on the right hand side of the menu bar. This can make it quite cumbersome for users looking at lists with many columns to change the view. A better option would be for the view selector to be on the left hand side and right hand side of the menu bar. I know I can move the view selector via SPD but would rather use a JavaScript options if one was available. Would this be something you would be interested in creating?

It’s often harder to think out the question than to actually solve the issue…

This is the default placement of the view-selector:
IMG

Here the selector is inserted after the “Settings-menu”:
IMG


Here is how it’s done
Add a CEWP below the list view, and add this code (alter the location of the jQuery-scipt as needed)

<script type="text/javascript" src="../../Javascript/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
$("td.ms-toolbar:last").insertBefore($("td.ms-toolbar[width='99%']"));
</script>

Use this code for a floating menu on the right side of the page:

<script type="text/javascript" src="../../Javascript/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
	viewMenuFloatRight();
});

function viewMenuFloatRight(){
	var top = $("td.ms-toolbar:last").offset().top+2;
	var menuWidth = $("td.ms-toolbar:last table").width()+15;
	var left = $(window).width() - menuWidth + $(window).scrollLeft();	
	// Position the menu
	$("td.ms-toolbar:last table").css({'position':'absolute','top':top,'left':left});
	// Paging
	$("td.ms-toolbar td[id='topPagingCellWPQ1'] table").css({'position':'absolute','top':top,'left':left-50});
}

// Handle resize and scroll
$(window).resize(function() {
	viewMenuFloatRight();
}).scroll(function() {
	viewMenuFloatRight();
});
</script>

Regards
Alexander

Requests


Post requests and questions in this post, but be patient awaiting reply or solution examples. This is a “one man blog” and i have limited time to answer questions. There will be less activity during the summer months, but i will keep posting new solutions, only on a slightly smaller scale.


I’m open to requests!
I get many ideas on things to write about from my readers, feel free to make requests on things you would like to learn more about.

I will, from the best of my knowledge (and time), answer your questions.

Post requests as comments to this post.

Updated!

Use the rating on the request to vote them up, thus “forcing” me to answer them…

The comments are split into pages with 25 top level comments per page, and the last page displayed by default. Use the links at the bottom of the page to read older comments.

Add HTML mouseover tooltip

28.12.2009: Updated the code in line 38 and 41 and added “stop(true,true)” to prevent animation from looping when mouse is rapidly hovered in and out.


In a previous post i described how to add a custom tool-tip on mouse-over a SharePoint field. This was using the “title-property” of a DOM element and therefore limited to plain text only.

Here is a method for adding HTML tool-tip

It uses a standard “Custom list” as a repository for the mouse-over tool-tip’s. This list can be used for multiple lists.

The tool-tip can look like this (but is unlimited as it uses HTML):
IMG

IMG

IMG

As always we start like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a sub site named “test” with a sub site named “English” with a document library named “Javascript”):
IMG

The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.3.2.min. If you download another version, be sure to update the script reference in the sourcecode.

The scripts “interaction.js” and “stringBuffer.js” is created by Erucy and published on CodePlex.

The sourcecode for “HTML_style_ToolTip.js” is provided below.

Create the repository like this:
IMG

IMG

IMG

Add a CEWP below your NewForm and EditForm list-form (and DispForm if you like) like this:
IMG

With this code:

<script type="text/javascript" src="/test/English/Javascript/jquery-1.3.2.min.js"></script>
<script type="text/javascript" src="/test/English/Javascript/interaction.js"></script>
<script type="text/javascript" src="/test/English/Javascript/stringBuffer.js"></script>
<script type="text/javascript" src="/test/English/Javascript/HTML_style_ToolTip.js"></script>
<script type="text/javascript">
	toolTip('MyTestList','MouseOverTooltipResources');
</script>

Sourcecode for the file “HTML_style_ToolTip.js”:

/* HTML style ToolTip
 * ---------------------------------------------
 * Created by Alexander Bautz
 * alexander.bautz@gmail.com
 * https://spjsblog.com
 * v1.1
 * LastMod: 28.12.2009
 * ---------------------------------------------
 * Include reference to:
 *  jquery // jquery.com
 *  interaction.js // http://spjslib.codeplex.com/
 *  stringBuffer.js // http://spjslib.codeplex.com/
 * ---------------------------------------------
 * Call from a CEWP below the list form in NewForm, DispForm or EditForm like this:
 * toolTip(ListnameOrToken,ResourcesListName)
 *
 * Parameters:
 *  ListnameOrToken = Identifier for the list - to distinguish between lists with similar FieldInternalName
 *  ResourcesListName = ListName or Guid of the tool-tip repository 
*/

fields = init_fields();

function toolTip(ListnameOrToken,ResourcesListName){
tooltip = init_tooltip(ListnameOrToken,ResourcesListName);	
	$.each(tooltip,function(idx,item){
		var split = item.split('|');
		var fieldName = split[0];
		var displayText = split[1];
		var toolTip = split[2];
			if(fields[fieldName]!=undefined){
				$(fields[fieldName]).find('td.ms-formbody').prepend("<div class='customMouseOverTooltip' style='float:right;cursor:hand'> " + 
				displayText + " <div style='padding:3px;display:none;'>" + toolTip + "</div></div>");
			}
	});
	
	$(".customMouseOverTooltip").hover(function(){	
		$(this).find('div:first').css({'position':'absolute','background-color':'f8f8ff','border':'1px silver solid'}).stop(true,true).fadeIn(350)
	},
	function(){
		$(this).find('div:first').stop(true,true).fadeOut(150);
	});
}

function init_tooltip(ConsumerListName,ProviderListName){
wsBaseUrl = L_Menu_BaseUrl + '/_vti_bin/';
var query = "<Where><Eq><FieldRef Name='Identifier' /><Value Type='Text'>" + ConsumerListName + "</Value></Eq></Where>";
var res = queryItems(ProviderListName,query,['Title','DisplayText','ToolTip']); 

tooltip = [];
	if(res.count==-1){
		alert("An error occured in the query:n" + query + "nnContact an administrator");
	}else if(res.count>0){
		$.each(res.items,function(idx,item){
			if(item['ToolTip']!=null){
				var title = item['Title'];
				var val = item['ToolTip'];
				var DisplayText = item['DisplayText'];						
			tooltip.push(title + "|" + DisplayText + "|" + val);
			}
		});
	}
	return tooltip;
}

function init_fields(){
var res = {};
$("td.ms-formbody").each(function(){
if($(this).html().indexOf('FieldInternalName="')<0) return; 
var start = $(this).html().indexOf('FieldInternalName="')+19;
var stopp = $(this).html().indexOf('FieldType="')-7; 
var nm = $(this).html().substring(start,stopp);
res[nm] = this.parentNode;
});
return res;
}

Save this as “HTML_style_ToolTip.js” and upload to your scriptlibrary as shown above.

Enjoy!

Regards
Alexander

Send E-mail with JavaScript

Note:
I’m far away from my safe “pure JavaScript” world here – if someone have any comments on this approach i will gladly listen!


In this article I will give an example of how to send e-mail (with CC, Bcc and HTML body) from SharePoint with JavaScript and the help of some server-side files (two variants of the aspx-page and a code-behind .cs file placed in the Layouts directory of your 12′ hive). This approach requires server access for placing the files.

I will first show you how to create a simple “contact-form”, secondly i will show you how to send the contents of a DispForm as a HTML-bodied email (in a follow-up post).

The method used passes the variables collected with javascript to the server-side file “SendMailBySessvars.aspx” or “SendMailNewWindow.aspx”. This page uses javascript to populate some hidden controls. The hidden controls is used to pass the variables to the server-side code as javascript cannot pass it’s variables directly. When the controls are populated, the server-side code is called to do the actual sending of the e-mail.

I will provide two options for sending the email:

  • The current page redirects to the “SendMailBySessvars.aspx” – sends the email and redirects back.
  • A new window is opened – the email is sent – and the window is closed.

The server-side files

In your SharePoint server – add these files to your 12’hive (C:Program FilesCommon FilesMicrosoft Sharedweb server extensions12TEMPLATELAYOUTSSendMailWithJavascript)
IMG

The sourcecode for the files “SendMailBySessvars.aspx”, “SendMailNewWindow.aspx” and “SendMail.aspx.cs” is provided below. The file “sessvars.js” is found here.

SourceCode for “SendMailBySessvars.aspx” – used for sending mail in the current window

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="SendMail.aspx.cs" Inherits="Enquiry" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<style>
.mailStatusMsg
{
	color:#666666;
	font-size:large;
	border:5px gray double;
	text-align:center;
}

</style>
<title>Send Mail</title>
</head>
<body>

<form id="formSendMail" runat="server">
	<div style="display:none">
		<asp:HiddenField ID="txtSMTP" runat="server"></asp:HiddenField>	
		<asp:HiddenField ID="txtSMTP_Port" runat="server"></asp:HiddenField>		
		<asp:HiddenField ID="txtEmailSubject" runat="server"></asp:HiddenField>	
		<asp:HiddenField ID="txtHtmlBody" runat="server"></asp:HiddenField>		
		<asp:HiddenField ID="txtFromEmail" runat="server"></asp:HiddenField>	
		<asp:HiddenField ID="txtToEmail" runat="server"></asp:HiddenField>	
		<asp:HiddenField ID="txtCCEmail" runat="server"></asp:HiddenField>	
		<asp:HiddenField ID="txtBccEmail" runat="server"></asp:HiddenField>
		<asp:HiddenField ID="txtNewWindow" runat="server"></asp:HiddenField>	
		<asp:HiddenField ID="txtShowSplash" runat="server"></asp:HiddenField>
		<asp:HiddenField ID="txtRedirUrl" runat="server"></asp:HiddenField>	
		<asp:HiddenField ID="txtTimer" runat="server"></asp:HiddenField>
		<asp:HiddenField ID="txtSuccessMsg" runat="server"></asp:HiddenField>
		<asp:HiddenField ID="txtErrorMsg" runat="server"></asp:HiddenField>		
		<asp:Button ID="btnSubmit" runat="server" Text="Submit" OnClick="btnSubmit_Click"/>
	</div>
</form>

<script type="text/javascript" src="sessvars.js"></script>
<script type="text/javascript">

if(sessvars.emailBySessvars!=undefined){
	if("<%=SendFunctionTriggered%>" != 1){
		document.getElementById('txtSMTP').value=sessvars.emailBySessvars.SMTP;
		document.getElementById('txtSMTP_Port').value=sessvars.emailBySessvars.SMTP_Port;
		document.getElementById('txtEmailSubject').value=sessvars.emailBySessvars.EmailSubject;
		document.getElementById('txtHtmlBody').value=sessvars.emailBySessvars.htmlBody;
		document.getElementById('txtFromEmail').value=sessvars.emailBySessvars.fromEmail;
		document.getElementById('txtToEmail').value=sessvars.emailBySessvars.toEmail;
		document.getElementById('txtCCEmail').value=sessvars.emailBySessvars.ccEmail;
		document.getElementById('txtBccEmail').value=sessvars.emailBySessvars.BccEmail;	
		document.getElementById('txtNewWindow').value="0";
		document.getElementById('txtShowSplash').value=sessvars.emailBySessvars.ShowSplash;
		document.getElementById('txtRedirUrl').value=sessvars.emailBySessvars.RedirUrl;
		document.getElementById('txtTimer').value=sessvars.emailBySessvars.RedirTimer;
		document.getElementById('txtSuccessMsg').value=sessvars.emailBySessvars.SuccessMsg;
		document.getElementById('txtErrorMsg').value=sessvars.emailBySessvars.ErrorMsg;
	
		// Clear sessvars 
		sessvars.$.clearMem();
		//if(confirm("Sessvars - send mail?")){
			document.getElementById('btnSubmit').click();
		//}
	}
}
</script>    
</body>
</html>

SourceCode for “SendMailNewWindow.aspx” – used for sending mail in new window

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="SendMail.aspx.cs" Inherits="Enquiry" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<style>
.mailStatusMsg
{
	color:#666666;
	font-size:large;
	border:5px gray double;
	text-align:center;
}

</style>
<title>Send Mail</title>
</head>
<body>

<form id="formSendMail" runat="server">
	<div style="display:none">
		<asp:HiddenField ID="txtSMTP" runat="server"></asp:HiddenField>	
		<asp:HiddenField ID="txtSMTP_Port" runat="server"></asp:HiddenField>		
		<asp:HiddenField ID="txtEmailSubject" runat="server"></asp:HiddenField>	
		<asp:HiddenField ID="txtHtmlBody" runat="server"></asp:HiddenField>		
		<asp:HiddenField ID="txtFromEmail" runat="server"></asp:HiddenField>	
		<asp:HiddenField ID="txtToEmail" runat="server"></asp:HiddenField>	
		<asp:HiddenField ID="txtCCEmail" runat="server"></asp:HiddenField>	
		<asp:HiddenField ID="txtBccEmail" runat="server"></asp:HiddenField>
		<asp:HiddenField ID="txtNewWindow" runat="server"></asp:HiddenField>	
		<asp:HiddenField ID="txtShowSplash" runat="server"></asp:HiddenField>
		<asp:HiddenField ID="txtRedirUrl" runat="server"></asp:HiddenField>	
		<asp:HiddenField ID="txtTimer" runat="server"></asp:HiddenField>
		<asp:HiddenField ID="txtSuccessMsg" runat="server"></asp:HiddenField>
		<asp:HiddenField ID="txtErrorMsg" runat="server"></asp:HiddenField>		
		<asp:Button ID="btnSubmit" runat="server" Text="Submit" OnClick="btnSubmit_Click"/>
	</div>
</form>

<script type="text/javascript" src="sessvars.js"></script>
<script type="text/javascript">


if(window.opener!=undefined){
	if(window.opener.openInNewWindowAndEmail!=undefined){
		if("<%=SendFunctionTriggered%>" != 1){
			document.getElementById('txtSMTP').value=window.opener.openInNewWindowAndEmail.SMTP;
			document.getElementById('txtSMTP_Port').value=window.opener.openInNewWindowAndEmail.SMTP_Port;
			document.getElementById('txtEmailSubject').value=window.opener.openInNewWindowAndEmail.EmailSubject;
			document.getElementById('txtHtmlBody').value=window.opener.openInNewWindowAndEmail.htmlBody;
			document.getElementById('txtFromEmail').value=window.opener.openInNewWindowAndEmail.fromEmail;
			document.getElementById('txtToEmail').value=window.opener.openInNewWindowAndEmail.toEmail;
			document.getElementById('txtCCEmail').value=window.opener.openInNewWindowAndEmail.ccEmail;
			document.getElementById('txtBccEmail').value=window.opener.openInNewWindowAndEmail.BccEmail;	
			document.getElementById('txtNewWindow').value="1";
			document.getElementById('txtShowSplash').value=window.opener.openInNewWindowAndEmail.ShowSplash;
			document.getElementById('txtTimer').value=window.opener.openInNewWindowAndEmail.CloseWindowTimer;
			document.getElementById('txtSuccessMsg').value=window.opener.openInNewWindowAndEmail.SuccessMsg;
			document.getElementById('txtErrorMsg').value=window.opener.openInNewWindowAndEmail.ErrorMsg;
		
			//if(confirm("Window.opener - send mail?")){
				document.getElementById('btnSubmit').click();
			//}
		}else if("<%=SendFunctionTriggered%>" == 1){
			if(window.opener.openInNewWindowAndEmail.ShowSplash=="1"){
				var showSplashTime = "<%=redirTimer%>" * 1000; // Milliseconds
				setTimeout("self.close()", showSplashTime);
			}else if(window.opener.openInNewWindowAndEmail.ShowSplash=="0"){
				self.close();
			}
		}
	}
}
</script>    
</body>
</html>

SourceCode for SendMail.aspx.cs

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Net.Mail;

public partial class Enquiry : System.Web.UI.Page
{
public string SendFunctionTriggered = string.Empty;
public string UrlStatusFlagPrefix = string.Empty;
public string EmailSendStatus = string.Empty;
public int redirTimer = 0;

protected void Page_Load(object sender, EventArgs e)
{
// Do nothing
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
SendFunctionTriggered = "1";
SendMail(txtToEmail.Value, txtFromEmail.Value, txtCCEmail.Value, txtBccEmail.Value, txtEmailSubject.Value, txtHtmlBody.Value);
if (txtRedirUrl.Value.IndexOf("?") > 0)
{
UrlStatusFlagPrefix = "&";
}
else
{
UrlStatusFlagPrefix = "?";
}

if (msg == "Successful")
{
EmailSendStatus = UrlStatusFlagPrefix + "Email=Success";
}
else
{
EmailSendStatus = UrlStatusFlagPrefix + "Email=Failure";
}

if (txtNewWindow.Value == "1") // Open new window to handle send mail operation
{
if (txtShowSplash.Value == "1")
{
statusMsgDisplay();
}
}
else // Handle send mail operation in current window
{
if (txtShowSplash.Value == "1")
{
// Show status msg
statusMsgDisplay();
}
else
{
Response.Redirect(txtRedirUrl.Value + EmailSendStatus);
}
}
}

public void statusMsgDisplay()
{
redirTimer = Convert.ToInt32(txtTimer.Value);
string newString = string.Empty;
if (msg == "Successful")
{
newString = txtSuccessMsg.Value.Replace("{0}", Convert.ToString(redirTimer));
Response.Write(newString);
if (txtNewWindow.Value != "1")
{
Response.Write("<Meta http-equiv=’REFRESH’ content=’" + redirTimer + ";URL=" + txtRedirUrl.Value + EmailSendStatus + "’/>");
}
}
else
{
redirTimer = redirTimer + 3;
newString = txtErrorMsg.Value.Replace("{0}", Convert.ToString(redirTimer));
newString = newString.Replace("{1}",msg);
Response.Write(newString);
if (txtNewWindow.Value != "1")
{
Response.Write("<Meta http-equiv=’REFRESH’ content=’" + redirTimer + ";URL=" + txtRedirUrl.Value + EmailSendStatus + "’/>");
}
}
}

public string msg = string.Empty;
public string SendMail(string toList, string from, string ccList, string bccList, string subject, string body)
{
MailMessage message = new MailMessage();
SmtpClient smtpClient = new SmtpClient();

try
{
MailAddress fromAddress = new MailAddress(from);
message.From = fromAddress;
message.To.Add(toList);
if(ccList != null && ccList != string.Empty)
message.CC.Add(ccList);
if(bccList != null && bccList != string.Empty)
message.Bcc.Add(bccList);
message.Subject = subject;
message.IsBodyHtml = true;
message.Body = body;

smtpClient.Host = txtSMTP.Value;
smtpClient.Port = Convert.ToInt32(txtSMTP_Port.Value);
smtpClient.UseDefaultCredentials = true;
smtpClient.Send(message);
msg = "Successful";
}
catch (Exception ex)
{
msg = ex.Message;
}
return msg;
}
}
[/javascript]


Simple Contact form

IMG

As always we start like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a sub site named “test” with a sub site named “English” with a document library named “Javascript”):
IMG

The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.3.2.min. If you download another version, be sure to update the script reference in the sourcecode.

The file “sessvars.js” is found here.

Sourcecode for the contact form:

&lt;button id=&quot;showContactForm&quot; onclick=&quot;javascript:toggleContactForm();&quot;&gt;Contact me&lt;/button&gt;
&lt;div id=&quot;contactForm&quot; style=&quot;display:none&quot;&gt;
&lt;table style=&quot;width: 500px&quot; cellpadding=&quot;4&quot; cellspacing=&quot;0&quot;&gt;
&lt;tr&gt;
&lt;td valign=&quot;top&quot; style=&quot;width:100px&quot;&gt;Your name&lt;span class=&quot;ms-formvalidation&quot;&gt; *&lt;/span&gt;&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;&lt;input id=&quot;customInputName&quot; type=&quot;text&quot; style=&quot;width: 300px&quot; /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td valign=&quot;top&quot; style=&quot;width:100px&quot;&gt;Your email&lt;span class=&quot;ms-formvalidation&quot;&gt; *&lt;/span&gt;&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;&lt;input id=&quot;customInputEmail&quot; type=&quot;text&quot; style=&quot;width: 300px&quot; /&gt;&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td valign=&quot;top&quot; style=&quot;width:100px&quot;&gt;Message&lt;span class=&quot;ms-formvalidation&quot;&gt; *&lt;/span&gt;&lt;/td&gt;
&lt;td valign=&quot;top&quot;&gt;&lt;textarea id=&quot;customInputMessage&quot; rows=&quot;10&quot; style=&quot;width: 100%&quot;&gt;&lt;/textarea&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td colspan=&quot;2&quot; align=&quot;right&quot;&gt;
&lt;button onclick=&quot;javascript:emailBySessvars()&quot;&gt;Sessvars Send Email&lt;/button&gt;&amp;nbsp;
&lt;button onclick=&quot;javascript:openInNewAndEmail()&quot;&gt;New Window Send Email&lt;/button&gt;&amp;nbsp;
&lt;button onclick=&quot;javascript:toggleContactForm();&quot;&gt;Cancel&lt;/button&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/div&gt;

&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/jquery-1.3.2.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/sessvars.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
function toggleContactForm(){
$('#contactForm').find(&quot;:input:not(button)&quot;).each(function(){
	$(this).val('');
});
$('#showContactForm').toggle();
$('#contactForm').toggle();
}

function preSendCheck(){
var arrTovalidate = ['customInputName','customInputEmail','customInputMessage'];
$(&quot;div.ms-formvalidation&quot;).remove();
var preCheckOK = true;
$.each(arrTovalidate,function(){
	var field = $(&quot;#&quot; + this);
	if(field.val()==''){
		preCheckOK = false;
		field.parent().append(&quot;&lt;div class='ms-formvalidation'&gt;You must specify a value for this required field.&lt;/div&gt;&quot;)
	}
});
return preCheckOK ;
}

function emailBySessvars(){ // Current window redirect
var sendOK = preSendCheck();
if(sendOK ){
	var name = $(&quot;#customInputName&quot;).val();
	var email = $(&quot;#customInputEmail&quot;).val();
	var message = $(&quot;#customInputMessage&quot;).val();
		sessvars.emailBySessvars = {
		&quot;SMTP&quot;:&quot;insert your SMTP here&quot;,
		&quot;SMTP_Port&quot;:&quot;25&quot;,
		&quot;fromEmail&quot;:email,
		&quot;toEmail&quot;:&quot;insert the address to send the mail to here&quot;,
		&quot;ccEmail&quot;:&quot;&quot;,
		&quot;BccEmail&quot;:&quot;&quot;,
		&quot;EmailSubject&quot;:&quot;Contact form by Sessvars&quot;,
		&quot;htmlBody&quot;:&quot;Name: &lt;br&gt;&quot; + name + &quot;&lt;br&gt;Message:&lt;br&gt;&quot; + message,
		&quot;ShowSplash&quot;:&quot;1&quot;,
		&quot;RedirUrl&quot;:&quot;/test/English/Javascript/SendMailWithJavascript.aspx&quot;,
		&quot;RedirTimer&quot;:&quot;1&quot;,
		&quot;SuccessMsg&quot;:&quot;&lt;div class='mailStatusMsg'&gt;E-mail successfully sendt&lt;div style='font-size:small'&gt;Redirecting in {0} seconds&lt;/div&gt;&lt;/div&gt;&quot;,
		&quot;ErrorMsg&quot;:&quot;&lt;div class='mailStatusMsg'&gt;Error sending message&lt;div style='font-size:small'&gt;{1}&lt;br&gt;Redirecting in {0} seconds&lt;/div&gt;&lt;/div&gt;&quot;
		};
			window.location.href='/_layouts/SendMailWithJavascript/SendMailBySessvars.aspx';
	}
}

function openInNewAndEmail(){ // New window
var sendOK = preSendCheck();
if(sendOK){
	var name = $(&quot;#customInputName&quot;).val();
	var email = $(&quot;#customInputEmail&quot;).val();
	var message = $(&quot;#customInputMessage&quot;).val();
		openInNewWindowAndEmail = {
		&quot;SMTP&quot;:&quot;insert your SMTP here&quot;,
		&quot;SMTP_Port&quot;:&quot;25&quot;,
		&quot;fromEmail&quot;:email,
		&quot;toEmail&quot;:&quot;insert the address to send the mail to here&quot;,
		&quot;ccEmail&quot;:&quot;&quot;,
		&quot;BccEmail&quot;:&quot;&quot;,
		&quot;EmailSubject&quot;:&quot;Contact form by Window.opener&quot;,
		&quot;htmlBody&quot;:&quot;Name: &lt;br&gt;&quot; + name + &quot;&lt;br&gt;Message:&lt;br&gt;&quot; + message,
		&quot;ShowSplash&quot;:&quot;1&quot;,
		&quot;CloseWindowTimer&quot;:&quot;1&quot;,
		&quot;SuccessMsg&quot;:&quot;&lt;div class='mailStatusMsg'&gt;E-mail successfully sendt&lt;div style='font-size:small'&gt;Closing this window in {0} seconds&lt;/div&gt;&lt;/div&gt;&quot;,
		&quot;ErrorMsg&quot;:&quot;&lt;div class='mailStatusMsg'&gt;Error sending message&lt;div style='font-size:small'&gt;{1}&lt;br&gt;Closing in {0} seconds&lt;/div&gt;&lt;/div&gt;&quot;
		};
		newwindow=window.open('/_layouts/SendMailWithJavascript/SendMailNewWindow.aspx',
		'SendMail','location=no,menubar=no,resizable=no,scrollbars=no,titlebar=no,toolbar=no,width=300,height=125');
		toggleContactForm();
	}
}
&lt;/script&gt;

In the code for the contact form you have to insert your SMTP-server and the “toEmail”. This example form provides buttons for both methods of sending mail.

When sending the email you are presented with a “splash-screen” like this when using “sessvars.js” and redirect in the same page:
IMG

When using the sessvars.js redirect, you also get a “receipt” in the URL:
IMG

And like this when opening in a new window:
IMG

These “receipts” are based on the “SuccessMsg” and “ErrorMsg” specified in the contact form. The “placeholder” {0} and {1} in the message text are replaced with the time to close/redirect, and for “ErrorMsg” also the actual error from the “sendmail-code”.
IMG
In case of an error – the splash screen is displayed for 3 seconds longer than specified.

I will follow up this one with an example on how to send the contents of a DispForm as a HTML-bodied email, but all that is to it is to loose the “contact-form-message-field” and define the “htmlBody” in the “contact-form-code” to hold the HTML-content you want to send.

I am open for questions – and as i noted above: this one is outside my comfort zone and i appreciate feedback on the selected method and tips on how to make it better.

Regards
Alexander

Arrange multiple selection lookup vertically

08.12.2009 Small update to fix scroll vertically.

Here’s a tip on how to rearrange the multiple select lookup column in a vertical orientation.

This is the default orientation:
IMG

This is the new vertical orientation:
IMG

Here’s how its done


As always we start like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a sub site named “test” with a sub site named “English” with a document library named “Javascript”):
IMG

The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.3.2.min. If you download another version, be sure to update the script reference in the sourcecode.

Add a CEWP below your list-form like this:IMG

And add this code:

<script type="text/javascript" src="/test/English/Javascript/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
fields = init_fields();
// Call script
mulitiLookupVertical('MultiLookup');

function mulitiLookupVertical(FieldInternalName,width){
if(width==undefined)width=370;
var thisField = $(fields[FieldInternalName]);
	thisField.find("td.ms-formbody td").each(function(){
		// Wrap in tr's and set width
		$(this).wrap("<tr></tr>")
			.find('div')
			.css({'width':width});

	});
	// Buttons
	thisField.find('br').replaceWith(' ');
	thisField.find('button:first').text('Add');
	thisField.find('button:last').text('Remove');	
}

function init_fields(){
  var res = {};
  $("td.ms-formbody").each(function(){
	  if($(this).html().indexOf('FieldInternalName="')<0) return;	
	  var start = $(this).html().indexOf('FieldInternalName="')+19;
	  var stopp = $(this).html().indexOf('FieldType="')-7; 
	  var nm = $(this).html().substring(start,stopp);
	  res[nm] = this.parentNode;
  });
  return res;
}
</script>

Ragards
Alexander

Edit the title property of a field to add custom tooltip

29.10.2009 Update: I have written a new article on how to add HTML-style tool tip – read it here.


I got a request for a method of adding a custom mouse-over tool tip on a field in a SharePoint form. Here is a quick example.

As always we start like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a sub site named “test” with a sub site named “English” with a document library named “Javascript”):
IMG

The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.3.2.min. If you download another version, be sure to update the script reference in the sourcecode.

Add a CEWP below your list-form like this:IMG

And add this code:

&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/jquery-1.3.2.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
fields = init_fields();

$(fields['Title']).find('input').attr('title','This is a new mouse-over tooltip');

function init_fields(){
  var res = {};
  $(&quot;td.ms-formbody&quot;).each(function(){
	  if($(this).html().indexOf('FieldInternalName=&quot;')&lt;0) return;	
	  var start = $(this).html().indexOf('FieldInternalName=&quot;')+19;
	  var stopp = $(this).html().indexOf('FieldType=&quot;')-7; 
	  var nm = $(this).html().substring(start,stopp);
	  res[nm] = this.parentNode;
  });
  return res;
}
&lt;/script&gt;

The tooltip should look like this:
IMG

Ask if this example is not enough.

Alexander

Master document template library

Have you ever wanted to:

  • have multiple document templates in i one document library
    – without having to use content types?
  • use the same template in multiple document library’s?
  • have different document templates available in each view?
  • have easy access to editing the templates?

I will provide a javascript solution that uses a standard document library as a master template placeholder. This library can have an unlimited number of templates and each template can be used in multiple document libraries. On each template you can specify which document library to use it in, and whether to use the template in all views, or to specify which views to use it in.

Then, in the document library where you want to use these templates, just drop a CEWP with some script references and like magic – your templates appear. No need to hand code each menu item – they are pulled straight from your master template library based on your metadata on each template.

As always we start like this:
Create a document library to hold your scripts (or a folder on the root created in SharePoint Designer). In this example i have made a document library with a relative URL of “/test/English/Javascript” (a sub site named “test” with a sub site named “English” with a document library named “Javascript”):
IMG

The jQuery-library is found here. The pictures and the sourcecode refers to jquery-1.3.2.min. If you download another version, be sure to update the script reference in the sourcecode.

The scripts “interaction.js” and “stringBuffer.js” is created by Erucy and published on CodePlex.

The sourcecode for “MasterDocumentTemplates.js” is provided below.

Create your master template placeholder library like this:

Create a standard document library and add two columns:
UsedIn of type “Multiple lines of text”

Here the document library’s where the template will be accessible is specified like this:
– DisplayNameOfYourLibrary|viewNameFromURL;
– You can add multiple libraries and multiple views for each library – separated by “|” for each view, and “;” for each library.

MouseOver of type “Single line of text”.

Here the description (in “bigIcon-mode”) or MouseOver (in “smallIcon-mode”) on the template in the dropdown is set.

The columns in the template library looks like this:
IMG

And the template library look like this with a few templates added:
IMG

As you see from the screenshot – all templates is used in the document library named “MyDocumentLibrary”. The file “PowerPoint template” is visible only in the view “PowerPoint” in “MyDocumentLibrary”. The rest of the templates is visible in all views.

Here is the code for the file “MasterDocumentTemplates.js”:

/* Add dropdown menu in document library - pulls templates from master template library and points &quot;save&quot; to the current library
 * Requires a &quot;Templates&quot; document library with the following fields (FieldInternalName):
 * &quot;UsedIn&quot;
 * - Multiple line of plain text. Here the document library's where the template will be accessible is specified like this:
 *   DisplayNameOfYourLibrary|viewNameFromURL;
 *   You can add multiple libraries and multiple views for each library - separated by &quot;|&quot; for each view, and &quot;;&quot; for each library. 
 *   If you do not specify view - the template is accessible to all views that have this script referred.
 * &quot;MouseOver&quot;
 * - Description (in &quot;bigIcon-mode&quot;) or MouseOver (in &quot;smallIcon-mode&quot;) on the template in the dropdown
 *
 * -----------------------------
 * Author: Alexander Bautz
 * Version: 1.2
 * LastMod: 18.10.2009
 * -----------------------------
 * Must include reference to jQuery and to the folloving scripts (http://spjslib.codeplex.com):
 * ----------------------------------------------------
 * interaction.js
 * stringBuffer.js
 * ----------------------------------------------------
Ex:
getTemplatesForThisLibrary('DocumentTemplates','/test/English','',true,true,'Upload document','Select template from list');

Parameters explained:
* templatesListName:
  Document library DisplayName of your master template library
* templatesListbaseUrl:
  The baseUrl of the site the library resides in (not the URL to the Document library it selves)
* targetListUrlDir:
 The actual URL path to the save location - to save in &quot;current&quot; library - where the menu is - use ''
* bigIcon:
 true for large icon and description (from field &quot;MouseOver&quot;) below the template name
 false for small icon mode with the value from the field &quot;MouseOver&quot; as tooltip and only the template name visible
* uploadOption:
 true add option for upload
 false hides option for upload
* uploadButtonTitle:
 if uploadOption set to true - the text displayed on the upload &quot;option&quot;&lt;/li&gt;
* menuTitle:
 The text on the dropdown-menu
*/

function getTemplatesForThisLibrary(templatesListName,templatesListbaseUrl,targetListUrlDir,bigIcon,uploadOption,uploadButtonTitle,menuTitle){
var bgColor = $('.ms-quicklaunchheader').css('background-color');
var btnBgColor = $('.ms-viewselector').css('background-color');
var thisViewNameRaw = $(&quot;#aspnetForm&quot;).attr('action');
var thisViewName = thisViewNameRaw.substring(0,thisViewNameRaw.indexOf('.aspx'));

wsBaseUrl = templatesListbaseUrl + '/_vti_bin/'; 
var q = &quot;&lt;Where&gt;&lt;Contains&gt;&lt;FieldRef Name='UsedIn'/&gt;&lt;Value Type='Text'&gt;&quot; + ctx.ListTitle + &quot;&lt;/Value&gt;&lt;/Contains&gt;&lt;/Where&gt;&quot; +
		&quot;&lt;OrderBy&gt;&lt;FieldRef Name='FileLeafRef' Ascending='True'/&gt;&lt;/OrderBy&gt;&quot;;
var links = '';
if(bigIcon){
	var iconPrefix = 'lg_';
	var menuClass = 'ms-MenuUILarge';
	var uploadIMG = 'menuuploaddocument.gif';
}else{
	var iconPrefix = '';
	var menuClass = 'ms-MenuUI';
	var uploadIMG = 'doclink.gif';
}

var res = queryItems(templatesListName,q,['UsedIn','FileLeafRef','MouseOver','DocIcon'],25);
	if(res.count&gt;0){
		if(targetListUrlDir==''){
			var listURLRaw = ctx.listUrlDir;
			var listUrl = listURLRaw.substring(listURLRaw.lastIndexOf('/'));
		}else{
			listUrl = &quot;/&quot; + targetListUrlDir;
		}
	    $.each(res.items, function(idx, item){ 
	    var showInView = false;
	    // Split the UsedIn value to separate the document library names
	    var useInSplit = item['UsedIn'].split(';');
	        for(i=0;i&lt;useInSplit.length;i++){ 
	        	// Split the value to find the view's in which to show the template
	       		var usedInSplitAgain = useInSplit[i].split('|');	       		
	       		if(usedInSplitAgain.length&gt;1){ // If length &gt; 1 there is specified one or more views
		       		if(usedInSplitAgain[0]==ctx.ListTitle){
				        for(j=1;j&lt;usedInSplitAgain.length;j++){
			        		if(usedInSplitAgain[j].toLowerCase()==thisViewName.toLowerCase()){
				        		// Show in this view
				        		showInView = true;
				        	}
				        }
					}				
				}else{ // View not specified - show in all views
					if(usedInSplitAgain[0]==ctx.ListTitle){
						showInView = true;
					}
				}
	        }
	        
			if(showInView){
		        var DocIcon = item['DocIcon'];
		        var docName = item['FileLeafRef'].substring(item['FileLeafRef'].indexOf(';#')+2);
		        var docDescription = '';
		        var mouseOver = '';		        
		        if(item['MouseOver']!=null){
		        	var docDescription = &quot;&lt;font color='#696969'&gt;&quot; + item['MouseOver'] + &quot;&lt;/font&gt;&quot;;
			        	if(!bigIcon){
			        		var mouseOver = item['MouseOver'];
			        	}
		        }	        
		        var link = &quot;createNewDocumentWithProgID('&quot; + ctx.HttpRoot + &quot;/&quot; + templatesListName + &quot;/&quot; + docName + &quot;','&quot; + ctx.HttpRoot + listUrl + &quot;','SharePoint.OpenDocuments', false)&quot;;
				if(bigIcon){
					 links += &quot;&lt;table style='border:1px white' width='100%' cellpadding='2' cellspacing='0' &quot; +
					 &quot;class='templateMenuHover &quot; + menuClass + &quot;' &quot; +
	        		 &quot;title='&quot; + mouseOver + &quot;' onclick=&quot;javascript:hideMenu($(this));&quot; + link + &quot;&quot;&gt;&quot; +
	        		 &quot;&lt;tr&gt;&lt;td style='vertical-align:middle'&gt;&lt;img style='text-align:center;vertical-align:middle' src='/_layouts/images/&quot; + iconPrefix + &quot;ic&quot; + DocIcon + &quot;.gif' /&gt;&lt;/td&gt;&quot; +
	        		 &quot;&lt;td width='100%' style='padding:0 15 0 10'&gt;&lt;b&gt;&quot; + docName + &quot;&lt;/b&gt;&lt;br&gt;&quot; + docDescription + &quot;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&quot;;	        		 
				}else{
					 links += &quot;&lt;table style='border:1px white' width='100%' cellpadding='2' cellspacing='0' &quot; +
					 &quot;class='templateMenuHover &quot; + menuClass + &quot;' &quot; +
	        		 &quot;title='&quot; + mouseOver + &quot;' onclick=&quot;javascript:hideMenu($(this));&quot; + link + &quot;&quot;&gt;&quot; + 
	        		 &quot;&lt;tr&gt;&lt;td style='vertical-align:middle;padding-left:2px'&gt;&lt;img src='/_layouts/images/&quot; + iconPrefix + &quot;ic&quot; + DocIcon + &quot;.gif' /&gt;&lt;/td&gt;&quot; + 
	        		 &quot;&lt;td width='100%' style='padding:0 15 0 10;white-space:nowrap'&gt;&quot; + docName + &quot;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&quot;;
				}
	    	}
	    }); 
    }else{ 
    	// No templates available for this view
		 links += &quot;&lt;table style='border:1px white' width='100%' cellpadding='2' cellspacing='0' &quot; +
		 &quot;class='templateMenuHover &quot; + menuClass + &quot;' &quot; + &quot;title=''&gt;&quot; + 
		 &quot;&lt;tr&gt;&lt;td style='vertical-align:middle;padding-left:2px'&gt;&lt;img src='/_layouts/images/&quot; + iconPrefix + &quot;ichlp.gif' /&gt;&lt;/td&gt;&quot; + 
		 &quot;&lt;td width='100%' style='white-space:nowrap;padding:0 10 0 10'&gt;No templates available&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&quot;;
   	
    }
    // Show upload option
    if(uploadOption){
		 links += &quot;&lt;table style='border:1px white' width='100%' cellpadding='2' cellspacing='0' &quot; +
		 &quot;class='templateMenuHover &quot; + menuClass + &quot;' &quot; + &quot;title='' onclick='location.href=&quot;&quot; + ctx.HttpRoot + &quot;/_layouts/Upload.aspx?List=&quot; + ctx.listName + &quot;&quot;'&gt;&quot; + 
		 &quot;&lt;tr&gt;&lt;td style='vertical-align:middle;padding-left:2px'&gt;&lt;img src='/_layouts/images/&quot; + uploadIMG +&quot;' /&gt;&lt;/td&gt;&quot; + 
		 &quot;&lt;td width='100%' style='white-space:nowrap;padding:0 10 0 10'&gt;&quot; + uploadButtonTitle + &quot;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&quot;;    			 
    }
    // Build the menu
  	links = &quot;&lt;div style='border-top:thin groove white;border-bottom:thin groove white;border-left:thin groove white;background-color:&quot; + bgColor + &quot;;height:25' &gt;&quot; +
  			&quot;&lt;div style='cursor:pointer;float:left;border:thin groove silver;padding:2;background-color:&quot; + btnBgColor + &quot;' class='templateMenuFirst'&gt;&quot; +
  			&quot;&lt;div title='Open Menu'&gt;&quot; + menuTitle + &quot;&lt;img alt='' src='/_layouts/images/menudark.gif'/&gt;&lt;/div&gt;&quot; +
  			&quot;&lt;div class='templateMenu ms-MenuUIPopupBody' style='display:none'&gt;&quot; + links + &quot;&lt;/div&gt;&quot; +
  			&quot;&lt;/div&gt;&quot; +
  			&quot;&lt;/div&gt;&quot;;
    
    // Insert the menu
	$(&quot;.ms-bodyareaframe&quot;).prepend(links);
	// Add show-hide functionality to the menu
	addShowHideToCustomMenu();
}

function addShowHideToCustomMenu(){ 
	// Add onclick and mouseleave to show and hide the menu
	$('.templateMenuFirst').click(function(){
		$('.templateMenu').slideDown(250)
		.css('position','absolute')
		.css('zIndex','1000')
	})
	.mouseleave(function(){
		$('.templateMenu').fadeOut(350);
	});
	
	// Add hover effect to highlight the element hovered with the mouse
	$(&quot;.templateMenuHover&quot;).hover(
	function () {
		$(this).addClass('ms-MenuUIItemTableHover');
	}, 
	function () {
		$(this).removeClass('ms-MenuUIItemTableHover');
	});
}

function hideMenu(obj){
	// Remove rather than hide to prevent flicker
	$('.templateMenu').remove();
	// Reload page - needed for Firefox
	history.go(0);
}

// Overcome the zIndex glitch in IE7 - http://www.vancelucas.com
$(function() { 
	var zIndexNumber = 1000;
	$('div.templateMenu').each(function() {
		$(this).css('zIndex', zIndexNumber);
		zIndexNumber -= 10;
	});
});

Save this as a file named “MasterDocumentTemplates.js” – be sure to use the right file extension, and upload to the javascript library as shown above.

Then add a CEWP below the list view in the document library where you want the menu to appear.
IMG

And add this code to it:

&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/jquery-1.3.2.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/interaction.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/stringBuffer.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;/test/English/Javascript/MasterDocumentTemplates.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot;&gt;
// bigIcon mode true
getTemplatesForThisLibrary('DocumentTemplates','/test/English','',true,true,'Upload document','Select template from list');
&lt;/script&gt;

Parameters explained:

  • templatesListName
    Document library DisplayName of your master template library
  • templatesListbaseUrl
    The baseUrl of the site the library resides in (not the URL to the Document library it selves)
  • targetListUrlDir
    The actual URL path to the save location – to save in “current” library – where the menu is – use ”
  • bigIcon
    true for large icon and description (from field “MouseOver”) below the template name
    false for small icon mode with the value from the field “MouseOver” as tooltip and only the template name visible
  • uploadOption
    true add option for upload
    false hides option for upload
  • uploadButtonTitle
    if uploadOption set to true – the text displayed on the upload “option”
  • menuTitle
    The text on the dropdown-menu

The menu will appear like this:
IMG
You can edit the color of the “button” and its background “banner” in line 44-45 in the script.

The menu should look like this in the “Default view” (note that the PowerPoint template is not displayed:
IMG

And like this in the view “PowerPoint”:
IMG

If you select “bigIcon=false” the menu looks like this:
IMG

I hope someone finds this “feature” handy!
Ask if something is unclear.

Regards
Alexander