Calculating URL of an image resource - xpages

In my xpage which is bound to a domDoc datasource I want to build a preview mechanism for images stored in the datasource's richtext field. Designer and server are V 9.0.1.
The names of the attached images are stored as an array in a viewScope var and fed as source to a repeat control. The image control sits inside the repeat. I also put a link control alongside the image offering a means to download the file. The calculated url looks like this:
/dbpath/dbfile.nsf/xsp/.ibmmodres/domino/OpenAttachment/dbpath/dbfile.nsf/docunid/rtBodyField/filename.gif
Calculation of the link works perfect, but the images are never displayed. Instead Firebug tells me that the image's source url could not be resolved. And indeed I see that the db path part has been rendered twice before the /xsp/.ibmmodres/domino/OpenAttachment/ portion of the url (but only once after it!):
/dbpath/dbfile.nsf/dbpath/dbfile.nsf/xsp/.ibmmodres/domino/OpenAttachment/dbpath/dbfile.nsf/docunid/rtBodyField/filename.gif
Here's the code I'm using to calculate the source urls for both the link (using its value property) and the image (using its url property):
var unid=context.getUrlParameter('documentId');
var p=facesContext.getExternalContext().getRequestContextPath();
return p+'/xsp/.ibmmodres/domino/OpenAttachment'+p+'/'+unid + '/rtBodyFld/'+imgEntry;
Here's what I tried so far to solve that miracle:
a) calculate the db path (facesContext...) beforePageLoad, store it in a viewScope, then reference the viewScope when build the image's source ==> same result as above
b) used the image's value property instead of url ==> same result as above
c) use a standard html <img /> tag where the src argument is built using "#{javascript:...}" with the identical code as above ==> this works fine!
So I do have a workaround with solution c), but still I'd like to learn why the path element is doubled only in the first portion of the url, and only for image resources.
EDIT:
tried two more things:
d) pulled the image control outside my repeat then added a fixed (and valid) filename to the calculated url ==> same (bad) result as above
e) calculated the entire url portion only except the image file name beforePageLoad and stored that in a viewScope var ==> this is the weirdest result: outside the image viewscope contains the correct path info, but inside I see the same bad result as above. So it appears that inside the image the viewScope variable is altered in parts???
This is so weird that I feel I must have made a very simple and stupid error here, but what could that be?

Are you looking for how to calculate attachment URLs? Try this:
function getAttachmentURL(docID:java.lang.String, attachmentName:java.lang.String) {
var base = getBaseURL();
var middle = "/xsp/.ibmmodres/domino/OpenAttachment";
if (base.substr(0,4) == "/xsp") {
middle += base.substr(4);
} else {
middle += base;
}
var result = base + middle + "/" + docID + "/$File/" + attachmentName + "?Open";
return result;
}
function getBaseURL() {
var curURL = context.getUrl();
var curAdr = curURL.getAddress();
var rel = curURL.getSiteRelativeAddress(context);
var step1 = curAdr.substr(0,curAdr.indexOf(rel));
// Now cut off the http
var step2 = step1.substr(step1.indexOf("//")+2);
var result = step2.substr(step2.indexOf("/"));
return result;
}
Hope that helps!

Alright, it is as I feared: I had not taken into account that a relative url defined for an image control is automatically prefixed with /dbpath/dbfile.nsf/.
That's why that part always appeared twice no matter how I calculated my own url. It would have worked if I had used absolute urls (http://server/path/dbfile.nsf/...)
So instead the calculated url has to be built like that:
var unid=context.getUrlParameter('documentId');
var p=facesContext.getExternalContext().getRequestContextPath();
return '/xsp/.ibmmodres/domino/OpenAttachment' + p + '/' +
unid + '/rtBodyFld/' + imgEntry;

Related

pdfkit nodejs, one element per page from page 2

Im using pdfkit to generate pdf invoice.
When all my content fit in one page I have no issue.
However when it doesn't fit and need an extra page, I have a strange behaviour:
Instead of adding the elements in the second page, it only add one line and the rest of the page is blank.
Then on 3rd page I have another element, and the rest it blank, then 4th page, 5th etc.
Here is the code corresponding to this part:
for (let i = 0; i < data.items.length; i++) {
const item = data.items[i];
this.itemPositionY = this.itemPositionY + 20;
if (item.bio) this.containBioProduct = true;
let itemName = item.bio ? `${item.item}*` : item.item;
this.generateTableRow(
doc,
this.itemPositionY,
itemName,
"",
this.formatCurrency(item.itemPriceDf.toFixed(2)),
item.quantity,
this.formatCurrency(item.itemPriceTotalDf.toFixed(2))
);
this.generateHr(doc, this.itemPositionY + 15);
}
Basically I just iterate over an array of products. For each line my Y position has +20.
Thanks for your help.
In case someone has this issue, here is a solution:
Everywhere in the code I know that an extra page could be generated, I add this:
if (this.position > 680) {
doc.addPage();
this.position = 50;
}
It allows you to control the generation of new pages (instead of pdfkit doing it automatically with potential problems)
You just need to track the position from the initialization of "this.position".
In that way, evertime it's superior than an Y position (680 in my case, it's a bit less than a page with pdfkit), you just do "doc.addPage()", which will create another page, and you reinitialize your position to the beginning of the new page.

How do I get the filename programmatically to use with this code?

The code below is the computed image source for an image control on my custom control. If I run it just like it stands it puts in photo.jpg and works fine. The problem is I want to pass that value programmatically. I have an upload control (fileUpload1) and a download control (fileDownload1) on the custom control as well. I've tried document1.photo which is where the jpg file is stored. I've tried several variations of currentdocument.getitemvalue, getitem, getattachments. But, so far, I have found no way to do this. In addition, I have tried setting the value of filename to the value of the upload control and the download control with no success.
I know this is probably simple, but, as a client developer, it's not simple to me.
Any help would be greatly appreciated.
var doc:NotesDocument = currentDocument.getDocument ();
var UID:string = doc.getUniversalID().toString();
var fp:string = database.getFilePath();
var filename = "photo.jpg"
return '/0/' + UID + '/$file/' + filename
Thanks,
MJ
There may be other ways to achieve this but here's what I usually do:
with image controls I need to be aware that computed resource URLs are in most cases prepended with the database's relative path ending with the database filename. So all I have to add is the usual file attachment URL as in
/0/docUnid/$File/imageFileName
If there is a chance that you have more than one file attached to your doc I need to find out which one of them is the one I wish to display. This can be done by looping through
currentDocument.getAttachmentList(fieldName)
which delivers a java.util.List of NotesEmbeddedObject elements. Using methods like .getName() I then can find the one file you're looking for.
EDIT:
quick example using a repeat to display multiple images sattached to a single richtext field (there may be better solutions esp. regarding the way to filter unwanted file types; I'm using file extensions, only allowing .jpg, .png and .gif). Images are displayed as simple html list elements:
<ul>
<xp:repeat id="repeat1" rows="30" var="pic">
<xp:this.value><![CDATA[#{javascript:
var att:java.util.List=docCar.getAttachmentList("Body");
var it=att.listIterator();
//new empty array, to be used as the repeater's datasource
var arr=[];
//build base path for each image file
var unid=docCar.getDocument().getUniversalID();
var p="/0/" + unid + "/$File/";
while(it.hasNext()){
var e:NotesEmbeddedObject=it.next();
if(e.getName().endsWithIgnoreCase(".png") ||
e.getName().endsWithIgnoreCase(".jpg") ||
e.getName().endsWithIgnoreCase(".gif")){
//push complete filepath + image filename to array
arr.push(p + e.getName());
}
}
return arr;}]]></xp:this.value>
<li>
<xp:image url="#{javascript:pic}" id="image1">
</xp:image>
</li>
</xp:repeat>
</ul>
Hope this helps
Well, I think I have it figured out. Here is the code that now works:
var doc:NotesDocument = currentDocument.getDocument ();
var UID:string = doc.getUniversalID().toString(); var fp:string =
database.getFilePath();
var filename = #AttachmentNames()
return '/0/' + UID + '/$file/' + filename
MJ

how do we add url parameters? (EJS + Node + Express)

I understood how we parse the url parameters in express routes, as in the example
How to get GET (query string) variables in Express.js on Node.js?
But where do the url parameters come from in the first place?
EDIT:
Apparently, I can build such a query with jquery (i.e $.get). I can append params to this query object. It s cool, but still i m trying to understand how we achieve this in the query that renders the page as a whole.
An example : when i choose the oldest tab below, how does SO add ?answertab=oldest to the url so it becomes :
https://stackoverflow.com/questions/30516497/how-do-we-add-url-parameters-ejs-node-express?answertab=oldest#tab-top
The string you're looking at is a serialization of the values of a form, or some other such method of inputing data. To get a sense of this, have a look at jQuery's built in .serialize() method.
You can construct that string manually as well, and that's pretty straight forward as well. The format is just ?var1=data1&var2=data2 etc. If you have a JSON object {"name": "Tim", "age": 22} then you could write a very simple function to serialize this object:
function serializeObject(obj) {
var str = "?";
for(var i = 0; i < Object.keys(obj).length; i++) {
key = Object.keys(obj)[i];
if (i === Object.keys(obj).length - 1)
str += encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]);
else
str += encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]) + "&";
}
return str;
}
Running seralizeObject({"name": "Tim", "age": 22}) will output '?name=Tim&age=22'. This could be used to generate a link or whatnot.
The page author writes them so. This is how they "come in the first place". The authors of an HTML page decide (or are told by website designers) where to take the user when he clicks on a particular anchor element on it. If they want users to GET a page with some query parameters (which their server handles), they simply add query string of their choice to the link's href attribute.
Take a look at the href attribute of the oldest tab you clicked:
<a
class="youarehere"
href="/questions/30516497/how-do-we-add-url-parameters-ejs-node-express?answertab=oldest#tab-top"
title="Answers in the order they were provided"
>
oldest
</a>
When you clicked it, the browser simply took you to path indicated in href attribute /questions/30516497/how-do-we-add-url-parameters-ejs-node-express?answertab=oldest#tab-top relative to the base URL http://stackoverflow.com. So the address bar changed.
stackoverflow.com may have its own system of generating dynamic HTML pages. Their administrators and page authors have configured their server to handle particular query parameters and have put in place their own methods to make sure that links on their pages point to the URL(including query string) they wish.
You need to provide URIs with query strings of your choice (you can build them using url.format and querystring.stringify) to your template system to render. Then make your express routes process them and generate pages depending on their value.

Pagination in ListViewByQuery web part

I have written a web part that use ListViewByQuery to display the items based on query provided. Everything work perfect except pagination.
When I specify rowLimit it is displaying me only first set of record and pagination control is not visible so I could not able to move to next set of records.
The problem is that when you click on the paged button 1-2 it will postback adding some values in query string.
I accidentally remove the view=. query string from the URL and hit enter to get the results.
So what I did is as follows
if (!string.IsNullOrEmpty(Request.QueryString["View"]))
{
string queryString = string.Empty;
foreach (string key in Request.QueryString.Keys)
{
if (key.ToLower() != "view")
queryString += key + "=" + Request.QueryString[key] + "&";
}
SPUtility.Redirect(Request.Url.GetLeftPart(UriPartial.Path), SPRedirectFlags.Default, HttpContext.Current,queryString);
return;
}
Great answer Muhammad - after removing the view key and value, the link works fine.
However the line SPUtility.Redirect(.. didn't work for me.
Instead, as I put your code inside CreateChildControls() I used:
this.Context.Response.Redirect(this.Context.Request.Url.GetLeftPart(UriPartial.Path) + "?" + queryString);

How can I set the default value in a SharePoint list field, based on the value in another field?

I have a custom list in SharePoint (specifically, MOSS 2007.) One field is a yes/no checkbox titled "Any defects?" Another field is "Closed by" and names the person who has closed the ticket.
If there are no defects then I want the ticket to be auto-closed. If there are, then the "Closed by" field ought to be filled in later on.
I figured I could set a calculated default value for "Closed by" like this:
=IF([Any defects?],"",[Me])
but SharePoint complains I have referenced a field. I suppose this makes sense; the default values fire when the new list item is first opened for entry and there are no values in any fields yet.
I understand it is possible to make a calculated field based on a column value but in that case the field cannot be edited later.
Does anyone have any advice how to achieve what I am trying to do?
Is it possible to have a "OnSubmit" type event that allows me to execute some code at the point the list item is saved?
Thank you.
Include a content editor web part in the page (newform.aspx / editform.aspx) and use jQuery (or just plain javascript) to handle the setting of default values.
Edit: some example code:
In the lists newform.aspx, include a reference to jquery. If you look at the html code, you can see that each input tag gets an id based on the field's GUID, and a title that's set to the fields display name.
now, using jquery we can get at these fields using the jQuery selector like this:
By title:
$("input[title='DISPLAYNAMEOFFIELD']");
by id (if you know the field's internal guid, the dashes will ahve to be replaced by underscores:
// example field id, notice the guid and the underscores in the guid ctl00_m_g_054db6a0_0028_412d_bdc1_f2522ac3922e_ctl00_ctl04_ctl15_ctl00_ctl00_ctl04_ctl00_ctl00_TextField
$("input[id*='GUID']"); //this will get all input elements of which the id contains the specified GUID, i.e. 1 element
We wrap this in the ready() function of jQuery, so all calls will only be made when the document has fully loaded:
$(document).ready(function(){
// enter code here, will be executed immediately after page has been loaded
});
By combining these 2 we could now set your dropdown's onchange event to the following
$(document).ready(function(){
$("input[title='DISPLAYNAMEOFFIELD']").change(function()
{
//do something to other field here
});
});
The Use jQuery to Set A Text Field’s Value on a SharePoint Form article on EndUserSharePoint.com shows you how to set a default value for a field using JavaScript/jQuery.
They also have a whole series of articles on 'taming calculated columns' that will show you many more powerful options you have for calculated fields with the use of jQuery.
One thing to be aware of when inserting JavaScript into a SharePoint page and modifying the DOM is support. There is a small chance that a future service pack will break the functionality you add, and it is quite likely that the next version of SharePoint will break it. Keeping this mind however, I believe it's a good solution at this time.
I've got a walk through with sample code that may help
Setting a default duration for new calendar events
It sets the End Time/Date fields to Start Time + 1.5 hours when you create a new event.
Its complicated a little by the steps need to do the time/date work, but you'll see examples of how to find the elements on the form and also one way to get your script onto the newform.aspx without using SPD.
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js">
</script>
<script type="text/javascript">
// Set the hours to add - can be over 24
var hoursToAdd = 1;
// Mins must be 0 or div by 5, e.g. 0, 5, 10, 15 ...
var minutesToAdd = 30;
// JavaScript assumes dates in US format (MM/DD/YYYY)
// Set to true to use dates in format DD/MM/YYYY
var bUseDDMMYYYYformat = false;
$(function() {
// Find the start and end time/minutes dropdowns by first finding the
// labels then using the for attribute to find the id's
// NOTE - You will have to change this if your form uses non-standard
// labels and/or non-english language packs
var cboStartHours = $("#" + $("label:contains('Start Time Hours')").attr("for"));
var cboEndHours = $("#" + $("label:contains('End Time Hours')").attr("for"));
var cboEndMinutes = $("#" + $("label:contains('End Time Minutes')").attr("for"));
// Set Hour
var endHour = cboStartHours.attr("selectedIndex") + hoursToAdd;
cboEndHours.attr("selectedIndex",endHour % 24);
// If we have gone over the end of a day then change date
if ((endHour / 24)>=1)
{
var txtEndDate = $("input[title='End Time']");
var dtEndDate = dtParseDate(txtEndDate.val());
if (!isNaN(dtEndDate))
{
dtEndDate.setDate( dtEndDate.getDate() + (endHour / 24));
txtEndDate.val(formatDate(dtEndDate));
}
}
// Setting minutes is easy!
cboEndMinutes.val(minutesToAdd);
});
// Some utility functions for parsing and formatting - could use a library
// such as www.datejs.com instead of this
function dtParseDate(sDate)
{
if (bUseDDMMYYYYformat)
{
var A = sDate.split(/[\\\/]/);
A = [A[1],A[0],A[2]];
return new Date(A.join('/'));
}
else
return new Date(sDate);
}
function formatDate(dtDate)
{
if (bUseDDMMYYYYformat)
return dtDate.getDate() + "/" + dtDate.getMonth()+1 + "/" + dtDate.getFullYear();
else
return dtDate.getMonth()+1 + "/" + dtDate.getDate() + "/" + dtDate.getFullYear();
}
</script>

Resources