Pages

Wednesday, February 15, 2017

Download a PDF in Angular 2

If you need to download a PDF file (stream) from your web service, there are a couple of key things to do.

Make sure you use the responseType property of the RequestOptions object (I'm using TypeScript here). You need to set this property to ResponseContentType.Blob (an enum from @angular/http)

Code snippet


// Depending on what you are sending to the server
// and what the server is sending back
let headers = new Headers({ 
   'Content-Type': 'application/json', 
   'Accept', 'application/pdf'
});


let options = new RequestOptions({ headers: headers });
// Ensure you set the responseType to Blob.
options.responseType = ResponseContentType.Blob;


Then, we will use the new Blob object in HTML5 to handle the response. To do this, create a new Blob using the response.blob() function in your map() function.

this.http
   .post(url, body, options)
   .map(response) => {
      // Removed checking of valid response
      ...
      let fileBlob = response.blob();
      let blob = new Blob([fileBlob], { 
         type: 'application/pdf' // must match the Accept type
      });
      ...
   })

Finally, use the saveAs() function of HTML5 to create the file on the client. Due to browser support, it is best to use the FileSaver JavaScript library (npm install file-saver, npm install @types/file-saver)

Don't forget to import it

...
import * as FileSaver from 'file-saver'; 
...

Then just use it after you have created the Blob.

...
let filename = 'mypdf.pdf';
FileSaver.saveAs(blob, filename);
...

Done.

Wednesday, January 18, 2017

Using RxJS Observable to return a simple value

So, I have a DataService that is using Observables (kinda the new Promises) to get a bunch of things, then when they are all ready it will get all the values and deal with them.
So functions that use the RxJS .post().map() etc will return Observable fine but I just wanted to return 10. As in the number 10. I don't really care if its Observable per se but I do want to be able to consume the function in the DataService as a promise. So how to you return a number 10? Here goes:

getNumber(): Observable {
   var observable = Observable.create((observer: Observer) => {
      observer.next(10);
      observer.complete();
   });
   return observable;
}

It can then be consumed, along with other promise-style functions and dealt with like promise.all()

Observable.forkJoin([
   this.dataService.getNumber(),
   this.dataService.getSomethingElse()
]).subscribe(results => {
   this.number = results[0];
   this.somethingElse = results[1];
});

Using the subscribe is like using promise.all()

Feels a bit weird at first, but basically if you want to return a promise of a simple type, just create a function like the getNumber() function above.

Wednesday, May 27, 2015

Filtering SPD 2013 Workflow REST calls by Date

I recently spent way too much time trying to figure out why my REST call was failing in a SPD 2013 Workflow when I was filtering on a DateTime field.

All the articles I found relating to filtering by a DateTime seemed to hard-code a date. I.e. ...$filter=SomeDateField eq DateTime'2015-05-27T00:00:00Z'... which is fine and works but if you are filtering using DateTime variable (because you are applying some sort of date transformation) then it is difficult to find anything about how to do this.

Turns out when you add your filter date variable to the above example, you need to change the 'Return field as:' setting from "String" to "ISO Formatted".

So the above example would look something like:
...$filter=SomeDateField eq DateTime'[%Variable:FilterDate%]'... and when you add that variable you select to return the contents as ISO Formatted.

NOTE: Just incase, you cannot just type in the bit of code in the [%..%] section, you need to use the "Add or Change Lookup" button in the String Builder dialog.

Hope this saves someone else some time.

Sunday, March 2, 2014

SharePoint 2013 Tasks LVWP bug with connections framework

OK, previously I posted about having issues with connecting web parts in 2013 without using the 'Server Render' option. I now know its a little more specific.

I've finally had a bug registered for this by Microsoft. It took nearly 4 months mind you.

The issue is in fact only Task-based List Templates that cannot connect with other web parts when using client side rendering. It seems that with the new features built into the CSR for the Tasks lists, they forgot to add the code to make the connections framework work. I'm hoping its a simple case of adding some JavaScript to get it to work. I haven't had the time to fish through the csr code to see if this is the case.

Tuesday, September 24, 2013

ddwrt:FormatDate bug - still in SP2013

OK, firstly, if you didn't know, SP2010 had a bug in the ddwrt:FormatDate function in XSLT where it assumes that you always use Locale: English (US). So if you live in any other part of the world you can't use this function.

Well, thanks to a bunch of other people and their posts, here, here, and kinda here, there is a way around this using even more XSLT.

So for me, I was calculating differences between dates using strings (yeah I know, probably not the best way but...) and because my Australian Dates were all over the place but I was always calculating them using yyyyMMdd I modified Elio's template for my own.

Hope this helps others, at least as a starting point. Oh yeah, and I hope one of these days MS get around to fixing the root cause!

<!-- ***************************************************************************** -->
<!-- This template fixes an issue where the US think they are the only ones on earth -->
<!-- NOTE: Returns date in yyyyMMdd -->
<!-- If you went to town implementing string manipulation templates to parse a date format out of a parameter you would -->
<!-- be able to return any formatted date, but I don't have the time at the moment -->
<xsl:template name="CustomFormatDate">
<xsl:param name="dateValue" />
<xsl:param name="monthFormat" />
<xsl:param name="locale" select="3081" />

<!-- Split Date -->
<xsl:variable name="day" select="substring-before($dateValue, '/')" />
<xsl:variable name="month" select="substring(substring-after($dateValue, '/'), 1, 2)" />
<xsl:variable name="year" select="substring(substring-after(substring-after($dateValue, '/'), '/'), 1, 4)" />

<!-- Create US Date Format -->
<xsl:variable name="USDate">
<xsl:value-of select="$month" />/<xsl:value-of select="$day" />/<xsl:value-of select="$year" />
</xsl:variable>

<!-- Month Notation -->
<xsl:variable name="monthString">
<xsl:choose>
<xsl:when test="$monthFormat='MM'">
<xsl:value-of select="$month" />
</xsl:when>
<xsl:when test="$monthFormat='MMM'">
<xsl:value-of select="ddwrt:FormatDateTime($USDate, $locale, 'MMM')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="ddwrt:FormatDateTime($USDate, $locale, 'MMMM')" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>

<xsl:variable name="twoDigitDay">
<xsl:choose>
<xsl:when test="string-length($day) = 1">0</xsl:when>
</xsl:choose>
<xsl:value-of select="$day" />
</xsl:variable>

<!-- Create Date -->
<xsl:value-of select="$year" /><xsl:value-of select="$monthString" /><xsl:value-of select="$twoDigitDay" />
</xsl:template>

Thursday, September 19, 2013

Filter web parts not working in SP2013

Recently I was building a new SP2013 site using the same techniques we had developed for SP2010, namely composite pages using query string parameters to filter on Master / Detail records. But we discovered that the filtering web parts were not filtering!

To get to the point, after much search around, turns out that for this feature to work, you need to ensure the ‘Server Render’ checkbox is enabled in the Miscellaneous section of the consuming web part.

To go even further, I would say that this field needs to be set for more than just this feature to work. XSLT doesn’t work properly if this isn’t enabled, and I’m sure there are other things that when they don’t work, will trace their roots back to this checkbox.

Thursday, September 5, 2013

Windows Workflow Manager Authentication

Although classic mode authentication is supported in SharePoint 2013, in reality you can only really use Claims. Workflow Manager 1.0 requires Claims-based authentication otherwise you will see all sorts of errors.

Thursday, April 18, 2013

Scripting User Profile Properties in SharePoint

Just can across a couple of great articles on how to script the creation and modification of the User Profile Properties. For those who are into repeatable process, this allows you to map out what properties you want in your environment and how you want users to interact with them. Then you can run the script through all your environments (assuming your AD/LDAP attributes are the same in all environments ;)) to configure all your farms accurately.

This one describes how to add new property mappings. ie. If you want a property called Pets that you want users to be able to fill in.
http://blogs.msdn.com/b/tehnoonr/archive/2010/11/22/mapping-user-profile-properties-in-sharepoint-2010-to-ldap-attributes.aspx

This one modifies existing ones (including new ones you create I guess) and allows you to set the other options around each property, like whether its alias, who can edit etc
http://www.c-sharpcorner.com/uploadfile/anavijai/modify-user-profile-properties-in-sharepoint-2010-using-powershell/

Reference material on SharePoint property constants that are used
http://msdn.microsoft.com/en-us/library/microsoft.office.server.userprofiles.propertyconstants_members.aspx

Thursday, February 7, 2013

Good SharePoint Blog

Karine Bosch has a great little blog that shows you how to create SharePoint-like Application Pages using the OOTB control templates and also has a great piece of source that wraps the Content Query WP's Select Site/List/Item dialog (LaunchPickerTreeDialog). Very handy.
http://karinebosch.wordpress.com/

Wednesday, April 25, 2012

Removing Columns for Content Types

Creating a new custom content type? Have to inherit from something but you don't want to include all the fields/columns from the parent?

Simple (in most cases). Use the following powershell script to remove (note does not delete the column which doesn't belong to you to delete anyway :))

$ver = $host | select version
if ($ver.Version.Major -gt 1) {$host.Runspace.ThreadOptions = "ReuseThread"}
if ((Get-PSSnapin "Microsoft.SharePoint.PowerShell" -ErrorAction SilentlyContinue) -eq $null)
{
    Add-PSSnapin "Microsoft.SharePoint.PowerShell"
}

#Attach to the web and content type
$web = Get-SPWeb http://intranet.contoso.com/
$ct = $web.ContentTypes["Content Type"]

#Insert name of column to remove from Content Type
$columnName = "Comments"

#Get link to the columnn from the web
$spFieldLink = $ct.FieldLinks[$columnName]
#Recently I tried to use this script and this line failed, I replaced it with the one above and its fine.
#$spFieldLink = New-Object Microsoft.SharePoint.SPFieldLink($web.Fields[$columnName])

#Remove the column from the content type and update
$ct.FieldLinks.Delete($spFieldLink.Id)
$ct.Update()

#Test that the column was successfully removed from the Content Type
if ($ct.Fields[$columnName])
{
    Write-Host "$columnName was not removed"
}
else
{
    Write-Host "$columnName was removed"
}

Note: Only issue I have found so far is that I still cannot remove all fields that I want removed. In my case I wanted to remove the "Comments" field from a content type but for some reason it won't remove.

EDIT: Figured out how to delete/remove the Comments field from my content type.
First make it hidden (you can do this through the UI or in a script) then the above script can be used to remove it. Weird by it works

SharePoint Permissions Tip: Leave the Style Resource Readers group alone

I've spent quite a bit of time tracking down issues that all relate to permissions for the Style Resource Readers group. My number 1 permissions tip: Don't change anything to do with this group!!!

There are several ramifications when this group's permissions change, users may start reporting that they have 'Access Denied' errors trying to edit or create pages. Or if you have a structure where you want users to only be able to access a sub site and you don't give any permissions to the Root site, then they will get 'Access Denied' everywhere.

Create / Edit publishing pages

What that meant was, because SharePoint was trying to render the masterpage and page layout on either the page they wanted to edit or a new page, it couldn't because they didn't have access to them, regardless of the fact they had access to the page library.

It's a weird error to try and track down because the user will report that they still have access to create/edit items in a list, they can still see Site Settings, they still seem to have enough privelages to do it, but get an outright 'access denied' error.

Access Denied to sub sites

I had created a group just for a sub site to have view only permissions however users were reporting that they got 'Access Denied' messages. Again it turns out that although the Style Resource Readers group existing in the Root site with all authenticated users it didn't have any permissions on the Style Library document library itself, hence why access didn't work in the sub site.
Just ensure that the Master Page Gallery (Style Library) has the Style Resource Readers group as having restricted read permission on the library.

SharePoint 2010 Web Services and Anonymous Access

Looks like SharePoint 2010 still has issues with Anonymous Access and its web services.
There are a fwe articles out there pointing at this issue in 2007 but I'm afraid its no better in 2010 (SharePoint 15?).

This article (Trinkit HQ) is short and sweet, and gets right to the point. Basically if your Web Application has Anonymous Access turned on, you will experience the 'Attempted to perform an unauthorized operation' error.

There are a few ways to deal with it, extend another web app, muck around with IIS (not recommended) but the bottom line is:
Anonymous Access = No Web Services

Thursday, March 15, 2012

Customising RichTextEditor's Styles and Markup Styles Menu Items

OK, there are a few articles around discussing how to do this, this post by Marc seems to be the definitive article about what, where, how and why but I felt like writing a simplified version for myself.
So here goes...

What are we talking about?
First, I'll try and give my own spin on what the Styles and Markup Styles are and what they affect.
Below is the definition given by many when describing what the differences are between the two, my interpretation of these are given in italics.

Markup Styles
  • Nest the text and its HTML tag within the html tag specified in the style if the tag to nest is a span element. What a mouthful, ok I think it means, if the text you are trying to apply a Markup Style to is a span element, then the text and its surrounding tag (probably div p or h1 etc) will get 'nested' inside the tag specified by the css class you create.
  • Replace the current tag if is is not a span element. If the text is not in a span, then the current tags (again probably div, p etc) will be replaced with the tag in the css class.
  • Remove all the styles for the children elements. This is easy, removes all the children styles so that the selected text will all be rendered using the specified css class.
  • Remove the html tag if the same style is applied a second time (this is a way of removing a Markup style for an end-user). Simple, removes the style.
Styles
  • Nest the text in a span tag with the style class if the text is not already inside an HTML tag.
  • Just add the class to the current HTML tag if this tag is not a span tag.
  • Replace the class of the HTML tag if this tag is a span tag.
  • Remove the html span tag if the same style is applied a second time (this is a way of removing a Style for an end-user) Simple, removes the style.

Clear as mud, good. Now, both these 'styles' are used in the Rich Text Editors of both Wiki Pages and Publishing Pages and by default, the CSS classes live in \14\template\layouts\1036\styles\themable\corev4.css stylesheet.
Right, well how do we change it?
So we now have an idea of what the ribbon controls do, how do we add to or change them?

Syntax
OK, now for the cool way in which this stuff gets pulled together. All styles conform to a naming convention. This convention is then used by the RTE on the client side to build the ribbon control. So all you need to do is follow the convention and you can add a new class/style to the ribbon, easy.

.ms-rteStyle-xxxxx - for styles

.ms-rteElement-xxxxx - for markup styles

Simple? Just add the name of the class you want to create where the x's are and there you go, well almost. Inside the class definition, you need to add

-ms-name:"xxxxx";

Where again, you replace the x's with the name you want to appear in the ribbon control. So if I wanted to add a style like Heading 5, I could do the following:

.ms-rteStyle-H5
{
   -ms-name:"Heading 5";
   FONT-SIZE:14pt;
   FONT-WEIGHT:normal;
}

You get the picture, sky's the limit as to what you can do with your text. Either trawl through the corev4.css or just look at the styles applied to text after you change a markup style using developer tools (F12 in IE).

Well, sounds like we have all the information about these styles that we need so we can pretty much do anything right? Not quite. Removing a style is tricky. Adding and Changing is simple, because its just css. There are some ways to remove styles including custom masterpages, jQuery and custom page layouts etc so there is a way around it. I won't go into that in this article, use bingle.

Solution Management
So now you know what to change, where do you change it? What's best practice for lifecycle management of this solution? Depends :)

Testing / Debugging
Simple, wack the css into a file, upload it to the Styles Library and reference it using the HTML Form Web Part on the page you want to test. Simple, no code changes to pages, solutions etc. And you could get away with doing this in production if it was only on 1 or 2 pages, but having this as a reuseable solution just won't cut it.

MasterPage
In my opinion, the best way to do this is by putting a CSS Registration reference into a master page pointing to a specific css file you want to setup in the Style Library. This way, if you don't want to use any custom styles, you just don't have the css file in the Style Library, but if you do, then you can just edit the file. The added benefit of this is that you can have different files for different Site Collections. Just add the line below to your masterpage, up near the top, there is probably already similar references.

{SharePoint:CssRegistration name="{% $SPUrl:~sitecollection/Style Library/~language/MarkupStyles.css %}" After="corev4.css" runat="server"/}
(don't forget to replace the curly braces with greater than/less than symbols, and change the path where relevant)

Other Options
There are other options available where you are either editing the core files, editing existing pages or page layouts etc. but I wouldn't really go near them.

Removal of the OOTB styles is difficult. This can be done either through JavaScript or by adding server-side code to remove references to corev4.css etc, see Marc's article near the bottom for these.

If you are going to go down the path of modifying a Master Page, then perhaps you can insert your JavaScript in the master page and do some tricky stuff there but that is out of my league :)

SharePoint Search Web Services and anonymous access

Recently had to debug an issue with a 3rd Party app using the /_vti_bin/search.asmx web service which, at the time, stopped working with 'no changes' to the environment. We were receiving the SoapException: 'Server was unable to process request ... Unauthorized Operation'.

This turned out to be not quite the truth and infact Anonymous Access was enabled on the Web Application for use by a monitoring tool. The Farm configuration doesn't use Kerberos (jury is still out on whether Kerberos is needed in this situation anyway), just Ntlm and it seems that once a successful connection is made to another SharePoint web service (in this case /_vti_bin/lists.asmx) then a subsequent call to the search service works.

We did a bit of packet sniffing as well and it looks like the search service trys to authenticate differently than the other web services. Not sure why this is the case. I need to understand more about how Search works.

Here's the post on TechNet.

Moral of the story is to check, and double check the environment before spending time going down rabbit holes. Statements like 'it was working, then it stopped...with no change in the environment' just don't add up.

Thursday, January 5, 2012

Issue with DNS using VirtualBox and the SharePoint 2010 VHD

After many hours of trying and reading I finally stumbled across an article that described my problem and solution.
Thanks very much Edward http://microsoftdevguy.blogspot.com/2011/05/sharepoint-demo-image-gets-error.html
It seems that trying to follow the MS Install guide didn't quite work and after you successfully run the image, the only site you can hit is CA. After following Edwards article and just using a Bridged Adapter and an available IP address in the Host networks range, then modifying the Guest adapter and DNS entries, viola! Magic!

Thursday, May 5, 2011

SharePoint 2010 Debugging

This one always gets me… There’s a few things that need to be done in order to better debug SharePoint.

  • Custom Errors Off
  • Enable Call Stack Trace
  • Debugging Mode
  • ASP.Net Tracing

…and the big one.

  • Multiple web.config files.

OK, to break it down. First, know what Web Application you want to debug. This might sound silly but I’ve been caught out a few times with a couple of (not straight forward) sp farm setups.

SharePoint web.config files

Make sure you are looking at the right Virtual Directory. Also, as mentioned as the last bullet, there are multiple web.config files that might need modifying. Below is a scrape from a good article by Daniel on blogspot (thanks)

The web.config files are genearally found in the following locations:

  • web.config file in the root folder of each virtual server / IIS Application.

Local_Drive:\Inetpub\wwwroot
This is the usually the file that contains most of the web configuration of a SharePoint site collection. To display full errors you would need to modify this web.config file. There may be one for each of the SharePoint applications running in some cases (MySites, Multiple Portals or Instances of SharePoint, Central Admin, etc.). If this is the case, you will only need to modify the web.config file which is in the root of the virtual directory for the instance of SharePoint which you using. To find out which directory is used by various SharePoint applications/websites, view the properties of the SharePoint website in IIS and from the "Home Directory" tab, the value in the "Local path" field will take you to the directory where the web.config file is for the specific instance of SharePoint / Application in IIS.

  • web.config file used in Web Part resources for the Global Assembly Cache (GAC)

Local_Drive:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\wpresources

  • web.config configuration file(s) for extending other virtual servers

Local_Drive:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\CONFIG

  • web.config file which defines configuration settings for the /_vti_bin virtual directory

Local_Drive:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\ISAPI

  • web.config file which defines configuration settings for the /_layouts virtual directory

Local_Drive:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\TEMPLATE\LAYOUTS

  • web.config configuration file for Central Administration pages.

Local_Drive:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\TEMPLATE\ADMIN\Locale_ID

Now that you know what config files to modify, here are the key parts that need modification.

Custom Errors Off

Set the customErrors mode to "Off"
Find:
<system.web> ...
<customErrors mode="On" />
Change To:
<system.web> ...
<customErrors mode="Off" />

Enable Call Stack Trace

Set the CallStack value of the SafeMode element to "true"
Find:
<SharePoint>
<SafeMode ... CallStack="false" ... >
<SharePoint>

Change To:
<SharePoint>
<SafeMode ... CallStack="true" ... >
</SharePoint>

Debugging Mode

Set batch and debug to "true"
Find:
<compilation batch="false" debug="false">
Change To:
<compilation batch="true" debug="true">

ASP.Net Tracing

Include the following line in the <system.web> element of the web.config file.
<system.web> ...
<trace enabled="true" pageOutput="true"/>

And that’s about that. I will post a bit about debugging via log files soon.

Friday, April 29, 2011

SharePoint 2010 - User Profile Sync Issues

Tricky topic, heaps of articles out there, but...

Make sure that both FIM services are started before worrying about any user profile sync issues.

For instance, in the contoso VHD, I added new AD users but they would not sync. When viewing the Sync Connection, there wasn't any. So I tried creating a new one but there were drama's with this which led me to the solution, Make sure the FIM services are running. As soon as these services are running, the sync connection will be displayed in CA.

Thursday, April 29, 2010

Using ROW_NUMBER

ROW_NUMBER() OVER (PARTITION BY ORDER BY ) AS RowNum

Monday, September 29, 2008

Deleting a user from all db's

If you need to recursively remove a user from all db's, run this.
What happens is the statement first checks whether the user is in the 'current' database (remember
msforeachdb)
then, changes the current context to the database, executes the dropuser command, then prints that it did it.

DECLARE @usr sysname

DECLARE @passwd varchar(20)
SET @usr = 'AUser'
SET @passwd = 'password'


DECLARE @cmd1 nvarchar(2000)
SET @cmd1 = 'if exists (select name from ?.dbo.sysusers where upper(name) = '''+@usr+''') '+
'begin '+
'use ? '+
'exec sp_dropuser ''' +@usr+ ''''+
'print ''dropped user from ?'''+
'end'
exec sp_msforeachdb @cmd1

Monday, August 11, 2008

Using a CSV array as a filter in a Stored Procedure

Here's a really handy trick I picked up.
If you have a stored procedure and you would like to pass a set of values in as part of a filter, ie. A list of Identifiers, or Names that you want to filter on, then you can use a handy table that contains just a bunch of numbers. I'll show you.

-- Create the table
CREATE TABLE [dbo].[Nums](
[n] [int] NOT NULL,
PRIMARY KEY CLUSTERED
(
[n] ASC
) ON [PRIMARY]
) ON [PRIMARY]

-- Now add the numbers
DECLARE @index int
SET @index = 1
WHILE (@index < @NumbersToAdd)
BEGIN
INSERT INTO [dbo].[Nums] (n)
VALUES (@index)
SET @index = @index +1
END

create a function to put the values into a table.

CREATE FUNCTION dbo.ufn_CommaValues(@List varchar(8000))
RETURNS @Array Table
(
pos integer,
val varchar(10)
)
AS
BEGIN
INSERT @Array
SELECT
n - LEN(REPLACE(LEFT(@arr, n), ',', '')) + 1 AS pos,
CAST(SUBSTRING(@arr, n, CHARINDEX(',', @arr + ',', n) - n) AS INT) AS val
FROM Nums --Uses the Nums table.
WHERE n <= LEN(@arr)
AND SUBSTRING(',' + @arr, n, 1) = ','
ORDER BY pos
RETURN
END

Now we have everything you can use it like this:

CREATE PROC dbo.usp_Example
@List varchar(8000) --This is a list of comma separated GUIDS
AS
SELECT *
FROM TableA
INNER JOIN dbo.ufn_CommaValues(@List) CSV ON TableA.Id = CSV.Val


Pretty useful if you have a list of records that you want to filter on, particularly for debugging.