|
11/2/2009
New features added
List reporting by view
In first release you couldn't just iterate list items from any wanted list. It was a must to have a parent child relation (lookup relation) in place. In this release we implemented a feature which lets you to ignore current context/parent on Repeat tag allowing you to iterate items from any list even if you don't have any relation in place. So, from now on you can insert any list you want in your reports and more over you can choose a view and report only those items that are filtered by selected view.
For example if you want to have a report of last month orders (which are stored in SharePoint list) all you need to do is to prepare a template with a table and a Repeat tag set on it with following attributes: list set to Orders, view set to Last Month Orders (name of the view), ignoreParent set to true.
As these kinds of reports don't have current context you don't need to provide list name and item ID in the URL when calling generator. That means you can call generator from any hyperlink you want (or from custom actions of course). It can be for example a HREF in CWEP or a link in Quick Launch and etc.
Filtering child items by view
Also you can use view attribute of Repeat tag for filtering child items by the view defined in the child list. Imagine you have Tasks items list related to Projects list and your want to generate report about Project plus you want show all not finished Tasks in the same report for particular Project. You need to create a view for not finished tasks, and then set Repeat tag attribute view to the name of the created view.
We hope you will like these new features. If you didn't download our free version yet you still can download it here. The limitation of free version is left the same: 3 items limitation in Repeat tag.
We are waiting for your feedback!
10/11/2009
You may need to redirect from NewForm.aspx to DispForm.aspx (or other page) of newly created item when creating SharePoint solutions like Invoices and Orders management. There are a couple of blogs out there that already have a solution for this. Because at the moment of item creation there is no item ID yet all we can do is to redirect to other page where we can query SharePoint list using web services for the last created item by current user. Then we can just redirect to wanted page using queried item ID.
I can't remember already where the idea came from first so I just write down how I'm doing this in my SharePoint solutions. This time I'll going to use only SPAPI no need to use jQuery or SharePoint Designer.
-
Edit NewForm.aspx by adding CEWP to it with following code: // This function is executed before save in SharePoint new and edit forms
function PreSaveAction() {
var srcUrl = GetSource();
var pathparts = document.location.pathname.split('/');
//you may need to modify this depending on where you site is located
var url = 'http://' + document.location.host + '/' + pathparts[1] + '/';
var newSrcUrl = url + "Custom Pages/NewFormRedirect.aspx";
var i = aspnetForm.action.lastIndexOf(srcUrl);
//changing Source parameter before form submit. SharePoint will redirect to new Source parameter provided by us after form submit.
aspnetForm.action = aspnetForm.action.substring(0, i) + newSrcUrl;
return true;
}
-
Create document library called Custom Pages.
-
Create Web Part page called NewFormRedirect.aspx with full page vertical layout in Custom Pages library.
-
Add CEWP to NewFormRedirect.aspx with the following code in it: //Set up SPAPI (in my case I have installed SPAPI in Layouts directory)
<script src="/_layouts/SPAPI/SPAPI_Core.js" type="text/javascript"></script>
<script src="/_layouts/SPAPI/SPAPI_Lists.js" type="text/javascript"></script>
<script type="text/javascript">
//You may need do modify this function depending where your site collection is located
function GetRootUrl() {
var pathparts = document.location.pathname.split('/');
var url = 'http://' + document.location.host + '/' + pathparts[1] + '/';
return url;
}
var lists = new SPAPI_Lists(GetRootUrl())
//quering last created item by current user
//i'm using _spUserId to get current user
var items = lists.getListItems(
'Orders', // listName
'', // viewName
'<Query><OrderBy><FieldRef Name="Modified" Ascending="FALSE"></FieldRef></OrderBy><Where><Eq><FieldRef Name="Author" LookupId="TRUE"/><Value Type="Lookup">' + _spUserId + '</Value></Eq></Where></Query>', // query
'<ViewFields><FieldRef Name="ID"/></ViewFields>', // viewFields
'1', // rowLimit
'<QueryOptions><IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns></QueryOptions>' // queryOptions
);
if (items.status == 200) {
var rows = items.responseXML.getElementsByTagName('z:row');
var lastID = rows[0].getAttribute('ows_ID');
//redirect to wanted page using quered last item ID
//here i'm redirecting to DispForm.aspx of newly cerated item
var url = GetRootUrl() + "Lists/Orders/DispForm.aspx?ID=" + lastID + "&Source=%2FLists%2FOrders%2FAllItems%2Easpx"
document.location.href = url;
}
</script>
Also check out Mark's solution of doing this with the help of SPServices library. 9/30/2009
Continuing my last post I will show some JavaScript that can be used to implement cascading drop down lists in SharePoint. Assume you have Products list and Categories list. Products have a lookup column to the Categories list. And we have Order line list which have lookup columns to Products list and to Categories list. When creating new Order line item we want that when Category is select then the Product combo box must be filtered to show only selected category products:
Again jQuery, SPAPI and CEWP comes to help. The only problem is that SharePoint renders combo box as INPUT or SELECT control depending on how much items it have. The following script has workaround for it (see comments inside): //Set up jQuery and SPAPI (in my case I have installed jQuery and SPAPI in Layouts directory)
<script src="/_layouts/jquery-1.3.2.js" type="text/javascript"></script>
<script src="/_layouts/SPAPI/SPAPI_Core.js" type="text/javascript"></script>
<script src="/_layouts/SPAPI/SPAPI_Lists.js" type="text/javascript"></script>
<script type="text/javascript">
//You may need do modify this function depending where your site collection is located
function GetRootUrl() {
var pathparts = document.location.pathname.split('/');
var url = 'http://' + document.location.host + '/' + pathparts[1] + '/';
return url;
}
lastSelectedCategory = 0;
//filter product by selected catagory
function FilterProducts() {
//get Product control (it can be INPUT or SELECT)
var productElement = $("select[title='Product']");
if (productElement.length == 0) { productElement = $("input[title='Product']"); }
if (productElement.length == 0) return;
//get Product Category control (it can be INPUT or SELECT)
var categoryElement = $("select[title='Product Category']");
if (categoryElement.length == 0) { categoryElement = $("input[title='Product Category']"); }
if (categoryElement.length == 0) return;
//get selected Product Category value
var category = '';
if (categoryElement[0].optHid) { category = document.getElementById(categoryElement[0].optHid).value; }
else { category = categoryElement[0].value; }
if (lastSelectedCategory != category) {
var foundedSelectedProductID = '';
var foundedSelectedProduct = '';
//get sletected Category Products
var lists = new SPAPI_Lists(GetRootUrl());
var items = lists.getListItems(
'Products', // listName
'', // viewName
'<Query><Where><Eq><FieldRef Name="Product_x0020_Category" LookupId="TRUE"/><Value Type="Lookup">' + category + '</Value></Eq></Where></Query>', // query
'<ViewFields><FieldRef Name="ID"/><FieldRef Name="Title"/></ViewFields>', // viewFields
'', // rowLimit
'<QueryOptions><IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns></QueryOptions>' // queryOptions
);
if (items.status == 200) {
var rows = items.responseXML.getElementsByTagName('z:row');
var options = '';
//format Products values for INPUT control
if (productElement[0].optHid) {
var productSelected = document.getElementById(productElement[0].optHid).value;
for (var i = 0; i < rows.length; i++) {
var productID = rows[i].getAttribute('ows_ID');
if (productID == productSelected) { //trying to select the same product in new category if it exists
foundedSelectedProductID = productID;
foundedSelectedProduct = rows[i].getAttribute('ows_Title');
}
options += rows[i].getAttribute('ows_Title') + '|' + productID + '|';
}
productElement[0].choices = options.substring(0, options.length - 1);
productElement[0].value = foundedSelectedProduct;
productElement[0].match = foundedSelectedProduct;
document.getElementById(productElement[0].optHid).value = foundedSelectedProductID;
} else {
//format Products for SELECT control
var productSelected = productElement[0].value;
for (var i = 0; i < rows.length; i++) {
var productID = rows[i].getAttribute('ows_ID');
if (productID != productSelected) {//trying to select the same product in new category if it exists
options += '<option value="' + productID + '">' + rows[i].getAttribute('ows_Title'); +'</option>';
} else {
options += '<option selected="selected" value="' + productID + '">' + rows[i].getAttribute('ows_Title'); +'</option>';
}
}
productElement.html(options);
}
}
lastSelectedCategory = category;
}
}
$(document).ready(
function() {
//FilterProducts(); //uncomment if you want Products be filttered on startup;
// Filter products on Category change (Category also can be INPUT or SELECT)
var categoryElement = $("select[title='Product Category']");
if (categoryElement.length == 0) { categoryElement = $("input[title='Product Category']"); }
if (categoryElement.length != 0) {
if (categoryElement[0].optHid) {
$("input[id='" + categoryElement[0].optHid + "']").bind("propertychange", function() { FilterProducts(); });
} else {
$("select[title='Product Category']").change(function() { FilterProducts(); });
}
}
// End Change Product Category
}
);
Using this technique you can implement as many cascading levels as you want and you can filter cascading lists by any CAML query you want. 9/23/2009
In my last post I showed one way of how to establish parent – child relations in SharePoint. Sometimes there is a need to pickup default columns values from related list. For example in my Sales Orders Management application I want to choose Product from a list when creating new Order or Invoice. But I can't just have a lookup column to the chosen Product because over time Product price and name can change and I need to have price and Product name fixed as it was at the time the Order or Invoice was created. So I need to copy price and name values from the chosen Product item to the Order details item.
It is possible to achieve this using just JavaScript (jQuery and SPAPI) and CEWP (Content Editor Web Part). All you need to do is add CEWP to your page (in my example I added it to the Order view page) and add the following code (see comments inside):
//Set up jQuery and SPAPI (in my case I have installed jQuery and SPAPI in Layouts directory)
<script src="/_layouts/jquery-1.3.2.js" type="text/javascript"></script>
<script src="/_layouts/SPAPI/SPAPI_Core.js" type="text/javascript"></script>
<script src="/_layouts/SPAPI/SPAPI_Lists.js" type="text/javascript"></script>
<script type="text/javascript">
//You may need do modify this function depending where your site collection is located
function GetRootUrl()
{
var pathparts = document.location.pathname.split('/');
var url = 'http://' + document.location.host + '/' + pathparts[1] + '/';
return url;
}
function ChangeProduct() {
// get Product selection control
var klElement = $("select[title='Product']");
if (klElement.length == 0) { klElement = $("input[title='Product']"); }
if (klElement.length == 0) return;
// get selected Product ID
var kl = '';
if (klElement[0].optHid) { kl = document.getElementById(klElement[0].optHid).value; }
else { kl = klElement[0].value; }
JSRequest.EnsureSetup();
// query Products list to get selected Product
var lists = new SPAPI_Lists(GetRootUrl())
var items = lists.getListItems(
'Products', // listName
'', // viewName
'<Query><Where><Eq><FieldRef Name="ID"/><Value Type="Counter">'+kl+'</Value></Eq></Where></Query>', // query
'<ViewFields><FieldRef Name="Item_x0020_Number"/><FieldRef Name="Price"/><FieldRef Name="ID"/><FieldRef Name="Title"/></ViewFields>', // viewFields
'', // rowLimit
'<QueryOptions><IncludeMandatoryColumns>FALSE</IncludeMandatoryColumns></QueryOptions>' // queryOptions
);
if (items.status == 200)
{
var rows = items.responseXML.getElementsByTagName('z:row');
if (rows.length > 0){
// set Order details item's Title, Item Number and Price to the coresponding values of selected Product
$("input[title='Title']").val(rows[0].getAttribute('ows_Title'));
$("input[title='Item Number']").val(rows[0].getAttribute('ows_Item_x0020_Number'));
// you may need to chanche this depending on yout local currency format
$("input[title='Price']").val(Number(rows[0].getAttribute('ows_Price')).toFixed(2).replace(/\./,"."));
}
}
}
$(document).ready(
function()
{
// if Order details fields is still empty, set them to the Product selected by default
if ($("input[title='Title']").val()=='') { ChangeProduct(); }
// Find Product control (where ID of selected Product is stored)
// SharePoint renders combobox control as SELECT until items number is below 20
// After that control is rendered as INPUT. As you can see I have a quick workaround for this.
var klElement = $("select[title='Product']");
if (klElement.length == 0) { klElement = $("input[title='Product']"); }
if (klElement.length == 0) return;
// attach to the propertychange event
if (klElement[0].optHid)
{
$("input[id='"+klElement[0].optHid+"']").bind("propertychange", function () { ChangeProduct(); });
} else {
$("select[title='Product']").change(function () { ChangeProduct(); });
}
}
);
</script>
Same way can be implemented so wanted cascading drop down lists in SharePoint. 9/17/2009
I want to show you one of the ways how to quickly establish parent – child relationship in SharePoint. By using this way you will be able to add child items on the parent item view page as shown in the image below:
The idea how to add new child item form is borrowed from OOTB SharePoint Blog application template. By investigating how comments are implemented on Post.aspx page I was able to reuse OOTB SharePoint:SubmitCommentButton control which is used by BlogCommentsForm SharePoint:RenderingTemplate. BlogCommentsForm rendering template is defined in Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\CONTROLTEMPLATES\DefaultTemplates.ascx file. So here are quick steps how to achieve this:
-
First you need a parent and child lists. In my example I have Orders and Oder details lists. In child list you need to create a lookup column to parent list named PostTitle (you must use this name because this is the name of column which is used by OOTB SubmitCommentButton functionality). It is important to choose ID column in the "In this column" combo box. You can hide PostTitle lookup column if you want.
-
Now using SharePoint Designer 2007 open your site and navigate to the parent list folder. Copy DispForm.aspx and rename the copy CustomDispForm.aspx (you can choose whatever name you want).
-
In the CustomDispForm.aspx replace ListFormWebPart with DataFormWebPart.
-
Add new ListFormWebPart for the new child list item form. Choose New item form in List or Document Form dialog.
-
Add child list Web Part for displaying all child list items.
-
Set CustomDispForm.aspx to be the parent list default display form.
-
In the DefaultTemplates.ascx find SharePoint:RenderingTemplate with ID="BlogCommentsForm". Select and copy all SharePoint:RenderingTemplate tag.
-
Create new file SubmitNewProduct.ascx and paste the code you copied in step 7 (you can choose another file name).
-
Find SharePoint:SubmitCommentButton tag and modify Text attribute to whatever you want (in my example I will use "Add Product"). Save the file in the same directory as DefaultTemplates.ascx.
-
Using SharePoint Designer 2007 open CustomDispForm.aspx created in step 2. Find ListFormWebPart added in step 4. Change TemplateName tag to a name you named the file in step 8.
-
Now when you open Order (parent list item) you already can add new child items. But last web part on page still displaying not filtered child items list. All you need to do is Edit the page and modify last web part by setting the following filter: PostTitle is equal to [ID].
In the next post I show how to implement a child item selection from another list. If you noticed in the image above Order details item have a lookup column name Product. By selecting Product automatically are filled child item fields. In others solutions it may be enough to have just a lookup column, but in my case I need to fix product details (name, price and etc.) at the time of Order or Invoice creation because later on product price may change. 9/6/2009
Did you ever wanted or tried to assembly a document by using data stored in SharePoint? Invoices, legal forms, memos, or letters for customers are just few document examples that are used every day. Microsoft Word 2007 provides a way to embed information from SharePoint document library metadata columns into Word document by using Quick Parts. Besides that there are some unexpected behavior when using Quick Parts with lookup, calculated or multi valued columns there are also no way to insert information from related lists by lookup columns. For example, image that you have purchase orders SharePoint document library where customer is defined as a lookup column to the customers list and you want to have customer's shipping address in the purchase order's document. Or, more over you want to iterate related items from the orders details list to purchase order's document details table. So what can be done in such situations?
I'm happy to announce we are releasing our first SharePoint Document Generator release. Using SharePoint Document Generator you can easy assembly documents from SharePoint lists using well know Microsoft Word 2007 templates.
SharePoint Document Generator template example
SharePoint Document Generator uses Custom XML schema to define which fields/columns values to embed in to the document. All fields values will be displayed the same as they are displayed in SharePoint lists. Great news is that you can insert lookup columns and set which column's value to take from the referenced list. And of course you can insert a repeat tag and set its properties to iterate whatever child list items you want. It is done with a couple of simply steps: select a table row, insert Repeat tag, set child list name you want to iterate. And that's it!
SharePoint Document Generator can be called from any SharePoint custom action or JavaScript. Generated documents can be returned directly to the browser or saved to SharePoint Document Library.
Custom actions in the Edit Control Block (ECB) menu and items display form.
You can download SharePoint Document Generator Free version and try it by yourself. The only limitation of free version is maximum of 3 rows can be iterated from lists related by lookup columns. Check out our demo video how to start using SharePoint Document Generator.
|
|
|
|