tag:blogger.com,1999:blog-21433639318012347252024-03-06T00:36:46.512-08:00SharePoint Features and FailuresJeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.comBlogger38125tag:blogger.com,1999:blog-2143363931801234725.post-55235970838024832082015-08-03T14:25:00.001-07:002015-08-03T14:25:51.034-07:00View All SQL Server Report Subscriptions with a Powershell ScriptWhen running SSRS in stand alone mode, there is an option to "Manage My Subscriptions". This is helpful, but doesn't provide a lot of details about each subscription. If you have many subscriptions on the same report with different parameters, there is no way to tell them apart.<br />
<br />
SSRS in SharePoint mode is even worse, because there is no way to view all subscriptions on one page. You must go to each report to view each subscription. Even from there, it gives almost no details about that particular subscription.<br />
<br />
Searching around, I found two solutions:<br />
<ol>
<li>Run a query directly against the database tables</li>
<li>Buy a $800 application (2ndrock.com)</li>
</ol>
<div>
The 2ndRock application is nice, but I don't have that kind of budget and it still doesn't provide exactly what I want since it doesn't export report Parameters.</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Instead, I wrote a powershell script to find all reports and dump the data out to a CSV file. This has been tested in SSRS 2008 Stand alone mode and SP2013 with SSRS 2012.</div>
<div>
<br /></div>
<div>
Rather than dump the code here and have Blogger butcher it, I've uploaded the code to GitHub.</div>
<div>
<br /></div>
<div>
https://github.com/jlboygenius/GetAllSSRSSubscriptions</div>
<div>
<br /></div>
<div>
<br /></div>
Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0tag:blogger.com,1999:blog-2143363931801234725.post-76923241731932529562014-12-12T13:06:00.002-08:002014-12-12T13:06:22.478-08:00SharePoint Event Handlers and Checkbox ValuesFound a new 'gotcha!' today.<br />
<br />
I have an event handler on a list. When an item changes, we update a database.<br />
<br />
If the item has a checkbox value(we have a few), and the item is edited through the normal EditForm.aspx page, the code is presented with a True/False value for that checkbox item. No problem, works great, our Convert.ToBoolean(value) works fine.<br />
<br />
If the item is updated using the data sheet view, which, users tend to like on lists of 1000+ items, the code is presented with a 0/1/-1 value for the checkbox field! The Convert.ToBoolean(value) fails. causing an exception.<br />
<br />
<br />
Kit Menke's blog confirmed i'm not crazy: <a href="http://kitmenke.com/blog/2009/07/13/boolean-values-in-an-event-handler/">http://kitmenke.com/blog/2009/07/13/boolean-values-in-an-event-handler/</a><br />
<br />
So, to steal his explanation:<br />
case "-1": // user changed a boolean field to true in datasheet<br />
case "1": // field was already true in the item after a datasheet edit<br />
case "true": // normal edit (not using datasheet)<br />
value = true;<br />
break;
<br />
<br />
<br />Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0tag:blogger.com,1999:blog-2143363931801234725.post-71737086186691835712013-10-24T12:41:00.000-07:002013-10-24T12:45:40.586-07:00Content deployment gotchasWhen planning for content deployment there is one thing that is important to watch out for: Hard drive space. Specifically, the C: drive.
When you deploy content, it is downloaded, compressed, copied, stored, uncompressed and loaded into sharepoint.
Sometimes, this process fails and you're left with data lying around.
The biggest problem is space. You need at least twice the size of your deployment as free space on your drive.
When setting up deployment, you are given the option to setup the location where files are stored temporarily. So you think to yourself, great! I'll set this up on my SAN and have plenty of space. Sadly, this doens't work. This free space is only used to store the CAB files that are moved. It is not used to store the files when they are uncompressed.
The cab files are stored in the folder you define(C:\programdata\contentdeployment is the default i believe), but then they are uncompressed and stored in the temporary files folder for the user that runs the process. And to make it worse, ALL of the files are uncompressed before the process begins.
This means if you are migrating 10GB of data, you'll need 10GB of free space in the user's home directory, and 10GB of free space in the content deployment folder! So pushing 10GB of data, requires 20GB of space plus the amount of space needed for the manifest XML files which can be quite large.
The uncompressed location of the files is:
c:\users\<CA User Account>\AppData\Local\Temp\<GUID for Content Deployment ID>
This progess happens quickly and it will clean up the files after it fails. You may look at your computer and see many GB of free space. What you didn't see was the drive filling up and then failing, then deleting the files.
So, when setting up content deployment. Figure out how much data you will be pushing with a full content deployment (and plan for future expansion), then add a few more GB for manfest XML(this is probably overkill), then double it.
It's not always easy to expand the C: drive later, and moving the temp directory isn't fun. Just make your C: drive REALLY big. Don't forget to check back in every now and then and clean up the files that are sometimes left from a failed deployment.
I seen to remember a way to set the max CAB size in 2007, but I don't see it in 2010. Keep in mind that it's actually the minimum cab size, not max. It will keep adding files until the cab reaches the max. If it's one byte below the max, and the next file is 200MB, it will end up with a cab that is 200MB+max-1 byte. This can be confusing when IIS rejects the file because it is too large.Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0tag:blogger.com,1999:blog-2143363931801234725.post-27999732105552657482013-09-16T14:05:00.003-07:002013-09-16T14:05:43.682-07:00Fixing missing Hidden and Sealed attributes in Site columns<p>After a recent upgrade to sharepoint 2010, we were no longer able to create new site templates. Creating a template would fail with an error:</p>
<p><code>Unexpected System.InvalidOperationException: Error copying temporary solution file to solutions gallery: _catalogs/solutions/TestProduction.wsp at Microsoft.SharePoint.SPSolutionExporter.ExportWebToGallery
</code></p>
<p>
Digging a little deeper into the ULS logs, I was able to find a better explaination of the error:</p>
<p>
<code>
SharePoint Foundation General 9fjj Monitorable SPSolutionExporter: Microsoft.SharePoint.SPException: Feature definition with Id 10a563f6-0afb-4d38-9ddc-241c61694ac6 failed validation, <br />file 'TestProductionListInstances\ElementsFields.xml', line 39, character 167:<br /> The 'Hidden' attribute is invalid - The value '' is invalid according to its datatype 'http://schemas.microsoft.com/sharepoint/:TRUEFALSE' - The Enumeration constraint failed. at Microsoft.SharePoint.Administration.SPSolutionPackage.SolutionFile.FeatureXmlValidationCallBack
</code>
</p>
<p>Something was wrong with our columns. The "Hidden" parameter was blank instead of true or false. Looking into the database confirmed the error. The xml schema of the column definition had : Hidden="" instead of Hidden="FALSE". I also noticed that "Sealed" was also blank, and would cause the same error when trying to save the site as a template. The error describes the elementfields.xml file. This file is part of the site template WSP file. To view the file, go to the solutions gallery and download the Site template WSP that gets created. Change the extension to a CAB and open it. You will find the ElementFields.xml file and can use the line and character from the ULS log error to find the field that causes the problem. In my case, it was about 40 differnet fields.</p>
<p>
These were errors on the site columns, many but not all of them being custom columns we created. Sealed and Hidden are not things that are easily changed through the UI, so I had to use Powershell to update the columns. I probably could have used SP Manager too, but since there were 40+ columns duplicated over a few different site collections, it was easier to use powershell.</p>
<p> Updating the Sealed column is fairly easiy since it is a property on a column that can be set directly. When trying to change the 'Hidden' property, I would get the following error:
<code>
PS C:\> $f = $web.Fields["Meeting Location"]<br />
PS C:\> $f.Hidden = $false<br />
Exception setting "Hidden": "Cannot change Hidden attribute for this field"<br />
At line:1 char:4<br />
+ $f. <<<< Hidden = $false<br />
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException<br />
+ FullyQualifiedErrorId : PropertyAssignmentException<br /></code> </p>
<p>So, if I can't change the field, how do I change it?. The answer is to change the xml schema. sharepoint will let you change the schema directly and skip around whatever check it has in place. <br />
<code><br /><br />
$field = $Web.fields[$i]<br />
[XML]$schema = $field.schemaxml <br />
$field.schemaxml = $schema.Innerxml<br />
$field.update()<br />
</code><br />
</p>
<p>The next problem, how do you know what fields need to be changed? If you just check for $field.hidden -eq "" or something like that it will not work. It will tell you that the field is "FALSE" even when it is blank! So, we have to look at the xml:<br />
<code>[XML]$schema = $field.schemaxml <br />
if ( $schema.InnerXML.Contains("Hidden=""""")){<br />do something}
</code><br />
</p>
<p> All together the script looks like this:<br /></p>
<p><code>$site = Get-SPSite "http://YourSiteCollectionURL"<br />
$Web =$site.RootWeb<br />
<br />
for($i = $Web.Fields.count -1; $i -ge 0; $i--)<br />
{<br />
$field = $Web.fields[$i]<br />
[XML]$schema = $field.schemaxml <br />
if ( $schema.InnerXML.Contains("Hidden=""""")){<br />
$schema.field.Hidden = "FALSE"<br />
$field.schemaxml = $schema.Innerxml<br />
Write-Host $field "HIDDEN: " $schema.field.Hidden <br />
$field.update()<br />
} <br />
if ( $schema.InnerXML.Contains("Sealed=""""")){<br />
Write-Host $field "Sealed: " $schema.field.Sealed <br />
$field.Sealed = "FALSE"<br />
$field.update()<br />
}<br />
if ( $schema.InnerXML.Contains("Required=""""")){<br />
Write-Host $field "Required: " $schema.field.Sealed <br />
$field.Required = "FALSE"<br />
$field.update()<br />
}<br />
}<br />
<br />
<br />
</code></p>
<p>
This script will set all the hidden and sealed fields to FALSE for the site columns that have the field as blank. Another way to do this is to modifiy the database directly, but that isn't supported and you can easily break things. </p>
<p> I hope this can help someone else who runs into the same problem. I wasn't able to find a good solution on the web. MSFT support(wipro) did help, but it took them almost 2 months, and 3 different support techs to come up with a script that I then had to extensivly modify to get to actually solve the problem.</p>Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com5tag:blogger.com,1999:blog-2143363931801234725.post-79484234594808235342013-06-04T14:18:00.002-07:002013-06-04T14:18:31.276-07:00Sum Grouped Calculated Columns in SharePoint After finally migrating to sharepoint 2010 from sharepoint 2007, i realized some of our custom Data view webparts were no longer working. They had been customized to add up the total hours and display it for each group.<br />
The view had items grouped by user and a total number of hours for that user. This the hours was computed by a check in time and check out time. Sharepoint does not provide a way to calculate a total for calculated fields.<br />
Most solutions tell you to use a dataview webpart to do this. Our old solution worked that way, but it was a hassle to add new views and re-use the code in other projects.<br />
<br />
My solution was to throw out the old code and re-build it using out of the box, easy to create views and some javascript/jQuery magic.<br />
<br />
The following code will find items by group, and compute a total for that group.<br />
The javascript has two variables:<br />
<b>COLUMNTITLE</b>: The name of the column you would like to find the total of all the rows<br />
<b>TEXTHEADER</b>: The text you would like to show in front of the total. This cannot be blank, and should be some unique text. It is also used to tell if the total has already been calculated and displayed.<br />
<br />
This code will automatically display totals for groups that have been expanded. If the group has not been expanded, it cannot show the total because SharePoint loads the data using ajax. When the group is expanded, the total will be calculated and displayed. <br />
<br />
This works on a SharePoint 2007 site upgraded to 2010, but still using the 2007 UI. I haven't tested this in other scenarios. The idea should be the same, some of the css tags might change. YMMV.<br />
<br />
<span style="font-family: Verdana, sans-serif;"><br />var TEXTHEADER = "Total Hours: ";// text to put in front of the total<br />var COLUMNTITLE = "TotalBillableTime";// which column of data do you want to sum up?<br />var COLUMNNUMBER =0; // placeholder variable.<br /><br />//alert("found:"+FindColumnNumber(COLUMNTITLE));<br /><br />$(function() {<br /> // attach to clicks to load the data when the user clicks to expand any closed sections<br /> COLUMNNUMBER = FindColumnNumber(COLUMNTITLE);<br /> AttachClicksToComputeTotalTime();<br /> // find the loaded blocks of data and populate the header<br /> // this can take some time, so we're calling it after a delay.<br /> // better method would be to attach to the ajax complete call of sharepoint if there is one.<br /><br /> setTimeout(function(){FindLoadedBlocks();},1000);<br /> <br />});<br /><br />function FindLoadedBlocks(){<br /> // search for all tbody's that have tbod in the id and have isLoaded = true<br /> // compute the totals for these<br /> $('tbody[id^="tbod"][isLoaded="true"]').each(function($this){<br /> ComputeBlock(this.id);<br /> }); <br />}<br /><br />function AttachClicksToComputeTotalTime(){<br /> // find all the header links that the user clicks to expand the data.<br /> // add an onClick event to compute and display the totals<br /> $("td.ms-gb2 a").click(function($this){<br /> FindBlock(this);<br /> });<br />}<br /><br />function FindBlock(Headerblock){<br /> // given a header, find it's child tbody and wait until it is fully loaded to compute its data<br /> var parentSectionID = Headerblock.parentNode.parentNode.parentNode.id;<br /> var childSection = parentSectionID.replace("titl","tbod")+"_";<br /> var kidSection = $("#"+childSection);<br /> if($(kidSection).text().length<100 br=""> setTimeout(function(){FindBlock(Headerblock);},50);<br /> return;<br /> <br /> } else{<br /> // it's done, let's load it.<br /> ComputeBlock(childSection); <br /> }<br /><br />}<br /><br /><br />function ComputeBlock(blockName){<br /> // for a block of data rows, compute the total and insert it into the header <br /> var parentSectionID = blockName.replace("tbod","titl").replace("__","_"); <br /><br /> // when groups are expanded by default, a second tobdy is created<br /> // check if this tobody has data, if not, go to the next<br /> if($('#'+blockName).html().length<100 br=""> var block = $('#'+blockName).next();<br /><br /> }else{<br /> var block = $('#'+blockName);<br /> }<br /> <br /> var rowSum = 0;<br /> childRows = $(block).find("tr").each(function(){ <br /><br /> // find the 6th column<br /> var x = $(this).find(":nth-child("+COLUMNNUMBER +")");<br /> var rowVal = parseFloat($(x).text().trim());<br /> if(!isNaN(rowVal)){<br /> rowSum+= rowVal;<br /> }<br /><br /> });<br /> rowSum = rowSum.toFixed(2); // trim it to 2 decimal places<br /><br /> // find the parent section <br /> var ParentSection =$("#"+parentSectionID+" tr td");<br /> // check that the parent section doesnt' already have the totals <br /> if($(ParentSection).text().indexOf(TEXTHEADER)>0){<br /><br /> }<br /> else{<br /><br /> $(ParentSection).append("<span style="align: right; color: red;"> "+TEXTHEADER +" "+rowSum+"</span>");<br /> }<br /><br />}<br /><br />function FindColumnNumber(ColumnTitle){<br />// find the column by name and figure out where it is.<br /> var pos = 0;<br /> var foundVal = 0;<br /><br /> $(".ms-vh2").each(function(){<br /> pos++;<br /> if($(this).text() == ColumnTitle){<br /> foundVal = pos;<br /> }<br /> });<br /> return foundVal;<br />}</100></100></span><br />
<span style="font-family: Verdana, sans-serif;"></span><br />
<br />
<br />
<br />
<br />
<span style="font-family: Verdana, sans-serif;">each group section will look something like this:</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFU4oNVB2P-KjQUvSMI2jpR2xskZdU2Rz_b3A2ih9o_6WcyUTr3C4U49XJHnhvI5b6mpq4IdJetD4vQqXsiWMeFlQUwWQKChacP0EyG86q9dgjGjUy_qS1DOHTyFXJi_oSiHyWg_QBeu8/s1600/groupTitle.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="31" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFU4oNVB2P-KjQUvSMI2jpR2xskZdU2Rz_b3A2ih9o_6WcyUTr3C4U49XJHnhvI5b6mpq4IdJetD4vQqXsiWMeFlQUwWQKChacP0EyG86q9dgjGjUy_qS1DOHTyFXJi_oSiHyWg_QBeu8/s320/groupTitle.PNG" width="320" /></a></div>
<span style="font-family: Verdana, sans-serif;"> </span>Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com1tag:blogger.com,1999:blog-2143363931801234725.post-42999979145153654332012-07-18T13:35:00.002-07:002012-07-18T13:36:09.622-07:00Find all Checked out filesHere's some sql you can run against your Content DB to find all checked out files. This will work against all files in the content DB, so it will work across multiple site collections.. everything in the DB.<br />
<br />
<span style="color: green; font-size: x-small;"><span style="color: green; font-size: x-small;"></span></span><span style="color: blue; font-size: x-small;"><span style="color: blue; font-size: x-small;">SELECT</span></span><span style="font-size: x-small;">[tp_Modified]</span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">,</span></span><span style="font-size: x-small;">[tp_Created]</span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">,</span></span><span style="font-size: x-small;">[tp_IsCurrent]</span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">,</span></span><span style="font-size: x-small;">[tp_DirName]</span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">,</span></span><span style="font-size: x-small;">[tp_LeafName]</span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">,</span></span><span style="font-size: x-small;">tp_login</span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">,</span></span><span style="font-size: x-small;">tp_title</span><span style="color: blue; font-size: x-small;"><span style="color: blue; font-size: x-small;">FROM</span></span><span style="font-size: x-small;"> [WSS_Content_CNS]</span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">.</span></span><span style="font-size: x-small;">[dbo]</span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">.</span></span><span style="font-size: x-small;">[AllUserData]</span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">left</span></span><span style="font-size: x-small;"> </span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">join</span></span><span style="font-size: x-small;"> [WSS_Content_CNS]</span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">.</span></span><span style="font-size: x-small;">[dbo]</span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">.</span></span><span style="font-size: x-small;">[UserInfo] </span><span style="color: blue; font-size: x-small;"><span style="color: blue; font-size: x-small;">on</span></span><span style="font-size: x-small;"> tp_checkoutuserid </span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">=</span></span><span style="font-size: x-small;"> userinfo</span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">.</span></span><span style="font-size: x-small;">tp_id</span><span style="color: blue; font-size: x-small;"><span style="color: blue; font-size: x-small;">where</span></span><span style="font-size: x-small;"> tp_CheckoutUserId </span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">is</span></span><span style="font-size: x-small;"> </span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">not</span></span><span style="font-size: x-small;"> </span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">null</span></span><span style="font-size: x-small;"> </span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">and</span></span><span style="font-size: x-small;"> tp_iscurrent</span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">=</span></span><span style="font-size: x-small;">1 </span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">and</span></span><span style="font-size: x-small;"> tp_deletetransactionid </span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">=</span></span><span style="font-size: x-small;"> 0x0</span><span style="color: blue; font-size: x-small;"><span style="color: blue; font-size: x-small;">order</span></span><span style="font-size: x-small;"> </span><span style="color: blue; font-size: x-small;"><span style="color: blue; font-size: x-small;">by</span></span><span style="font-size: x-small;"> tp_modified </span><span style="color: blue; font-size: x-small;"><span style="color: blue; font-size: x-small;">desc</span></span><span style="font-size: x-small;"></span><span style="color: grey; font-size: x-small;"><span style="color: grey; font-size: x-small;">,</span></span><span style="font-size: x-small;">tp_title</span>Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0tag:blogger.com,1999:blog-2143363931801234725.post-10462607617050704432012-06-29T10:50:00.000-07:002012-06-29T11:36:19.255-07:00KB2699988, iFrames, Anchor tags and the headaches that won't stop.Recently, a page on one of our sites stopped working. <br />
<br />
The page lists things and has anchor tags that will take you to the relavant part of the page. We are currently excusivly using IE8. <br />
<br />
The trick about this page is that it is displayed on our sharepoint portal using a Page viewer webpart. The page is from a legacy app, and the easiest way to put it on the portal was using an iFrame. <br />
<br />
<br />
The second notable piece of information is that the page viewer webpart is set with a heigh that is large enough to show the entire page inside the iframe without scrolling, something around 15000px. To the user, it looks as if the page is part of the portal. <br />
<br />
Recently, the anchor tags at the top of the page in the iFrame stopped working. The page would no longer scroll down to show the relavant part of the iFrame'd page. Clicking the anchor tags would do nothing. <br />
<br />
<br />
What's going on? These worked for 2 years without any problems! <br />
After a lot of searching and testing, I found out that this was a potential security hole. The problem is that when the user clicks an anchor tag, the main page has to scroll down to show the part of the inner iframe(the page is like 15000px long). <br />
Because the outer page scrolls, it's possible to setup an exploit that allows the outer page to tell if something exists on the inner page. Someone wrote about it at a blackhat conference: <a href="https://media.blackhat.com/bh-eu-10/presentations/Stone/BlackHat-EU-2010-Stone-Next-Generation-Clickjacking-slides.pdf">https://media.blackhat.com/bh-eu-10/presentations/Stone/BlackHat-EU-2010-Stone-Next-Generation-Clickjacking-slides.pdf</a>. The explaination starts at page 38. <br />
<br />
<br />
I found this because the same page does not work in Firefox or chrome. Fortunatly, Firefox is fairly open about their bugs and has a comments section to allow people to discuss the problem and the solution. The firfox bug (<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=583889">https://bugzilla.mozilla.org/show_bug.cgi?id=583889</a>) talks about the problem. This was helpful to track this down as a security update and not something that we changed. They fixed it in early 2011.<br />
<br />
It looks like that this same problem was fixed in KB2699988. Reading through the hotfixes that are in KB2699988, nothing really jumps out at me that says that this was fixed or changed. The closest think I can find is "<i>A memory leak may occur when a modal dialog box opens in an iframe in Internet Explorer 8 </i>". This to me doesn't have anything to do with the problem I'm running into, but if it does, then KB2695422 is the culprit.<br />
<br />
Update: Here's a solution that should work - but only if you have access to change the iFrame page's code. - <a href="http://matthewmanela.com/blog/making-linking-between-anchors-in-an-iframe-work-in-firefox-11-and-above/comment-page-1/#comment-1787">http://matthewmanela.com/blog/making-linking-between-anchors-in-an-iframe-work-in-firefox-11-and-above/comment-page-1/#comment-1787</a><br />
<br /><br />
I haven't found a good solution to this yet. If you setup the iFrame so that the height is less than the page height, you will somewhat fix the problem. It will cause you to have scroll bars, and the iFrame will scroll. It won't cause the outer page to scroll though, so you're users won't be able to see it.<br />
<br />
The other solution I'm looking into is changing the code on the page inside the iFrame so that it uses JavaScript to scroll the page. Hopefully this will work for me, but for you, you may not be able change the code inside the iFrame.<br />
<br />
I will note that this isn't specifically a SharePoint problem. You can test this out by creating a page that has an iFrame in it. The iFrame height should be set long enough that it's taller than the page - something like 2000+ pixels. <br />
(Replace the curly brackets with GT and LT's, obviously) <br />
At the top of the iframe's page, put a link to an anchor Tag {a href="#end"} go to end {/a}. At the bottom of the iFrame'd page, put an anchr Tag like {a name="end"}end{/a} the End should be way down at the bottom of the page, and not visible(add some <a href="http://lorizzle.nl/" target="_blank">gangsta Lorum Ipsum</a>, or just a bunch of {BR}'s to make the page really long, but not so long that it is more than the 2000 pixels you set it to and introduces some scroll bars. <br />
<br />
To the user, it would look like one long page with a link at the top and at the bottom. Clicking the 'go to end' link at the top of the page <em>should</em> scroll the page down to the bottom. It will if the page is not in an iFrame. If the page <em>IS</em> in an iframe, it won't work. Nothing happens. <br />
<br />
<br />
I guess this just shows that Firefox and Chrome are more secure browers. They fixed this 'hole' (and I'm not sure I'm ready to agree that this is a legit security issue given the consequenses of disabling the 'feature') more than a year and a half before IE <i>fixed </i>it.Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0tag:blogger.com,1999:blog-2143363931801234725.post-52188102537772865492012-02-02T12:05:00.000-08:002012-02-03T07:19:39.978-08:00SharePoint 2010 - Documents Tabwhile trying to make the transition from sharepoint 2007 to 2010, I've found that the default view of the ribon is confusing. What is the point of the browse tab? Especially if you're in a document library/list? In 07, useres have quick access to upload a document or create a new list item. In 2010, they must click a tab on the ribbon to be able to see that button.<br />To solve that problem, I want the documents or list items tab to be automatically selected for them.<br /><br />I found two blogs that talk about this issue. I found that neither one really did what I want.<br /><br />This site talks about how to set it as the default using the query string. Thought this might work for some, I can't change every link on every page to pass this as a parameter.<br /><a href="http://www.iotap.com/Blog/tabid/673/entryid/149/Displaying-default-tab-in-Sharepoint-2010-Ribbon.aspx">http://www.iotap.com/Blog/tabid/673/entryid/149/Displaying-default-tab-in-Sharepoint-2010-Ribbon.aspx</a><br />This second page talks about using javascript to set it to a custom tab based on some rules you setup in the javascript. I simplified this code for my use:<br /><a href="http://code.bkwdesign.com/2011/01/11/using-jquery-to-activate-default-sharepoint-tab/">http://code.bkwdesign.com/2011/01/11/using-jquery-to-activate-default-sharepoint-tab/</a><br /><br />There is probably a better way to do this using some built in sharepoint javascript funciton. I found one blog post that mentioned something like that, but I was unable to get it to work. <br /><br />The following code will automatically select the Documents tab in a document library and the List Item tab in a list. In any other case it should not do anything, so it's safe to put on every page. You could also add in other options to handle pages librarys or some other case I haven't run into yet.<br />I added this code to my master page, but you could easily add it to a javascript file and add a reference to your master page or page layout or whatever, even put it in a Content Editor for one particular page. <br /><br /><br /><br />Here's the code:<br /><br /><br /><script type="text/javascript"><br /><br /> function setTabDefault(tabid){<br /> $("li[id=" + tabid + "] a span.ms-cui-tt-span").trigger('click');<br /> }<br /> $(document).ready(function () {<br /> <br /> setTimeout("setTabDefault('Ribbon.Document-title');",150);<br /> setTimeout("setTabDefault('Ribbon.ListItem-title');",150); <br /> <br /> });<br /> <br /></script>Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0tag:blogger.com,1999:blog-2143363931801234725.post-70084119756492621782010-12-02T14:54:00.000-08:002010-12-02T15:18:05.804-08:00Installing SharePoint 2010 Without Internet AccessRecently, I was forced to install SharePoint 2010 on a network without internet access. Due to the fact that SP2010 requires about a dozen prerequisites, this is much more difficult than it should be.<br />There is a way to set up the installer to not attempt to access the internet to download the prerequisites.<br />First, You'll need a computer that has internet access so that you can download the prerequisites.<br />Fortunately, someone did the work for me to do this part.<br />The<a href="http://autospinstaller.codeplex.com/releases/view/44442"> SharePoint PowerShell script to download all the prerequisite files</a><br /><br />SharePoint can be downloaded as an ISO from Microsoft. You'll need to get this and copy all the files into a directory somewhere. Put all the prerequisite files in the ""PrerequisiteInstallerFiles" folder.<br />This list assumes you are installing on Server 2008 R2. Server 2008 has some slightly different items and hotfixes<br /><br />Next, create a file called: "PrerequisiteInstaller.Arguments.txt". Open it up in notepad and add:<br /><br />/SQLNCli:PrerequisiteInstallerFiles\sqlncli.msi /ChartControl:PrerequisiteInstallerFiles\MSChart.exe /IDFXR2:PrerequisiteInstallerFiles\Windows6.1-KB974405-x64.msu /Sync:PrerequisiteInstallerFiles\Synchronization.msi /FilterPack:PrerequisiteInstallerFiles\FilterPack\FilterPack.msi /KB976462:PrerequisiteInstallerFiles\Windows6.1-KB976462-v2-x64.msu /KB976394:PrerequisiteInstallerFiles\Windows6.0-KB976394-x64.msu /ADOMD:PrerequisiteInstallerFiles\SQLSERVER2008_ASADOMD10.msi /Speech:PrerequisiteInstallerFiles\SpeechPlatformRuntime.msi /SpeechLPK:PrerequisiteInstallerFiles\MSSpeech_SR_en-US_TELE.msi /ReportingServices:PrerequisiteInstallerFiles\rsSharePoint.msi<br /><br /><br />NOTE: This should all be on one line! No New lines!<br /><br /><br />When you run the SharePoint setup.exe, it'll run the PrerequisiteInstaller.exe which will read the arguments file, and load the files from the prerequisites installer directory instead of trying to go to the internet!Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0tag:blogger.com,1999:blog-2143363931801234725.post-16369677957594441292010-11-10T08:11:00.000-08:002010-11-10T08:19:28.194-08:00Mapping Office Shared Template Location to a SharePoint LibraryA user asked if you can create a new word document from a template that is stored in SharePoint.<br />Normally, i'd say sure, lets make it a content type, add it to the doc library and just click new! But of course this doesn't work. how many users really do that anyway? None of mine.<br /><br />So, i found out there is a Word Workgroup Templates folder that you can set. When you click new, my templates, it will show you all the templates from that folder. It even creates tabs for the sub folders. Very nice.<br /><br />To set the folder in Word 2007<br /><ol><li>Word Options</li><li>General -> File Locations</li><li>Set the folder for Workgroup Template.</li></ol>Unfortunatly, you can't just enter a sharepoint URL, or even the Shared folder path (\\portal\somesite\templates\). It will give you an error saying "You cannot use an internet address here. Enter a path that points to a location on your computer or on the network".<br /><br />Solution:<br />Trick Word.<br /><br />Map a drive to the SharePoint folder path. Then tell word to point to that folder.<br /><br />I chose to map T:\ to \\portal\somesite\templates\ and then told word that my templates folder is t:\<br /><br />Problem solved!Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com2tag:blogger.com,1999:blog-2143363931801234725.post-61573402409878338672010-10-29T14:14:00.000-07:002010-10-29T14:25:29.269-07:00Programtically hiding SharePoint Document Information Panel in an Office DocumentUsing the Document Information Panel in SharePoint to show document information in Office can be very useful for users. I've used it to show information from a custom database inside of word so that users can find out what a document is about without having to visit the SharePoint site. This is very useful for when a user gets assigned a document from a sharepoint workflow.<br /><br />The problem with this was that documents that did not connect to our Document Management system would not show any data. In a perfect world, the *special* documents would use a different content type so that using a different Document Information Panel would be easy. This was not the case in our system.<br /><br />SO! How do you tell Word to hide the DIP when there is no data to show?<br /><br />Microsoft has put together a nice post on how to do it:<a href="http://msdn.microsoft.com/en-us/magazine/cc500578.aspx"> http://msdn.microsoft.com/en-us/magazine/cc500578.aspx</a><br /><br />The problem is that this does not work. This requires you to have access to the Word API. When using a SharePoint DIP you only have the InfoPath API.<br /><br />When the document loads, it attempts to get data from the database. The specific key to look-up data from the database is stored as a custom column on the document. Getting this is relatively easy, and many other blogs have written about it.<br /><br />When the data key is blank(a document that someone uploaded to the Doc Library, not a System generated file), we are unable to get any data. I would like to hide the DIP in this case, but we can't!<br /><br />Instead, I created a blank view. Then, in the OnLoad event, when the data can't be loaded, I set the default view to be the blank view. You could also do this the other way. Set the blank view as the default and then if data loads OK, set it to be your nice, pretty, data filled view.<br /><br /><br /><br />The code to set the default view is simple:<br /><span style="font-style: italic;">e.SetDefaultView(</span><span style="font-style: italic; color: rgb(163, 21, 21);font-size:85%;" >"Blank"</span><span style="font-style: italic;">) ;</span><br /><br />Where <span style="font-style: italic;">e</span> is passed to you in the FormEvents_Loading parameter: <span style="font-style: italic;">LoadingEventArgs e</span><br /><br /><br />Problem Solved!<br />I also put a nice note on the data filled view that says the data was unable to load, just in case someone switches over to that view.Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0tag:blogger.com,1999:blog-2143363931801234725.post-1462793044387047762010-09-24T09:11:00.000-07:002010-09-24T09:32:05.119-07:00Getting Rows of data as a single column in a SQL queryRecently, I've been working on a Sharepoint Based document workflow solution that uses a custom SQL database. I ran into an issue where I needed to join a database table and get many rows of data back as one column in a query.<br /><br />For example:<br />Table A:<br />ID, Name,<br /><br />Table B:<br />childName, ParentID<br /><br />where parent ID is table A.ID<br /><br /><br />I needed my results to be in the format:<br />A.ID, name, (childName1, ChildName2,ChildName3,....)<br /><br /><br />I found a few options,<br />One:<a href="http://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/"> http://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/</a><br /><br />Provided about a dozen different ways to do it. All of them looked overly complex.<br /><br />Then i stumbled upon:<br /><a href="http://johnnycoder.com/blog/2006/09/05/concatenate-multiple-rows-into-a-single-string/">http://johnnycoder.com/blog/2006/09/05/concatenate-multiple-rows-into-a-single-string/</a><br /><br />which provided a simple Scalar function to return the results. I took that method and put together a SQL Scalar function to do it:<br /><br /><span style="font-family:verdana;">CREATE FUNCTION GetChildrenFromParent<br />(<br /><br /><br /> @ParentID varchar(15)<br />)<br />RETURNS varchar(5000)<br />AS<br />BEGIN<br /><br /> DECLARE @ChildList varchar(5000)<br /><br /> SET @</span><span style="font-family:verdana;">ChildList</span><span style="font-family:verdana;"> = ''<br /><br /> select @</span><span style="font-family:verdana;">ChildList</span><span style="font-family:verdana;"> = coalesce(@</span><span style="font-family:verdana;"></span><span style="font-family:verdana;">ChildList</span><span style="font-family:verdana;"> </span><span style="font-family:verdana;">+ ', ', '') + childName from B where ParentID=@</span><span style="font-family:verdana;">ParentID </span><br /><span style="font-family:verdana;"><br /><br /><br /> -- Return the result of the function<br /> RETURN @</span><span style="font-family:verdana;"></span><span style="font-family:verdana;">ChildList</span><span style="font-family:verdana;"> </span><br /><span style="font-family:verdana;">END<br />GO</span><br /><br /><br />Then my SQL to get the data looks something like:<br />select *,<span style="font-family:verdana;">GetChildrenFromParent(id) as ChildNames from A</span><br /><br />This returns results like:<br /><br />1|Jeff| andy,mark,shannon<br /><br />so, the one column is a comma separated list of rows of data from another table.Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0tag:blogger.com,1999:blog-2143363931801234725.post-8703792424621641822010-04-22T06:55:00.001-07:002010-04-22T07:04:38.749-07:00How to make a film strip the default view for a Picture LibraryRecently, I was trying to make a picture library for a customer to store their pictures in. I would prefer to use the film strip view as the default view because I think it is nicer looking.<div><br /></div><div>The problem is that the details/filmstrip/thumbnails views are controlled via javascript, they do not change the URL when you visit the page and they cannot be set as the default view.</div><div><br /></div><div>I found:</div><div><a href="http://jyhuh.com/blog/2009/11/how-to-make-a-filmstrip-a-default-view-for-a-picture-library/">http://jyhuh.com/blog/2009/11/how-to-make-a-filmstrip-a-default-view-for-a-picture-library/</a></div><div>which has a pretty good description of how to fix this issue. Only problem is that the most important part, the javascript, is not visible!</div><div><br /></div><div>I've decided to post it here so that it's easy to find.</div><div><span class="Apple-style-span" style=" -webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; font-family:'Times New Roman';font-size:medium;"><script>SwitchViewStyle('filmstrip');</script></span></div><div><span class="Apple-style-span" style=" -webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; font-family:'Times New Roman';"><span class="Apple-style-span" style=" ;font-size:medium;"><script>SwitchViewStyle('filmstrip');</script ></span></span></div><div><span class="Apple-style-span" style=" -webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; font-family:'Times New Roman';">You'll need to add a content editor to the page, and add this javascript to it.</span></div><div><span class="Apple-style-span" style=" -webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; font-family:'Times New Roman';"><br /></span></div><div><span class="Apple-style-span" style=" -webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; font-family:'Times New Roman';">Make sure you add the content editor web part after the Picture Library View Web part. Otherwise, as the page loads, it runs the javascript before the Picture library exists and does nothing.</span></div>Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com9tag:blogger.com,1999:blog-2143363931801234725.post-75200399640760113782009-09-24T07:25:00.000-07:002009-09-24T07:32:29.178-07:00What happened to my SharePoint Usage Analysis Logs?Recently, I went to check my usage analysis logs and noticed that they were practically empty. No new data in weeks. I know that it was working before, but what changed? What's going on here?<br /><br />I double checked that everything was enabled in CA, the SSP and in the site settings, and everything was still good. I also noticed that my Log location was D:\UsageLogs. I remember setting that up, so i went and looked at the logs. There was data, but only for the CA site. After some checking, I realized what was going on.<br /><br />Make sure that all your App Pool accounts have Access to write to that location. For me, only CA had the correct rights. After giving the WSS_WPG and WSS_RESTRICTED_WPG and WSS_ADMIN_WPG groups Write permission to that folder, the logs immediately started filling up. No errors in the event logs were being created for me, or else I would have noticed this a long time ago.<br /><br /><br />Conclusion: Just give all your SharePoint accounts Admin access on your servers, it'll save you a LOT of headaches. Or, even better, just run everything under one account! HAHA, no, don't actually do this, but always check permissions when things aren't working as you expect.Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0tag:blogger.com,1999:blog-2143363931801234725.post-47442625602547550992009-08-07T11:16:00.001-07:002009-08-07T11:30:02.779-07:00Oh NO! I deleted a site that I shouldn't have!There is no recycle bin for sites. You probably know this, or you or your users found this out the hard way. This has happened to me more than once, and is the topic of a previous post about removing the 'delete this site' link.<br /><br />Let's stay that you haven't done that, and you don't have a nice SharePoint backup solution that will let you easily restore deleted content. A user has just deleted a site and has come to you to get it back.<br /><br />Possible solutions:<br /><span style="font-weight: bold;">Solution 1:</span><br />Copy your backups(you have those right? :P ) to another SharePoint server and create a new Web Application using that DB. That site should now have the content that you lost. Migrate it back over to your production portal.<br />This is a solution, but migration can be a pain the butt. Also, it assumes you've got backups. You'll lose all your version history, and information about created and last modified. If your site is small enough you might be able to make a template out of it and rebuild a new site from there. You might even be able to use stsadm to backup and restore. Both of those options will run into trouble if your site is too big or complex.<br /><br /><span style="font-weight: bold;">Solution 2:</span><br />Roll back the database to a time before this disaster happened. This solution will work if you don't have backups(I think) :) and everything will seem as if nothing happened. The down side is that you will have lost everything that has happened before the roll-back point. Hopefully you were alerted quickly to the problem.<br />First, make a backup of your transaction logs. if you already have backups, this should be enough. If you don't, you'll want to backup the site as well.<br />Next, Restore a Database. Give it a new name, and pick a time that you know to be before the problem happened(but not too far back or else you'll lose more data). Choose your live database as the place to restore from. You should see it list your backup's. If you didn't back up your transaction log, you'll only be able to restore to the last backup. That is why it is important to backup your transaction log before you start.<br />Once the restore is complete, you'll have a copy of the DB from before the problem happened. detach your production DB, and rename this restored copy to the production name. You should be good to go.<br />You may want to shut down the site when you do this so that no new data is uploaded, since it will be lost. If done correctly, you should be able to restore the site very quickly, but it depends on how big your content DB is and how much traffic you get.<br /><br />Good luck! And now go delete the "delete this site" link from your _layouts/settings.aspx page so that users can't do this again in the future :PJeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com2tag:blogger.com,1999:blog-2143363931801234725.post-54662307336881254302009-07-15T13:10:00.000-07:002009-07-15T13:12:46.694-07:00Add webpart to newform, DispForm and EditForm pagesLists and Doc libraries both have Edit and DispForm pages. These pages have webpart zones on them, and adding your own webpart is possible. For some reason, sharepoint doesn't want to let you do it(the Edit Form Site action is grey'd out).<br /><br />to get around this, add:<br />&PageView=Shared&ToolPaneView=2<br /><br />to the end of the URL. It will put the page into the browse to add a webpart view. You should be good to go from there.Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0tag:blogger.com,1999:blog-2143363931801234725.post-3805392743742433042009-07-14T07:49:00.001-07:002009-07-14T08:20:17.060-07:00Error Code Gotchas due to Policy SettingsI run into a few problems on almost every SharePoint install. <br />David Szabo has put together a great post on some of the common ones:<br /><a href="http://blogs.msdn.com/dszabo/archive/2008/01/02/some-more-moss-gotchas.aspx">http://blogs.msdn.com/dszabo/archive/2008/01/02/some-more-moss-gotchas.aspx</a><br /><br />I've also run into issues with search not being able to search itself. This is due to a registry setting to prevent the server from DOS'ing itself. Make sure you add all the possibly URL's that your site is running to the registry setting.<br /><a href="http://support.microsoft.com/kb/896861">http://support.microsoft.com/kb/896861</a><br /><br /><br /><br />One more thing that always seems to come up a few months after the SharePoint portal has been up and running causes IIS to lock up. You'll get lots of errors in the event log for 6398, 7076, and 6482. You also won't be able to administer IIS. This has to do with two processes trying to access IIS admin service at the same time. <br /><br />Install IIS Hotfix KB946517 and restart. This should fix the issue.<br /><br /><br />Problems with the timer service can also be caused by the scheduled task service being disabled. You'll re-enable it and it will turn off again. This is due to a group policy setting shutting it down. Have whoever manages the policy settings disable that setting on your sharepoint machines. <br /><br />Errors with DCOM settings: <br />The application-specific permission settings do not grant Local Activation permission for the COM Server application with CLSID - Error 10016<br />Check out: <a href="http://geekswithblogs.net/mhamilton/archive/2006/12/19/101568.aspx">http://geekswithblogs.net/mhamilton/archive/2006/12/19/101568.aspx</a><br /><br />http://geekswithblogs.net/mhamilton/archive/2006/12/19/101568.aspx<br /><br /><br /><br />I'm sure there will be more to come as I remember them.Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0tag:blogger.com,1999:blog-2143363931801234725.post-39130228870438804572009-07-01T06:47:00.001-07:002009-07-01T06:53:21.869-07:00Add Webparts Popup missing links and buttonsAfter a few days of modifying the CSS and master pages for SharePoint and thinking everything was good I found the Add Webparts popup had lost it's buttons and the Advanced Link!<br /><br />Tracking down what's going wrong can be tricky. This page uses the same CSS as the rest of your site, so good luck tracking down what you did. Also, this page doesn't work in Firefox, so using FireBug isn't an option(more on that later). And worst, IE won't let me right click and get Page Source!<br /><br />Fortunately, someone else has hit this problem:<br />http://sharethelearning.blogspot.com/2009/01/missing-buttons-and-link-on-add-web.html<br /><br />This person did the work to track down which page is used: <span style="font-weight:bold;">/_layouts/webpartgallerypickerpage.aspx</span><br /><br />Going directly to the link will let you us FireFox and Firebug to track down the problem. For me, I was setting the height of the page to Auto, not 100%. This caused the buttons and advanced link to appear near the top of the page, and behind the box that lets you pick webparts. I simply added:<br /><br /><span style="font-weight:bold;">#mainTable{<br /> height:100%;<br />}</span><br /><br />to my CSS file, and everything was fixed. :)Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com2tag:blogger.com,1999:blog-2143363931801234725.post-81315317115379015282009-04-15T13:42:00.000-07:002009-04-15T13:48:38.709-07:00View all Site content Blank after ImportRecently, A coworker and I had to move some sites around after a previous contractor setup the site with a bunch of different site collections and other junk that had to be removed as the site was moved to production.<br /><br />Instead of moving the whole database, we only wanted to move a certain group of sites, so we used stsadm to export the sites and import them on the new server. After Import, we noticed that the the Quick Launch bar showed the correct lists, and they worked fine, but if we went to the "View All Site Content" link, nothing showed up other than the recycle bin.<br /><br /><br />Not sure what was going on here, but we were able to fix it. Here's the process we went through:<br /><br />On the New server, backup the site.<br />Move the site(or change the URL) to something like <site>_old.<br />import the site to the original site's URL.<br />If everything looked good, delete the <site>_old site.<br /><br />Maybe something else was going on here, but it works now.Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0tag:blogger.com,1999:blog-2143363931801234725.post-54804267829638102052009-03-11T09:23:00.000-07:002009-03-11T09:32:47.824-07:00Disable Site DeletionHave you had a user delete a site, and then complain that it's gone? I have! The solution is to disable the ability to delete a site! <br /><br /><br />Simply add this line of code to the core.js, or application.master, or anywhere else that is going to get loaded by the settings.aspx page. There is probably a dozen places you could add this code.<br /><br />NOTE: I haven't tested this out, but at worst, all it's going to do is find the Delete web link, and hide it on the page. It doesn't actually change the page, just the display of the page. You could disable javascript in your browser and it would re-appear.<br /><br />document.getElementById("ctl00_PlaceHolderMain_SiteAdministration_RptControls_DeleteWeb").parentElement.parentElement.style.display="none";<br /><br />This basically says, find the "Delete this site" link, and turn off the display of that row in the table.<br /><br />If you do need to delete the site, you could always just go directly to the _layouts/deleteweb.aspx page on that site, and click to delete. <br /><br />Hopefully this will save you a few headaches.<br /><br />You could get fancy, and change the link's HREF to be a mailto: link to your <br />helpdesk or sys admins. That way, if they want to delete a site, they will have to send you an email. Then you simply remove everyone's permissions. They'll think it's deleted, but you could easily bring it back if you needed to.<br /><br /><br />Good Luck!Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0tag:blogger.com,1999:blog-2143363931801234725.post-24872795030312903522009-02-11T13:57:00.000-08:002009-03-10T13:56:36.837-07:00Attachments and Customized FormsMicrosoft has relased a new hotfix for Sharepoint:<br /><br />http://support.microsoft.com/kb/960311/en-us?spid=11677&sid=global<br /><br /><br /><br /><br />This allows you to use attachments and customized forms. Without this hotfix, the customized form you would get a popup error saying: "This form was customized not working with attachment. "<br /><br />Supposedly, this hotfix fixes the issue. You will probably have to rebuild your forms though.<br /><br /><br />UPDATE:<br /><br />There is another hotfix related to this issue. There are two parts, one is to update your Sharepoint server, the second is to add data to the forms. I've tested this out and it works!<br /><br />http://support.microsoft.com/default.aspx?scid=kb;en-us;953271&sd=rss&spid=11373Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0tag:blogger.com,1999:blog-2143363931801234725.post-34547797072796641002009-02-03T13:58:00.000-08:002009-02-03T14:06:28.299-08:00Dynamic drop down lists on FormsOften, customers want options in one field to be driven by a different field in the form. For example, Categories and sub-categories. If the user picks A in field 1, field 2 should be filtered to only show options A1,A2,A3, etc.<br /><br />Since sharepoint doesn't have a good way to do this, and I dont want to build a custom user control, javascript is the solution.<br /><br />First, you'll want to create a function to clear out all the options from the second drop down list. Something like:<br /><br />function clearSubCat(){<br /><br /> var subCat = getTagFromIdentifierAndTitle("select","DropDownChoice","Sub-Category");<br /> <br /> var opts = subCat.options;<br /> <br /> var len = subCat.length;<br /> for (i = 0; i <len; i++)<br /> {<br /> subCat.remove(0);<br /> }<br />}<br /><br /><br />If you google getTagFromIdentifierAndTitle, you'll find a nice blog article and the source for this function. <br /><br />Second, you'll need a function to re-populate the drop down list given the value of colum 1:<br /><br />function populateSubCat(){<br /> var subCat = getTagFromIdentifierAndTitle("select","DropDownChoice","Sub-Category");<br /> var catList = getTagFromIdentifierAndTitle("select","DropDownChoice","Category");<br /> <br /> var cat = catList.options[catList.selectedIndex].text;<br /> <br /> clearSubCat();<br /> switch(cat)<br /> {<br /> case "Directives":<br /> var optA = document.createElement('option');<br /> optA.text = "DOI"; <br /> optA.value = "DOI"; <br /> subCat.options.add(optA); <br /><br /> } <br />}<br /><br />You'll have to add more cases to the switch statement. <br />Basically, this gets the DOM object for the two categories, and inserts values into the sub-category based on the value in the category.<br /><br />Third, you'll want to setup the javascript to set the onChange event for the Category drop down list.<br /><br />function filterSubCat(){<br /> var catList = getTagFromIdentifierAndTitle("select","DropDownChoice","Category");<br /> catList.onchange = function() {populateSubCat()};<br /> populateSubCat();<br />}<br /><br /><br />I call the filterSubCat() function in a content editor webpart.<br /><br /><br />In my case, the Drop down list values were limited and Static. If your lists are more dynamic this may not be the best solution.Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com2tag:blogger.com,1999:blog-2143363931801234725.post-42644380390223823412009-01-15T07:33:00.000-08:002009-01-15T07:49:27.243-08:00Creating custom Item ID'sA customer would like to use custom ID's to track items throughout the site. These ID's are something like:<br />TYPE_DATE_ID<br /><br />I would like to delete the Title field and use this throughout the system, but this causes some problems. I can't rename Title without renaming Title on every item in the entire site. I can hide title, and create my own column, but then I have to create custom datasheet views everywhere because Title is the only field that has a link to the new form, and has the drop down list available. I suppose I'll just have to use the Edit link everywhere.<br /><br />Creating a custom ID field should be easy right? Just use a calculated field you say! <br />Sadly, it's not that simple. First, I would like to have the ID portion padded to 4 spaces. ie. 0001, 0021, etc. This causes some troubles. Second and most important, the ID field is set after the calculated field is set. If you do this strictly through a calculated field, you'll end up with TYPE_DATE_0000.<br /><br />Ok, so then do it with a workflow! This solves the ID being 0 problem, but the Designer workflows don't seem to support the formula's needed to create the Date in a YYYYMMDD format. If you just use Created as the date, you'll end up with something like: TYPE_1/15/2009 8:43:03 AM_0001. UGLY. <br /><br />The solution is to use a hybrid of both. Create a calculated field that is:<br />=TEXT(Created,"yyyyMMdd")<br />Next, create a workflow that is activated on New item created for you're list. The workflow will need two steps. <br />The first step is to create the necessary padding for your ID. Mine is something like:<br /><br />If ID < 10 set variable 'padding' = '000'<br />if ID >=10 and ID <100 set variable 'padding' = '00'<br />If ID >=100 and ID <1000 set variable 'padding' = '0'<br /><br />Next, set the prefix = 'TYPE' or whatever you want. If this is dynamic, you'll need another step to set this to be what you want.<br /><br />Use the string builder to put all your variables together. [PREFIX]_[createDate]_[padding][ID]<br /><br />Set your Identifier field to the new variable you created with the string builder. DONE!Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0tag:blogger.com,1999:blog-2143363931801234725.post-44085777422432248752009-01-13T10:44:00.000-08:002009-01-15T07:33:43.130-08:00Changing Title Site Column nameWhen creating a new site content type you are required to choose a type that it will inherit from. The simplest type is a list content type who's parent is 'Item'. <br />The problem is that you'll end up with a default column called 'Title'. Great, what if I don't want a column called Title? <br /><br />DO NOT ATTEMPT TO CHANGE THIS COLUMN!!<br />This column is inherited by every list item across the site. If you change the name, the entire site will change! What really gets you, is that you can't change it back!<br />The form checks and see's that the name 'Title' is reserved. It doesn't care that you're not using it anymore.<br /><br />If you've already done this, There is a way to change it back. (<a href="http://www.eggheadcafe.com/software/aspnet/29625393/title-site-column.aspx">http://www.eggheadcafe.com/software/aspnet/29625393/title-site-column.aspx</a>)<br /><br /><br />Here's the basic idea:<br />Open the page that lets you change the name and save it to your computer(the complete page). Open the page, and remove the javascript that does the check:<br /><br /><span style="font-style:italic;">if (doesFieldNameConflict(DisplayName))<br />{<br />alert(L_alert3_Text);<br />frm.DisplayName.focus();<br />return false;<br />}<br /></span><br /><br />Open the page, rename it back to "Title" and hit save. SharePoint isn't smart enough to realize that the page lives on your computer and not the server. It will let you change it back. <br /><br /><br />The right solution is to edit your site content type, and change the 'Title' field to be hidden. Then create a new column called Title, or whatever you want to name it. <br /><br />The problem this creates, is that whenever you have a view of the list, the Title field is the one that is used to link to the DispForm, and also has the drop down list of options to modify that row. <br />The only solutions I've seen so far is to create your content types with a feature. This isn't a good solution when you're trying to prototype something though.Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0tag:blogger.com,1999:blog-2143363931801234725.post-38431220615568787662009-01-13T06:32:00.001-08:002009-02-04T13:47:09.717-08:00Importing and Exporting WebpartsFrequently, I find myself creating XSLT data view webparts because the out of the box layouts don't cut it. <br /><br />I found that if a webpart is added to the page using the browser, then edited in designer and converted into a XSLT view, it can cause problems. I was no longer able to export the webpart and import it to another page. <br /><br />Unfortunately, to work on a webpart in designer, it has to be on a web part page since you have to break the page from the template. I then exported the webpart and imported it to a normal publishing page. The webpart imports, but causes the page to error out. You must then edit the page in the maintenance view and remove the offending webpart(the last one listed, since they are listed in order they were added to the page).<br /><br />So, add your webparts in Designer as a Dataview, edit them in designer, and move them later. Sorry if you're trying to edit one you created in the browser and converted to an XSLT view, just start over and save your self a headache later.Jeff Lesterhttp://www.blogger.com/profile/15100537688793888318noreply@blogger.com0