Sharepoint > Which characters are *not* allowed in a list? - sharepoint

I've tried looking this up and haven't come up with the answer I'm looking for; I've found what cannot be included in filenames, folder names, and site names... but nothing on actual fields in a list.
I noticed that the percent symbol (%) is one that's not allowed in files/sites/folders. But it also doesn't populate when I try to pro grammatically add the fields to the list. I am doing this by using a small C# application that sends the data via Sharepoint 2010's built-in web services. I can manually enter the character, but it messes up each field in the row if I try it through code.
I've tried some of the escape characters that I've found via Google (_x26), but these don't seem to work either. Has anyone else had an issue with this? If these characters are allowed, how can I escape them when sending the data through a web service call?
Thanks in advance!
Justin

Any characters that aren't allowed when you enter a field name get encoded in the internal name. The format is a little different to what you show - try "_x0026_".
I usually avoid issues with weird internal names by creating the field with no spaces or special characters in the name, then renaming it. When you rename a field, only the display name changes and you keep the simple internal name.

Characters not allowed in SharePoint file name:
~, #, %, & , *, {, }, \, :, <, >, ?, /, |, "
Pasted from http://chrisbarba.com/2011/01/27/sharepoint-filename-special-characters-not-allowed/

Am I saying something weird when I state that there usually is a reason for certain characters not being allowed. I don't know which or why, but there probably is a reason.
Since you control which fields need to be there you can also dictate their (internal) names. I'd say follow best practice and name your fields using Camel case. And because you created them, you can just map the fields to the according fields in your data source.

As a follow on to #Elisa's answer, here's some JavaScript / TypeScript code that helps to prevent users from uploading files into SharePoint with invalid characters in the file name implemented on Nintex forms.
Here's the gist of the JavaScript version (note you'll have to obviously adapt for your own needs since this was designed for Nintex) :
//------------------------------------------------------------------------
//JavaScript Version:
//Code from http://cc.davelozinski.com/tips-techniques/nintex-form-tips-techniques/javascript-typescript-for-nintex-forms-to-validate-file-names
//------------------------------------------------------------------------
function validateAttachmentNames(eventObject) {
var textbox$ = NWF$(this);
var attachrowid = this.id.substring(10, 47);
var fileUploadid = attachrowid;
var index = attachrowid.substring(36);
//console.log('index:' + index);
//console.log('attachrowid:' + attachrowid);
//console.log('fileUploadid:' + fileUploadid);
if (index == '') {
attachrowid += '0';
}
var fileName = NWF.FormFiller.Attachments.TrimWhiteSpaces(textbox$.val().replace(/^.*[\\\/]/, ''));
var match = (new RegExp('[~#%\&{}+\|]|\\.\\.|^\\.|\\.$')).test(fileName);
if (match) {
isValid = false;
setTimeout(function () {
NWF$("tr[id^='attachRow']").each(function () {
var arrParts = (NWF$(this).find(".ms-addnew")[0]).href.split('"');
var fileName = arrParts[5];
var attachRow = arrParts[1];
var fileUpload = arrParts[3];
var match = (new RegExp('[~#%\&{}+\|]|\\.\\.|^\\.|\\.$')).test(fileName);
if (match) {
console.log(fileName);
NWF.FormFiller.Attachments.RemoveLocal(attachRow, fileUpload, fileName);
alert('Invalid file: ' + fileName + ' You cannot attach files with the following characters ~ # % & * { } \ : < > ? / + | \n\nThe file has been removed.');
}
});
}, 500);
}
}

Related

How to extract value from a url

I have a few requests coming in which follow the pattern below
contacts/id/
contacts/x/id/name
contacts/x/y/id/address
contacts/z/address/
I want to extract the value which follows right after 'contacts'
In above cases,
1. id
2. x
3. x
4. z
Here is my regex
(?<=contacts)\/[^\/]+
https://regex101.com/r/ePmv5Y/1
But it is matching along with the trailing '/' for eg. /id, /x etc
How do I optimize to get rid of this trailing slash?
We can use match() here:
var urls = ["contacts/id/", "contacts/x/id/name", "contacts/x/y/id/address", "contacts/z/address/"];
for (var i=0; i < urls.length; ++i) {
var output = urls[i].match(/\bcontacts\/(.*?)\//)[1];
console.log(urls[i] + " => " + output);
}
I have a few requests coming in
If you mean http requests, then this is likely the pathname of the requested URL, and they'll start with a /. (This is the value of req.url in a Node.js server.)
To match on a URL pathname, you can use this expression: ^\/contacts\/([^/?]+). Here's a link to another regular expression builder that demonstrates it and includes an explanation for every character: https://regexr.com/6tugf
The [^/?] is a negated set that matches any token which is not a / or a ? and the + means that it matches 1 or more of those tokens. It's important to include the ? because otherwise it could match into the query string portion of the URL — for example, in this URL:
https://domain.tld/contacts/x/id/name?filter=recent # URL
/contacts/x/id/name?filter=recent # req.url in Node.js
/contacts/x/id/name # pathname
?filter=recent # query string
And here's a runnable code snippet demonstrating the same expression, using String.prototype.match():
const contactIdRegexp = /^\/contacts\/([^/?]+)/;
const inputs = [
'/contacts/id/', // id
'/contacts/x/id/name', // x
'/contacts/x/y/id/address', // x
'/contacts/z/address/', // z
'/contacts/x/id/name?filter=recent', // x
];
for (const str of inputs) {
const id = str.match(contactIdRegexp)?.[1];
console.log(id);
}
You can add the / inside the lookbehind:
(?<=contacts\/)[^\/]+
See a regex demo.
If you like to continue without regex, You can try below.
//get the URL object.
const url = new URL(`${req.protocol}://${req.get('host')}${req.originalUrl}`);
//extract the pathname and split using "/"
const pathName= url.pathname.split("/");
//get the required value using array index.
const val = pathName[2];

Office.js wildcard search with special characters crashes Word Online but not installed MSWord

Wildcard searches such as the example detailed in the following article crashes Word Online, whilst working as expected for the installed version of MSWord
https://learn.microsoft.com/en-us/office/dev/add-ins/word/search-option-guidance
I'm searching for the following text:
'Payment of invoices by Sponsor shall be made within thirty (30'
The text may or may not be broken across line/section breaks. Therefore I'm performing a wildcard search as follows:
'Payment?of?invoices?by?Sponsor?shall?be?made?within?thirty?(30'
This causes Word-Online to crash the browser.
Interestingly the following works in both Word Online and MSWord:
'Payment of invoices by Sponsor shall be made within thirty (30) days'
'Payment?of?invoices?by?Sponsor?shall?be?made?within?thirty?(30)?days'
There appears to be a problem with un-closed parenthesis in the wildcard expression.
Following the article above, it suggests that the parenthesis should be escaped with square brackets. The following worked in MSWord but crashed Word-Online:
'Payment?of?invoices?by?Sponsor?shall?be?made?within?thirty?[(]30'
Even stranger is that the square brackets escapement only works if the last character is being escaped.
This works in both MSWord and Word-Online:
'include?intangible?things?[(]'
whereas this only works in MSWord and crashes Word-Online
include?intangible?things?[(]e
The example pattern in the article above ' [ * ] * ' crashes Word Online, whilst working as expected in MSWord.
input = 'include?intangible?things?[(]e';
const { Word } = window;
Word.run(context => {
const { body } = context.document;
const searchResults = body.search(input, {
matchCase: false,
ignoreSpace: true,
ignorePunct: true,
matchWildcards: true
});
context.load(searchResults);
return context
.sync()
.then(() => {
})
.catch(error => {
});
});
Both MSWord and Word Online should behave the same when both are executed through the Office.js API.
This looks like a bug in Word Online. I would speculate that it's probably something to do with url encoding.
Either that, or I'm not encoding my input pattern correctly when the target is Word Online. In which case the article referenced above needs updating to detail what the Word Online 'escapes' are for the special characters.
Curiously the escaping [ ] brackets are themselves special characters.
Word Online crashing
Not a perfect answer this, but it solves the Word Online crashing issue and the un-closed parenthesis problem in one hit. I'm replacing all the special characters in my search string with '?' characters. This might result in a misidentified piece of text, but the likelihood is low enough for this to not be a problem for what I'm trying to achieve.
function replaceSpecialCharacters(input) {
let modifiedInput = '';
const specialCharacters = ['[', ']', '(', ')', '{', '}', '*', '?', '<', '>', '!', '#'];
[...input].forEach(c => {
if (specialCharacters.includes(c)) {
modifiedInput = `${modifiedInput}?`;
} else {
modifiedInput = `${modifiedInput}${c}`;
}
});
return modifiedInput;
}

How to split text depending on word count

I am trying to make a lyric project using discord.js, cheerio and the website called genius.com.
I have successfully found a way to scrape the lyrics from the website, I am onto the part where I need to split it because discord has a max word limit of 2000.
I can check how many characters/words are in the overall lyrics by doing lyrics.length, I just need to find a way to split the string and send both, in the future I might implement richEmbeds to make it more stylish but for now I'm focusing on the basics.
var request = require('request');
var cheerio = require('cheerio');
/*
This is a project for my discord bot, the reason for the 2000 word limit is because
discords character limit is currently set to 2000, this means that i will have to add
a function to split the lyrics and send each part
*/
//Define the URL that we are going to be scraping the data from
var UR_L = "https://genius.com/Josh-a-and-jake-hill-not-afraid-of-dying-lyrics";
//send a request to the website and return the contents of the website
request(UR_L, function(err, resp, body) {
//load the website using cheerio
$ = cheerio.load(body);
//define lyrics as the selector to text form
var lyrics = $('p').text();
if (lyrics.length > "2000" && lyrics.length < "4000") {
} else if (lyrics.length > "4000" && lyrics.length < "6000") {
} else {
//send the lyrics as one message
}
})
You can find a live version running here on repl.it.
You don't need to use any fancy function, that function is already built in discord.js: you can attach some options to a message, and MessageOptions.split is what you're searching for. When you want to send the text, do it like this:
channel.send(lyrics, { split: true });
If lyrics.length is greater that the limit, discord.js will cut your messages and send them one after the other, making it seem like it's only one.
channel is the TextChannel you want to send the messages to.
Discord has a 2000 characters limit not a 2000 words limit.
One solution to your problem could be this:
// This will result in an array with strings of max 2000 length
const lyricsArr = lyrics.match(/.{1,2000}/g);
lyricsArr.forEach(chunk => sendMessage(chunk))
Given the async nature of sending messages, you might want to look into modules like p-iteration to ensure the chunks arrive in the correct order.
That being said, there exists APIs for getting lyrics of songs, which I would recommend instead of scraping. See apiseeds lyrics API as an example.
UPDATE
const lyrics = 'These are my lyrics';
const lyricsArr = lyrics.match(/.{1,8}/g);
console.log(lyricsArr); // [ 'These ar', 'e my lyr', 'ics' ]
lyricsArr.forEach((chunk, i) => {
// Break if this is the last chunk.
if (i == lyricsArr.length -1) {
return;
}
// If last character is not a space, we split a word in two.
// Add additional non-wordbreaking symbols between the slashes (in the regex) if needed.
if (!chunk[chunk.length - 1].match(/[ ,.!]/)) {
const lastWord = chunk.match(/\s([^ .]+)$/)
lyricsArr[i + 1] = lastWord[1] + lyricsArr[i + 1];
lyricsArr[i] = lyricsArr[i].split(/\s[^ .]*$/)[0];
}
})
console.log(lyricsArr) // [ 'These', 'are my', 'lyrics' ]
Updated as per the comments.
This is some crude code that i did not spend much time on, but it does the job.
Some info when using this approach:
You need to add any symbols that should not be considered wordbreaking to the regex in the second if
This has not been tested thoroughly, so use at your own risk.
It will definitely break if you have a word in the lyrics longer than the chunk size. Since this is around 2000, I imagine it will not be problem.
This will no longer ensure that the chunk length is below the limit, so change the limit to around 1900 to be safe
You can use .split( ) Javascript function.
word_list = lyrics.split(" ")
And word_list.length to access the number of words in your message and word_list[0] to select the first word for instance.

How to access 'Abbreviation' field of a custom list in NetSuite custom lists

I have a custom list that is used as a matrix option of Inventory item. Its 'Color'. This custom list has an abbreviation column. I am creating a saved search on item and using Color field(join) and trying to access 'Abbreviation' field of color.
Abbreviation on custom list is available on when 'Matrix Option List' is checked.
Can someone please help me achieve this? I tried to do this through script and it seems like we cannot access 'Abbreviation' column through script. I also tried to use script to write a search directly on 'Color' - custom list and get the 'abbreviation' through search columns. It did not work. Is there a way to access 'Abbreviation' from custom lists?
Thanks in Advance
You can access it via suitescript by using the record type "customlist" and the internal id of the list like so:
var rec = nlapiLoadRecord('customlist', 5);
var abbreviation = rec.getLineItemValue('customvalue', 'abbreviation', 1);
nlapiLogExecution('DEBUG', 'abbreviation', abbreviation);
Keep in mind that the third argument of getLineItemValue is the line number, not the internal ID of the item in the list. If you want to find a specifc line item, you may want to use rec.findLineItemValue(group, fldnam, value).
Unfortunately, it doesn't look like this translates to saved searches. The suiteanswer at https://netsuite.custhelp.com/app/answers/detail/a_id/10653 has the following code:
var col = new Array();
col[0] = new nlobjSearchColumn('name');
col[1] = new nlobjSearchColumn('internalid');
var results = nlapiSearchRecord('customlist25', null, null, col);
for ( var i = 0; results != null && i < results.length; i++ )
{
var res = results[i];
var listValue = (res.getValue('name'));
var listID = (res.getValue('internalid'));
nlapiLogExecution('DEBUG', (listValue + ", " + listID));
}
However, whatever part of the application layer translates this into a query doesn't handle the abbreviation field. One thing to keep in mind is that the 'custom list' record is basically a header record, and each individual entry is it's own record that ties to it. You can see some of the underlying structure here, but the takeaway is that you'd need some way to drill-down into the list entries, and the saved search interface doesn't really support it.
I could be wrong, but I don't think there's any way to get it to execute in a saved search as-is. I thought the first part of my answer might help you find a workaround though.
Here is a NetSuite SuiteScript 2.0 search I use to find the internalId for a given abbreviation in a custom list.
/**
* look up the internal id value for an abbreviation in a custom list
* #param string custom_list_name
* #param string abbreviation
* #return int
* */
function lookupNetsuiteCustomListInternalId( custom_list_name, abbreviation ){
var internal_id = -1;
var custom_list_search = search.create({
type: custom_list_name,
columns: [ { name:'internalId' }, { name:'abbreviation' } ]
});
var filters = [];
filters.push(
search.createFilter({
name: 'formulatext',
formula: "{abbreviation}",
operator: search.Operator.IS,
values: abbreviation
})
);
custom_list_search.filters = filters;
var result_set = custom_list_search.run();
var results = result_set.getRange( { start:0, end:1 } );
for( var i in results ){
log.debug( 'found custom list record', results[i] );
internal_id = results[i].getValue( { name:'internalId' } );
}
return internal_id;
}
Currently NetSuite does not allows using join on matrix option field. But as you mentioned, you can use an extra search to get the result, you could first fetch color id from item and then use search.lookupFields as follows
search.lookupFields({ type: MATRIX_COLOR_LIST_ID, id: COLOR_ID, columns: ['abbreviation'] });
Note: Once you have internalid of the color its better to use search.lookupFields rather than creating a new search with search.create.

Internet Explorer truncating flashVars containing JSON

This only happens in IE.
I'm using swfobject and loading the flash vars as such
var flashVars = {
myVar:'{"url":"http://google.com/", "id":"9999"}',
};
var params = {
allowFullScreen:"true",
wmode:"transparent",
allowScriptAccess:'always'
};
swfobject.embedSWF("mySwf.swf", "mySwf", "512", "318", "10.0.0", "./js/swfobject/expressInstall.swf", flashVars, params);
Everything works perfectly in all browser but IE. I checked myVar and it comes into the swf as { and that's it. I know it's dying at the '. I've tried putting a \ infront, then tried \\ and kept adding one slash until I got to \\\\\\\\. I even inverted all the slashes and tried the same ritual. Nothing.
I can get the string to finally come through, with inverted quotes and using double slashes, but then my JSON parser gets mad about there being slashes in my string.
Here's an example of what works, but of what is invalid JSON:
"{\\'url\\':\\'http://google.com/\\', \\'id\\':\\'9999\\'}"
Yep IE treats flashVars differently to all the other major browsers, I believe you need to make use of the JavaScript encodeURIComponent method which will escape all reserved characters from your String, eg:
// Removing all reserved characters from the flashVar value.
var flashVars = {
myVar: encodeURIComponent('{"url":"http://google.com/", "id":"9999"}'),
};
If you are passing multiple values in the flashVars then you could iterate through them and encode all chars in a single pass:
var flashVars = {
myVar: '{"url":"http://google.com/", "id":"9999"}',
anotherVar: 42
};
// Escape all values contained in the flashVars object.
for (key in flashVars) {
if (flashVars.hasOwnProperty(key)) {
flashVars[key] = encodeURIComponent(flashVars[key]);
}
}
As #dgmdan and #bcmoney suggested, it would probably make your code easier to read if you made use of JSON.stringify - however, you need to bear in mind that IE8 and below do not have a native JSON object, so you will need to include Crockford's JS Library in your HTML page.
// Making use of a JSON library.
var flashVars = {
myVar: encodeURIComponent(JSON.stringify({ url: "http://google.com/", id: "9999"})),
};
Also, it's worth bearing in mind that flashVars are limited to ~64k; so if you are planning to pass a lot of data, it might be better to use an ExternalInterface call to pull them from the JavaScript instead.
Try this to replace your first 3 lines:
var subVars = { url: "http://google.com/", id: "9999" };
var flashVars = { myVar: JSON.stringify(subVars) };

Resources