I have a "Priority Indicator" column in my SharePoint page which shows a bullet point of either green, yellow or red. I cannot seem to find anywhere any help on how I can code this to link with "Due Date" column.
At the moment my status indicators only show if high, normal or low on "Priority" are selected.
For the "Priority Indicators" the code I have is;
Priority Indicator
For my JavaScript code see below;
JavaScript Code
I want the Priority Indicators to show;
Red indicator shows if due date within a week
Yellow indicator shows if due date within 2 weeks
Green indicator shows if due date within 3+ weeks.
Lookup column won't refresh automatically so I suggest you use CSR for this requirement.
Insert script editor webpart into your list view webpart page and insert below script into script editor webpart.
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script type="text/javascript">
(function () {
// Create object that have the context information about the field that we want to change it's output render
var priorityFiledContext = {};
priorityFiledContext.Templates = {};
priorityFiledContext.Templates.Fields = {
// Apply the new rendering for Priority field on List View
"Priority_x0020_Indicators": { "View": priorityFiledTemplate }
};
SPClientTemplates.TemplateManager.RegisterTemplateOverrides(priorityFiledContext);
})();
// This function provides the rendering logic for list view
function priorityFiledTemplate(ctx) {
// get today's date
var today = new Date();
// zero out the time portion so we will only compare days
today.setHours(0, 0, 0, 0);
// get the date set in your date YourDateField
var itemDate = new Date(ctx.CurrentItem["Due_x0020_Date"]);
// zero out the time portion so we only compare days
itemDate.setHours(0, 0, 0, 0);
// Return html element with appropriate color based on priority value
var oneDay = 24 * 60 * 60 * 1000; // hours*minutes*seconds*milliseconds
var diffDays = Math.round(Math.abs((itemDate.getTime() - today.getTime()) / (oneDay)));
if (diffDays < 7) {
return "<span style='color:red'>•</span>";
} else if (diffDays < 14) {
return "<span style='color:yellow'>•</span>";
} else {
return "<span style='color:green'>•</span>";
}
}
</script>
Check field static name by developer tool(in list new/edit form).
Related
I want to make filters in Gmail email with certain subjects automatically deleted if more than 1 day from example#gmail.com. but when the filter is entered there is a warning from gmail:
Filter searches containing "label:", "in:", "is:", date range (e.g. "before:" or "after:") or stars criteria (e.g. "has:yellow-star") are not recommended as they will never match incoming emails. Do you still wish to continue to the next step?
How do you do it in the latest version (Gmail)?
I want to delete emails with certain subjects more than 1 day from example#gmail.com, how do that?.
Try using Google Script:
- Open: https://script.google.com
- Click File => New => Script File
- Type this code
// reference: https://medium.com/#fw3d/a-cleaning-robot-for-your-gmail-inbox-f44c01306ea2
// Modify by: Dicky Ibrohim for delete (move to trash) specific subject, sender, and time
function removetotrash() {
var delayDays = 1; // Impact form email more than 1day
var maxDate = new Date();
maxDate.setDate(maxDate.getDate()-delayDays);
// What will be executed, adjust the string here
var searches = [
'"SUBJECT WANT SEARCH TO DELETE" from:example#gmail.com'
];
// Looping for 500 last email
var threads = [];
for (var i = 0; i < searches.length; i++) {
var tmp_threads = GmailApp.search(searches[i], 0, 500); // search until 500 mail
var threads = threads.concat(tmp_threads);
}
// Then remove all to trash
for (var i = 0; i < threads.length; i++) {
if (threads[i].getLastMessageDate()<maxDate)
{
threads[i].moveToTrash();
}
}
}
Click File => Save
Make it work automatic: Click icon "Current project's triggers",
Click "Create new triger"
Setup AND SELECT intermediate time
Save
I understand SharePoint lists are like excel, so I was wondering if it was possible to conditionally highlight whole rows/ cells based on the text value of a field.
I have a column in a list called "Status" with 4 options (initial, in progress, completed, awaiting developer resource). I would like to highlight these rows (or even just the status field) a different colour, depending on the value of the status.
Is this possible? Cant find anything relating to this for SP 2016
Cheers
Please use JavaScript to highlight the row based on the Status field:
<script type="text/javascript">
SP.SOD.executeFunc("clienttemplates.js", "SPClientTemplates", function() {
SPClientTemplates.TemplateManager.RegisterTemplateOverrides({
OnPostRender: function(ctx) {
var statusColors = {
'initial' : '#FFF1AD',
'in progress' : '#FFD800',
'completed' : '#01DF3A',
'awaiting developer resource':'#ff0000'
};
var rows = ctx.ListData.Row;
for (var i=0;i<rows.length;i++)
{
var status = rows[i]["Status"];
var rowId = GenerateIIDForListItem(ctx, rows[i]);
var row = document.getElementById(rowId);
row.style.backgroundColor = statusColors[status];
}
}
});
});
</script>
And place the code above in a Content Editor Web Part in the list view page, so the list row will render different color based on status:
I have 2 lists which are Room and Equipment.
In the Equipment list there is a lookup Field to the Room list .
I want to display all related Equipment in Room simple tabular Display Form.
How should I achieve this?
A general solution would be as follow
Check you can work with the REST API in JavaScript like below. (in this step you can just make sure the URL is returning XML data you requested)
http://portal/_api/web/lists/getByTitle('Equipments')/items/?$select=Title,ID&$filter=Room eq 'room1'
create a new custom DisplayForm with SharePoint Designer and add the following jQuery Script to read and generate the previous fetched data at the end of
<script src="/_layouts/15/CDN/Js/jquery-1.11.1.min.js" type="text/javascript" > </script>
<script type="text/javascript" >
function LoadEquipments(roomValue) {
$.ajax({
url : _spPageContextInfo.webServerRelativeUrl + "/_api/web/lists/getByTitle('Equipments')/items/" +
"?$select=Title,ID" +
"&$filter=Room eq '" + roomValue + "'",
type : "GET",
headers : {
"accept" : "application/json;odata=verbose",
},
success : function (data) {
var equipments = [];
var results = data.d.results;
for (var i = 0; i < results.length; i++) {
if (equipments.indexOf(results[i].Title) < 0)
equipments.push(results[i].Title);
}
var ullist = $('<ul/>');
for (var i = 0; i < equipments.length; i++)
$('<li/>').val(equipments[i]).html(equipments[i]).appendTo(ullist);
ullist.appendTo('#divEquipements');
$("#divEquipements ul li").css("font-size", "16px").css("color", "salmon");
},
error : function (err) {
alert(JSON.stringify(err));
}
});
}
$(document).ready(function () {
var roomVal = $("#TitleValue").text();
LoadEquipments(roomVal);
});
for previous code to work as you might guess you would better add id for new td to enclose the new requirements(for example a new tr as the last row which contains aa div with id='divEquipements' ) data and also just add id='TitleValue' to the td containing the room title (usually the first row)
OK there are a lot of possibilities for achieving this. Some possible and good looking solution can be:
Overload the click of the market value in the list(can be done using JSLink) to open a Modal dialog page where all the equipments are listed(fetched using REST query)
Overload the click of the market value in the list(can be done using JSLink) and redirect to the same page but with filters to the same view.
If the number of markets are not many, views can also be created per market(Not so good, but will reduce coding effort)
If its something else you are looking for, please let know. Hope this helps!
I've been trying to create a code that takes info from a Google Spreadsheet, and creates Google Calendar events. I'm new to this, so bear with my lack of in-depth coding knowledge!
I initially used this post to create a code:
Create Google Calendar Events from Spreadsheet but prevent duplicates
I then worked out that it was timing out due to the number of rows on the spreadsheet, and wasn't creating eventIDs to avoid the duplicates. I got an answer here to work that out!
Google Script that creates Google Calendar events from a Google Spreadsheet - "Exceeded maximum execution time"
And now I've realised that it's over-writing the formulas, I have in the spreadsheet, auto-completing into each row, as follows:
Row 12 - =if(E4="","",E4+1) // Row 13 - =if(C4="","",C4+1) // Row 18 - =if(B4="","","WHC - "&B4) // Row 19 - =if(B4="","","Docs - "&B4)
Does anyone have any idea how I can stop it doing this?
/**
* Adds a custom menu to the active spreadsheet, containing a single menu item
* for invoking the exportEvents() function.
* The onOpen() function, when defined, is automatically invoked whenever the
* spreadsheet is opened.
* For more information on using the Spreadsheet API, see
* https://developers.google.com/apps-script/service_spreadsheet
*/
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Export WHCs",
functionName : "exportWHCs"
},
{
name : "Export Docs",
functionName : "exportDocs"
}];
sheet.addMenu("Calendar Actions", entries);
};
/**
* Export events from spreadsheet to calendar
*/
function exportWHCs() {
// check if the script runs for the first time or not,
// if so, create the trigger and PropertiesService.getScriptProperties() the script will use
// a start index and a total counter for processed items
// else continue the task
if(PropertiesService.getScriptProperties().getKeys().length==0){
PropertiesService.getScriptProperties().setProperties({'itemsprocessed':0});
ScriptApp.newTrigger('exportWHCs').timeBased().everyMinutes(5).create();
}
// initialize all variables when we start a new task, "notFinished" is the main loop condition
var itemsProcessed = Number(PropertiesService.getScriptProperties().getProperty('itemsprocessed'));
var startTime = new Date().getTime();
var sheet = SpreadsheetApp.getActiveSheet();
var headerRows = 4; // Number of rows of header info (to skip)
var range = sheet.getDataRange();
var data = range.getValues();
var calId = "flightcentre.com.au_pma5g2rd5cft4lird345j7pke8#group.calendar.google.com";
var cal = CalendarApp.getCalendarById(calId);
for (i in data) {
if (i < headerRows) continue; // Skip header row(s)
var row = data[i];
var date = new Date(row[12]); // First column
var title = row[18]; // Second column
var tstart = new Date(row[15]);
tstart.setDate(date.getDate());
tstart.setMonth(date.getMonth());
tstart.setYear(date.getYear());
var tstop = new Date(row[16]);
tstop.setDate(date.getDate());
tstop.setMonth(date.getMonth());
tstop.setYear(date.getYear());
var id = row[17]; // Sixth column == eventId
// Check if event already exists, update it if it does
try {
var event = cal.getEventSeriesById(id);
}
catch (e) {
// do nothing - we just want to avoid the exception when event doesn't exist
}
if (!event) {
//cal.createEvent(title, new Date("March 3, 2010 08:00:00"), new Date("March 3, 2010 09:00:00"));
var newEvent = cal.createEvent(title, tstart, tstop).addEmailReminder(5).getId();
row[17] = newEvent; // Update the data array with event ID
}
else {
event.setTitle(title);
}
if(new Date().getTime()-startTime > 240000){ // if > 4 minutes
var processed = i+1;// save usefull variable
PropertiesService.getScriptProperties().setProperties({'itemsprocessed':processed});
range.setValues(data);
MailApp.sendEmail(Session.getEffectiveUser().getEmail(),'progress sheet to cal','item processed : '+processed);
return;
}
debugger;
}
// Record all event IDs to spreadsheet
range.setValues(data);
}
/**
* Export events from spreadsheet to calendar
*/
function exportDocs() {
// check if the script runs for the first time or not,
// if so, create the trigger and PropertiesService.getScriptProperties() the script will use
// a start index and a total counter for processed items
// else continue the task
if(PropertiesService.getScriptProperties().getKeys().length==0){
PropertiesService.getScriptProperties().setProperties({'itemsprocessed':0});
ScriptApp.newTrigger('exportDocs').timeBased().everyMinutes(5).create();
}
// initialize all variables when we start a new task, "notFinished" is the main loop condition
var itemsProcessed = Number(PropertiesService.getScriptProperties().getProperty('itemsprocessed'));
var startTime = new Date().getTime();
var sheet = SpreadsheetApp.getActiveSheet();
var headerRows = 4; // Number of rows of header info (to skip)
var range = sheet.getDataRange();
var data = range.getValues();
var calId = "flightcentre.com.au_pma5g2rd5cft4lird345j7pke8#group.calendar.google.com";
var cal = CalendarApp.getCalendarById(calId);
for (i in data) {
if (i < headerRows) continue; // Skip header row(s)
var row = data[i];
var date = new Date(row[13]); // First column
var title = row[19]; // Second column
var tstart = new Date(row[15]);
tstart.setDate(date.getDate());
tstart.setMonth(date.getMonth());
tstart.setYear(date.getYear());
var tstop = new Date(row[16]);
tstop.setDate(date.getDate());
tstop.setMonth(date.getMonth());
tstop.setYear(date.getYear());
var id = row[20]; // Sixth column == eventId
// Check if event already exists, update it if it does
try {
var event = cal.getEventSeriesById(id);
}
catch (e) {
// do nothing - we just want to avoid the exception when event doesn't exist
}
if (!event) {
//cal.createEvent(title, new Date("March 3, 2010 08:00:00"), new Date("March 3, 2010 09:00:00"));
var newEvent = cal.createEvent(title, tstart, tstop).addEmailReminder(5).getId();
row[20] = newEvent; // Update the data array with event ID
}
else {
event.setTitle(title);
}
if(new Date().getTime()-startTime > 240000){ // if > 4 minutes
var processed = i+1;// save usefull variable
PropertiesService.getScriptProperties().setProperties({'itemsprocessed':processed});
range.setValues(data);
MailApp.sendEmail(Session.getEffectiveUser().getEmail(),'progress sheet to cal','item processed : '+processed);
return;
}
debugger;
}
// Record all event IDs to spreadsheet
range.setValues(data);
}
You have to ways to solve that problem.
First possibility : update your sheet with array data only on columns that have no formulas, proceeding as in this other post but in your case (with multiple columns to skip) it will rapidly become tricky
Second possibility : (the one I would personally choose because I 'm not a "formula fan") is to do what your formulas do in the script itself, ie translate the formulas into array level operations.
following your example =if(E4="","",E4+1) would become something like data[n][4]=data[n][4]==''?'':data[n+1][4]; if I understood the logic (but I'm not so sure...).
EDIT
There is actually a third solution that is even simpler (go figure why I didn't think about it in the first place...) You could save the ranges that have formulas, for example if col M has formulas you want to keep use :
var formulM = sheet.getRange('G1:G').getFormulas();
and then, at the end of the function (after the global setValues()) rewrite the formulas using :
sheet.getRange('G1:G').setFormulas(formulM);
to restore all the previous formulas... as simple as that, repeat for every column where you need to keep the formulas.
I realize this is probably an accessibility issue that may best be left alone, but I'd like to figure out if it possible to prevent the tab from visiting the address bar in the tabbing cycle.
My application has another method of cycling through input areas, but many new users instinctively try to use the tab, and it doesn't work as expected.
Here's a generic jquery implementation where you don't have to find the max tab index. Note that this code will also work if you add or remove elements in your DOM.
$('body').on('keydown', function (e) {
var jqTarget = $(e.target);
if (e.keyCode == 9) {
var jqVisibleInputs = $(':input:visible');
var jqFirst = jqVisibleInputs.first();
var jqLast = jqVisibleInputs.last();
if (!e.shiftKey && jqTarget.is(jqLast)) {
e.preventDefault();
jqFirst.focus();
} else if (e.shiftKey && jqTarget.is(jqFirst)) {
e.preventDefault();
jqLast.focus();
}
}
});
However, you should note that the code above will work only with visible inputs. Some elements may become the document's activeElement even if they're not input so if it's your case, you should consider adding them to the $(':input:visible') selector.
I didn't add code to scroll to the focus element as this may not be the wanted behavior for everyone... if you need it, just add it after the call to focus()
You can control the tabbing order (and which elements should be able to get focus at all) with the global tabindex attribute.
However, you can't prevent users to tab into another context not under control of the page (e.g. the browser's address bar) with this attribute. (It might be possible in combination with JavaScript, though.)
For such a (evil!) use case, you'd have to look into keyboard traps.
WCAG 2.0 has the guideline: 2.1.2 No Keyboard Trap. In Understanding SC 2.1.2 you can find "Techniques and Failures" for this guideline:
F10: Failure of Success Criterion 2.1.2 and Conformance Requirement 5 due to combining multiple content formats in a way that traps users inside one format type
FLASH17: Providing keyboard access to a Flash object and avoiding a keyboard trap
G21: Ensuring that users are not trapped in content
So maybe you get some ideas by that how such a trap would be possible.
I used to add two tiny, invisible elements on tabindex 1 and on the last tabindex. Add a onFocus for these two: The element with tabindex 1 should focus the last real element, the one with the max tabindex should focus the first real element. Make sure that you focus the first real element on Dom:loaded.
You could use Javascript and capture the "keydown" event on the element with the highest "tabindex". If the user presses the "TAB" key (event.keyCode==9) without the "Shift" key (event.shiftKey == false) then simply set the focus on the element with the lowest tabindex.
You would then also need to do the same thing in reverse for the element with the lowest tabindex. Capture the "keydown" event for this element. If the user presses the "TAB" key (event.keyCode==9) WITH the "Shift" key (event.shiftKey == true) then set the focus on the element with the highest tabindex.
This would effectively prevent the address bar from ever being focused using the TAB key. I am using this technique in my current project.
Dont forget to cancel the keydown event if the proper key-combination is pressed! With JQuery it's "event.preventDefault()". In standard Javascript, I believe you simply "return false".
Here's a JQuery-laden snippet I'm using...
$('#dos-area [tabindex=' + maxTabIndex + ']').on('keydown', function (e) {
if (e.keyCode == 9 && e.shiftKey == false) {
e.preventDefault();
$('#dos-area [tabindex=1]').focus();
}
});
$('#dos-area [tabindex=1]').on('keydown', function (e) {
if (e.keyCode == 9 && e.shiftKey == true) {
e.preventDefault();
$('#dos-area [tabindex=' + maxTabIndex + ']').focus();
}
});
Also keep in mind that setting tabindex=0 has undesirable results on the order in which things are focused. I always remember (for my purposes) that tabindex is a 1-based index.
Hi i have an easy solution. just place an empty span on the end of the page. Give it an id and tabindex = 0, give this span an onfocus event, when triggered let your focus jump to the first element on your page you want to cycle trough. This way you won't lose focus on the document, because if you do your events don't work anymore.
I used m-albert solution and it works. But in my case I do not control the tabindex properties. My intention is set the focus on a toolbar at the top of the page (first control) when user leaves the last control on the page.
$(':input:visible').last().on('keydown', function (e) {
if (e.keyCode == 9 && e.shiftKey == false) {
e.preventDefault();
$('html, body').animate({
scrollTop: 0
}, 500);
$(':input:visible', context).first().focus();
}
});
Where context can be any Jquery object, selector, or even document, or you can omit it.
The scrolling animation, of course, is optional.
Not sure if this is still a issue, but I implemented my own solution that does not use jQuery, can be used in the case when all the elements have tabindex="0", and when the DOM is subject to change. I added an extra argument for a context if you want to limit the tabcycling to a specific element containing the tabindex elements.
Some brief notes on the arguments:
min must be less than or equal to max, and contextSelector is an optional string that if used should be a valid selector. If contextSelector is an invalid selector or a selector that doesn't match with any elements, then the document object is used as the context.
function PreventAddressBarTabCyle(min, max, contextSelector) {
if( isNaN(min) ) throw new Error('Invalid argument: first argument needs to be a number type.')
if( isNaN(max) ) throw new Error('Invalid argument: second argument needs to be a number type.')
if( max < min ) throw new Error('Invalid arguments: first argument needs to be less than or equal to the second argument.')
min = min |0;
max = max |0;
var isDocumentContext = typeof(contextSelector) != 'string' || contextSelector == '';
if( min == max ) {
var tabCycleListener = function(e) {
if( e.keyCode != 9 ) return;
var context = isDocumentContext ? document : document.querySelector(contextSelector);
if( !context && !('querySelectorAll' in context) ) {
context = document;
}
var tabindexElements = context.querySelectorAll('[tabindex]');
if( tabindexElements.length <= 0 ) return;
var targetIndex = -1;
for(var i = 0; i < tabindexElements.length; i++) {
if( e.target == tabindexElements[i] ) {
targetIndex = i;
break;
}
}
// Check if tabbing backward and reached first element
if( e.shiftKey == true && targetIndex == 0 ) {
e.preventDefault();
tabindexElements[tabindexElements.length-1].focus();
}
// Check if tabbing forward and reached last element
if( e.shiftKey == false && targetIndex == tabindexElements.length-1 ) {
e.preventDefault();
tabindexElements[0].focus();
}
};
} else {
var tabCycleListener = function(e) {
if( e.keyCode != 9 ) return;
var context = isDocumentContext ? document : document.querySelector(contextSelector);
if( !context && !('querySelectorAll' in context) ) {
context = document;
}
var tabindex = parseInt(e.target.getAttribute('tabindex'));
if( isNaN(tabindex) ) return;
// Check if tabbing backward and reached first element
if (e.shiftKey == true && tabindex == min) {
e.preventDefault();
context.querySelector('[tabindex="' + max + '"]').focus();
}
// Check if tabbing forward and reached last element
else if (e.shiftKey == false && tabindex == max) {
e.preventDefault();
context.querySelector('[tabindex="' + min + '"]').focus();
}
};
}
document.body.addEventListener('keydown', tabCycleListener, true);
}
More notes:
If min is equal to max, then tab cycling will occur naturally until the last element in the context is reached. If min is strictly less than max, then tabbing will cycle naturally until either the min or the max is reached. If tabbing backwards and the min is reached, or tabbing forward and the max is reached, then the tabbing will cycle to the min element or max element respectively.
Example usage:
// For all tabindex elements inside the document
PreventAddressBarTabCyle(0,0);
// Same as above
PreventAddressBarTabCyle(1,1);
// NOTE: if min == max, the tabindex value doesn't matter
// it matches all elements with the tabindex attribute
// For all tabindex elements between 1 and 15
PreventAddressBarTabCyle(1,15);
// For tabindex elements between 1 and 15 inside
// the first element that matches the selector: .some-form
PreventAddressBarTabCyle(1,15, '.some-form');
// For all tabindex elements inside the element
// that matches the selector: .some-form2
PreventAddressBarTabCyle(1,1, '.some-form2');
Salinan solution worked for me
Put this in the start of your html page:
<span tabindex="0" id="prevent-outside-tab"></span>
and this at the end of your html page.:
<span tabindex="0" onfocus="foucsFirstElement()"></span>
foucsFirstElement() :
foucsFirstElement() {
document.getElementById("prevent-outside-tab").focus();
},