Showing posts with label software. Show all posts
Showing posts with label software. Show all posts

Friday, 1 August 2014

Difficulties overriding XLV web parts with external stylesheets in SharePoint libraries

Overriding an XSLT List Web Part runs into some limitations when using an external stylesheet with the XslLink property. The problem is that the ECB menu (which comes up when you click the arrow next to a list item name or title) is broken. Also, many of the item related ribbon buttons under the Documents tab are greyed out. The combination of those two problems makes for a very crippled web part which is good for not much more than displaying the list items.
Reproducing the problem is more specific than I've suggested. Only external stylesheets located in the SharePoint content database have these problems. In other words, stylesheets located in one of your document libraries (such as the root site's Style Library) break the ECB and the ribbon. Stylesheets located on the file system in the _layouts folder do not. This is all explained in some detail by Glyn Clough in his SharePoint blog - XLV Bug: XSL Link Property and the ECB Menu.
I've done a bit of my own debugging and found that the XslLink problem affects the ECB menu and the ribbon whether you use a fully qualified absolute URL, a server relative URL, or a page relative URL. The ECB menus and ribbon controls are rendered as a result of respective AJAX calls to obtain HTML to add to the DOM node tree. This includes script source​, which is executed by the browser. It's a rather clunky and insecure way to do it, if you ask me, and it may even be vulnerable to cross-site scripting exploits. Nevermind that. Long story short, when the XSLT stylesheet is in the web part definition in the page itself, no problem. When the stylesheet is external an in the content database, the SharePoint application page /_layouts/inplview.aspx is unable to create the output required.
For the ECB menu, the URL would look something like this:
http://[my host]/[path to my site]/_layouts/inplview.aspx?Cmd=Ctx&List={GUID of list}&View={GUID of view}&ViewCount=1&IsXslView=TRUE&Field=LinkFilename&ID=5&ListViewPageUrl=[URL of my page]
Plugging that URL into a browser results in the following response:
<div class="ms-vb">Unable to display this Web Part. To troubleshoot the problem, open this Web page in a Microsoft SharePoint Foundation-compatible HTML editor such as Microsoft SharePoint Designer. If the problem persists, contact your Web server administrator.</div>
<br/><br/>Correlation ID:[correlation ID GUID]<br/>
The javascript doesn't find what it's looking for in the HTML source of that response, and the result is a canned alert box displaying the unhelpful message "This item is no longer available.  It may have been deleted by another user.  Click 'OK' to refresh the page."
It turns out there are a couple decent workarounds to this problem with the ECB menu apart from keeping the XSLT in the page. The simplest workaround is to edit the web part in the browser and tick "Enable Asynchronous Update" under AJAX Options in the web part properites. This causes the ECB menu to be loaded in a different way that does not rely on the bad call to inplview.aspx. What other side effects it may have, desirable or otherwise, I'm not sure.
Another good workaround that avoids editing the web part properties is this one, discovered by Henk Haarsma: XSLT in sandbox solution. This technique uses javascript in the XSLT code to change the URL in the AJAX call so that the isXslView parameter is removed. That parameter does not seem to be needed in this call. When the call is working because the conditions for the bug are not present, the removal of the query string parameter makes no difference to the output.
Neither of these workarounds solve the ribbon problem. Encouraged by the success of the "Enable Asynchronous Update" solution, I tried all sorts of other settings in the web part properties. Nothing made a difference. The javascript solution prompted my to try the same thing in another part of the stylesheet that controls the URL to the AJAX call for the ribbon settings. That URL looks something like this:
http://[my host]/[path to my site]/_layouts/inplview.aspx?List={GUID of list}&View={GUID of view}&ViewCount=1688&ListViewPageUrl=[URL of my page]&IsXslView=TRUE&Cmd=EcbView
It shows the same output as the broken ECB call and removing the isXslView query string parameter does seem to fix it; however, when the call is working properly the output with the isXslView parameter is very different. So that's not a solution for the ribbon.
There is, however, hope. The two calls seem to fail for different reasons, or in different ways. There is another way to pull in an external stylesheet. You can use a very simple inline stylesheet in the web part's Xsl property that does nothing more than an xsl:include of the external file. It looks something like this:
<xsl:stylesheet xmlns:x="http://www.w3.org/2001/XMLSchema" xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" version="1.0" exclude-result-prefixes="xsl msxsl
ddwrt" xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime" xmlns:asp="http://schemas.microsoft.com/ASPNET/20"
xmlns:__designer="http://schemas.microsoft.com/WebParts/v2/DataView/designer" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-
microsoft-com:xslt" xmlns:SharePoint="Microsoft.SharePoint.WebControls" xmlns:ddwrt2="urn:frontpage:internal" xmlns:o="urn:schemas-microsoft-com:office:office"
ddwrt:ghost="show_all">
<xsl:include href="[path to external XSL file]"/>
</xsl:stylesheet>
An xsl:import would give identical results in this case. Using this method had no effect on the rendering of the ECB menu; however, with an absolute URL supplied the ribbon does work! So you can use one of the two methods I described earlier to enable the ECB menu combined with this method for solving the ribbon problem for a fully functioning web part. It is unfortunate to have to use an absolute URL, but at least that shows it's possible. Debugging with .Net Reflector shows that strange things are happening when SharePoint is trying to resolve the URL to the stylesheet in the ribbon AJAX call, and it fails in different ways depending on how the external file is referenced. I'm hoping that with a bit more digging I'll be able to find a workaround for page relative and server relative URLs as well.

Tuesday, 13 September 2011

SharePoint 2010 UI: custom action to launch "Move To" for a list item

Mood = :proud:

I've worked out how to create a custom menu action in a SharePoint 2010 list view that allows a user to move a single file (with meta-data intact) to another location similar to the functionality available in Site Content and Structure. In fact, I have made use of the Site Content and Structure page directly within a modal dialog. You can use SharePoint Designer to create a custom list item menu action. I won't go into detail here on how to do that as there is plenty of information available on the web. In the "Create Custom Action" dialog, tick the radio button next to "Navigate to URL:" and paste in the following javascript URL all on a single line (you will need to change the webId value to your own):

javascript:var listId='{ListId}'.toLowerCase().replace(/[{}]/g, ""); var webId='4836a39d-e154-4822-8826-8bcd5029cad2'; var dlg=SP.UI.ModalDialog.showModalDialog({ url: "{SiteUrl}/_Layouts/sitemanager.aspx?FilterOnly=1&SmtContext=SPList:" + listId + "?SPWeb:" + webId + ":&SmtContextExpanded=True&Filter=1&pgsz=100&vrmode=False", dialogReturnValueCallback: function(res, val) {location.reload(true);}}); var idString="SPListItem:{ItemId}?SPList:" + listId + "?SPWeb:" + webId; var item; var func; var doc; var win; setTimeout(func = function() {for (var i=0; i < window.top.frames.length; i++) {if (window.top.frames[i].location.href.indexOf("sitemanager.aspx") != -1) {win = window.top.frames[i]; doc = win.document;}}; if (doc == null || doc.forms.length == 0) {return setTimeout(func, 5000);}; for (var i=0; i < doc.forms[0].elements.length; i++) {var elem=doc.forms[0].elements[i]; if (elem.type == 'checkbox' && elem.id == idString) {item = elem; break;}}; if (item == null) {return setTimeout(func, 500);}; win.SmtSingleObjectMove(idString+":", false); item.checked=true; win.SmtGridSelectItem(item);}, 100); return false;


I had to use some setTimeout calls in order to ensure that the objects I was referring to were present. There is a particularly long timeout of 5 seconds that allows the dialog to hopefully complete rendering before it attempts to launch the popup with the tree view that actually selects the destination. All efforts to encapsulate this in the dialog's onload function met with failure. Perhaps one day I'll work that one out. Closing the dialog will refresh the view. This should work for any list or library in your SharePoint web application. Unfortunately there is no URL token corresponsing to the guid of the web application, so this will need to be provided as a hard-code value as I have done. I hope this is helpful to someone, as it took quite a lot of trial and error.

Wednesday, 7 September 2011

SharePoint 2010 - weirdness with custom list item edit form

I don't like SharePoint very much, but I'm forced to work with it in my software development role. A lot of what you might think of as simple functionality that could be expected out-of-the-box in a commercial content management system actually requires custom coded solution. Okay, I can do that. The trouble is though that the IT department at my company are quite strict about the provisioning of custom software. Given how buggy SharePoint Services is, I can't say I'm terribly surprised. The upshot is that any custom coded solution is discouraged in favour of some third party alternative if available. And either way, it needs to be justified. I tend not to bother with custom solutions. I often encounter weird problems with the out-of-the-box functionality though. Here's one.

I have a requirement to allow designated people to move a document from a staging document library to a permanent location once the document has been approved. After lots of mucking around I've found that the simplest way to do this is to create a custom edit form that allows input of only one item property. I've decided to use the Source URL property, since this is not actually useful in the context of the library. The plan is that there will be a workflow that kicks off whenever the document properties are changed and if there is a value for Source URL, the workflow will move the document to within that path (if legal) and then terminate. As an aside, I really wish the standard list views gave access to the file move functionality. You can't move a file from with a library, you can only send it to another location as a copy. But that's only one from an ever growing list of gripes, and not a very important one.

So anyway, I've found that this can work. However, when I (or the user) submits the custom edit form (which by the way cannot open as a dialog like the built in forms do), the result is that the document is output by the browser as an attachment. This causes a download dialog to open. I really don't want that to happen. It is confusing, to say the least. If I put the IsDialog=1 query string variable on the URL, this does not happen; however, I can't control that from actions and it shouldn't open as a dialog anyway. Also, the form does not close itself and redirect to the list as I'd expect it to. This seems to be a problem only with the custom edit forms. The default EditForm.aspx does not exhibit this strange behaviour, whether or not it is in dialog mode. I can't find anything about it on teh interwebs, so I thought I'd be the first to report it. If anything comes back, I'll be very surprised though.

Friday, 7 January 2011

How to get files off an unfinalized DVD-Video disk using free software

Hey, happy new year! This is a bit of a techie experience transfer. I don't always write blog posts of a political nature. Actually, I don't often write blog posts, but here's one. I wanted to share with you my experiences of trying to extract content from an unfinalized video DVD that was recorded with a consumer DVD recorder (brand unknown, not in my possession). A friend was kind enough to send us by post some DVDs with material recorded off the television. [Now before you get all sanctimonious with me about copyright and IP and all that jazz, let me say that I fully intend to buy the DVD of this series when it is available. It is not currently available. I could get this series on my own Virgin Media TV package if I upgrade, but I don't want to commit to another fixed term contract. And I'm not going to tell you the name of the show, so nyuh.*] Anyway, our kind friend sent us these DVDs but unfortunately they were not finalized, which means they can only be played in the same video recorder. This is an easy mistake to make. Although this friend claims that the DVDs were finalized, she has been misinformed as I shall show you below. So for the last couple days I set about trying to create workable DVD disks from the contents of the ones I was given. This is tricky, since an unfinalized DVD video disk is left in a state where it is virtually unreadable without specialist software. Such a DVD will not load in a commercial DVD deck, nor will it mount on any operating system. I tried Windows 7, Mac OSX 1.4 and CentOS Linux. Trawling the various video forums, I came across a couple helpful articles:



When I'd first discovered the problems with these disks I was at home and using Windows; therefore, I was inclined to try Windows oriented solutions first. On my Windows 7 laptop, the DVD disks when inserted appear as full but blank. There is no discernable file system, and out of all my DVD related tools only Nero could detect that there was a session with tracks on the disk. It could not do anything with the disk though. I don't have the full commercial version of Nero. I tried the free tool DVD Decrypter, which is recommended for ripping from DVD file systems, but it detected nothing. The excellent ShareWare tool IsoBuster is able to detect and recover files from these DVDs, but in order to extract the files you need to shell out $30 for a license to unlock the Pro features. I considered this a small price to pay and so I did, though I now know that this was not necessary. If you don't fancy mucking around with command line tools then go with IsoBuster, but make sure it can detect your tracks before you pay for it. The more adventurous might continue reading, after all I told you this can be done at no cost.

Linux is a powerful open source operating system whose many distributions offer packages of free software applications, many of which are as fully featured as competing commercial software available for closed source operating systems. And the maintainers of many of these tools also provide binary ports that run on Windows or MacOSX. On my CentOS 5.4 Linux desktop computer at work, I have the dvd+rw-tools package installed. This is also available as a binary port for Windows. This provides among other things a neat little utility that gives the structure of a DVD disk, even one that can't be mounted in the usual ways. With a disk in the drive and assuming a DVD device of /dev/dvd, I type 'dvd+rw-mediainfo /dev/dvd' and hit Return:
% dvd+rw-mediainfo /dev/dvd
INQUIRY:                [PHILIPS ][DVD+-RW DVD8801 ][4D28]
GET [CURRENT] CONFIGURATION:
 Mounted Media:         11h, DVD-R Sequential
 Current Write Speed:   16.0x1385=22160KB/s
 Write Speed #0:        16.0x1385=22160KB/s
 Write Speed #1:        12.0x1385=16620KB/s
 Write Speed #2:        8.0x1385=11080KB/s
 Write Speed #3:        4.0x1385=5540KB/s
GET [CURRENT] PERFORMANCE:
 Write Performance:     16.0x1385=22160KB/s@[0 -> 1845983]
 Speed Descriptor#0:    00/1845983 R@6.7x1385=9276KB/s W@16.0x1385=22160KB/s
 Speed Descriptor#1:    00/1845983 R@6.7x1385=9276KB/s W@12.0x1385=16620KB/s
 Speed Descriptor#2:    00/1845983 R@6.7x1385=9276KB/s W@8.0x1385=11080KB/s
 Speed Descriptor#3:    00/1845983 R@6.7x1385=9276KB/s W@4.0x1385=5540KB/s
READ DVD STRUCTURE[#10h]:
 Media Book Type:       00h, DVD-ROM book [revision 0]
 Legacy lead-out at:    2298496*2KB=4707319808
READ DVD STRUCTURE[#0h]:
 Media Book Type:       25h, DVD-R book [revision 5]
 Last border-out at:    2045*2KB=4188160
READ DISC INFORMATION:
 Disc status:           appendable
 Number of Sessions:    1
 State of Last Session: incomplete
 "Next" Track:          1
 Number of Tracks:      8
READ TRACK INFORMATION[#1]:
 Track State:           reserved incremental
 Track Start Address:   0*2KB
 Next Writable Address: 0*2KB
 Free Blocks:           12272*2KB
 Track Size:            12272*2KB
READ TRACK INFORMATION[#2]:
 Track State:           complete incremental
 Track Start Address:   12288*2KB
 Free Blocks:           0*2KB
 Track Size:            192*2KB
 Last Recorded Address: 12479*2KB
READ TRACK INFORMATION[#3]:
 Track State:           complete incremental
 Track Start Address:   12496*2KB
 Free Blocks:           0*2KB
 Track Size:            887216*2KB
 Last Recorded Address: 899711*2KB
READ TRACK INFORMATION[#4]:
 Track State:           complete incremental
 Track Start Address:   899728*2KB
 Free Blocks:           0*2KB
 Track Size:            16*2KB
 Last Recorded Address: 899743*2KB
READ TRACK INFORMATION[#5]:
 Track State:           complete incremental
 Track Start Address:   899760*2KB
 Free Blocks:           0*2KB
 Track Size:            192*2KB
 Last Recorded Address: 899951*2KB
READ TRACK INFORMATION[#6]:
 Track State:           complete incremental
 Track Start Address:   899968*2KB
 Free Blocks:           0*2KB
 Track Size:            945984*2KB
 Last Recorded Address: 1845951*2KB
READ TRACK INFORMATION[#7]:
 Track State:           complete incremental
 Track Start Address:   1845968*2KB
 Free Blocks:           0*2KB
 Track Size:            16*2KB
 Last Recorded Address: 1845983*2KB
READ TRACK INFORMATION[#8]:
 Track State:           invisible incremental
 Track Start Address:   1846000*2KB
 Next Writable Address: 1846000*2KB
 Free Blocks:           451888*2KB
 Track Size:            451888*2KB
READ CAPACITY:          0*2048=0


We can see that there are 8 tracks on this DVD in a single session, though the last track has not been started. The first track is reserved and the middle six are complete. Under the heading READ DISK INFORMATION, we also see why this disk fails to mount. The disk has an 'appendable' status and its single session is in an 'incomplete' state. It is not finalized. Generally a recordable DVD can only be played or finalized using the software that created it because this is the only software that can locate the files without the support data created during finalization. I've read that it is sometimes possible to create a clone image of an unfinalized DVD and use other software to close the session, but I have never tried this. The important thing is to get the VOB files off the DVD disk. If you can do this then you can reauthor a new DVD. The output of dvd+rw-mediainfo above shows that there are likely two VOB tracks (#3 and #6). These two tracks are each about 2 Gigs long. The other tracks are very short, suggesting they probably contain menu or other setup files. The field "Track Size" is shown with the number of blocks and the size of each block separated by an asterisk. Track #6 is 945984*2KB = 1891968 KB = 1847.628 MB = 1.804 GB roughly.

Now for the fun part. The dvd+rw-mediainfo output actually tells us everything we need to know in order to extract the VOB files using a tool such as "dd" (a GNU command line utility for copying parts of files and block devices). dd is also available for Windows, but I don't have a link at the moment[This excellent tool is available here]. In order to extract a VOB from our DVD we only need to know its starting block and its size. For track #6, the starting block is 899968 and its size is once again 945984 blocks. Block size is 2KB. The command to extract the track would be written as follows (note: 2KB = 2*1024 bytes):

 
dd bs=2048 skip=899968 count=945984 if=/dev/dvd of=/tmp/track6.vob

The 'bs' option specifies the block size, skip specifies the number of input blocks to skip and count specifies the block length to copy. 'if' is input file and 'of' is output file, which can be any writable path. Here's what I get when I run the command (it takes a few minutes to complete):
% dd bs=2048 skip=899968 count=945984 if=/dev/dvd of=/tmp/track6.vob
945984+0 records in
945984+0 records out
1937375232 bytes (1.9 GB) copied, 903.739 seconds, 2.1 MB/s

And the delight of the day is that if I open that file in VLC media player, it plays perfectly! I can now add that file (and track 3 once extracted) to a new DVD project in a DVD authoring application (such as the free cross-platform DVDStyler) and burn a DVD. The DVD authoring software is needed in order to create DVD menus. Although in theory it should be possible to extract menu files from the original DVD in the same way, I have not tried this. I have viewed extracted files and they are in a binary file format. Anyway, the menus created by your standard DVD recorder are nothing special. Might as well make new ones.

The first DVD I created was not done using the dd tool. Instead it was done using IsoBuster's extraction feature. Had I known what I was doing I could have selected the tracks to export and created raw export image files that I could rename as .vob files. I did not know what I was doing so I asked IsoBuster to find and recover missing files based on signatures. It found some IFO files and two groups of three VOB files. It was easy for me to see that each set of three VOB files was a single episode split into three parts. Unfortunately I struggled for a long time with an error in my DVD authoring software after trying to import the VOB files as separate files. VOB files are multiplexed video and audio and contain crucial timing information. A split VOB file cannot be imported into DVDStyler (if indeed any authoring software) and so it was necessary for me to recombine the VOBs which is a simple matter of concatenating them. The end result was a working DVD video disk with two menus and two titles (which I'm not about to show you   ;-)  I hope this has been useful.

*UPDATE 29th September 2011: We now own a properly licensed commercial DVD copy of this television series.

*UPDATE 25th May 2013:
Some commenters have pointed to difficulties using the Windows port of dd. This has almost invariably been due to incorrect specification of the input file parameter. Regardless of operating system the source disk partition needs to be addressed as a block device. Different operating systems will do this differently. On Windows the disk device nodes are rarely seen. However, using the free dd port written by John Newbigin, we can easily find out how to address our drives in Windows. Here's an extract from my laptop running Windows 7:

C:\[some path]>dd --list
rawwrite dd for windows version 0.6beta3.
Written by John Newbigin 
This program is covered by terms of the GPL Version 2.

Win32 Available Volume Information
\\.\Volume{96e6c57a-4602-11e0-b661-806e6f6e6963}\
  link to \\?\Device\HarddiskVolume3
  fixed media
  Not mounted

\\.\Volume{96e6c578-4602-11e0-b661-806e6f6e6963}\
  link to \\?\Device\HarddiskVolume1
  fixed media
  Mounted on \\.\c:

\\.\Volume{96e6c579-4602-11e0-b661-806e6f6e6963}\
  link to \\?\Device\HarddiskVolume2
  fixed media
  Mounted on \\.\d:

\\.\Volume{8413f99c-d57c-11e1-a997-70f39540a6d8}\
  link to \\?\Device\CdRom0
  CD-ROM
  Mounted on \\.\e:

Here we can see that there are two ways to address the optical device, which are '\\?\Device\CdRom0' or '\\.\e:'. I prefer the latter style. I've used this and can confirm that it works as expected.

Flayman on LiveJournal (old)