Thursday, December 8, 2011

Monitoring SharePoint Recycle Bin via Powershell and Email

I was doing some SharePoint 2010 work for a small investment / financial organization. One of the request from the president is to add some mechanism to monitor the SharePoint recycle bin.

For investment film, every piece of investment related document is important to the client profile, some documents need to remain in the profile for longer than 10 years. The president is worry if documents were accidentally/intentionally deleted from the SharePoint, it may be too late for administrator to discover missing documents. Currently, they have not implemented any record management solution yet.

So I wrote them a script to send out recycle bin status (weekly). the email receiver will decide if they want to restore any document manually.

Here is the script will do.
1) List recent deletion (from recent x number of day)
2) List deleted documents that will be removed from first-stage recycle bin
3) List deleted documents was removed from user recycle bin.
4) Email will provide link to the Recycle bin

Sample email screenshot


PowerShell Script
##==================================================================##
## PowerShell Send out Recycle Bin Status
## Version: 1.0
## Last updated: 08 Dec, 2011
## Description: This script will perform the following
## 1) List recent deletion (from recent x number of day)
## 2) List deleted documents that will be removed from first-stage recycle bin
## 3) List deleted documents was removed from user recycle bin.
## 4) Send out status email
##==================================================================##

#$snapin = Get-PSSnapIn -name "Microsoft.SharePoint.PowerShell" -registered
#if(!$snapin)
#{
Add-PSSnapIn Microsoft.SharePoint.PowerShell
#}

$today = (Get-Date -Format yyyy-MM-dd)

# Host
$url = "http://SP2010/";

#Variables for Email
$emailFrom = "SP2010@SharePoint4Newbie.com"
$emailTo = "administrator@SharePoint4Newbie.com"
$smtpServer = "MailServer.SharePoint4Newbie.com"
$emailSubject = "SharePoint Recycle Bin Report - ($today)"

# Number of day in Recycle Bin
$RecentDeletionDay = 7

# Warning Item in recycle bin that is older then x day.
$WarningDay = 30;

$siteCollection = New-Object Microsoft.SharePoint.SPSite($url);

# First lets create a temp text file, where we will later save the recycle bin info
$recycleBinTmpFile = "Recyclebin.htm"



#Create a Temp File
New-Item -ItemType file $recycleBinTmpFile -Force

# Function to write the HTML Header to the file
Function writeHtmlHeader
{
param($fileName)
$date = ( get-date ).ToString('yyyy/MM/dd')
Add-Content $fileName "<html>"
Add-Content $fileName "<head>"
Add-Content $fileName "<meta http-equiv='Content-Type' content='text/html; charset=iso-8859-1'>"
Add-Content $fileName '<title>$emailSubject</title>'
Add-Content $fileName '<STYLE TYPE="text/css">'
Add-Content $fileName "<!--"
Add-Content $fileName "td {"
Add-Content $fileName "font-family: Tahoma;"
Add-Content $fileName "font-size: 11px;"
Add-Content $fileName "border-top: 1px solid #F0F0F0;"
Add-Content $fileName "border-right: 1px solid #F0F0F0;"
Add-Content $fileName "border-bottom: 1px solid #F0F0F0;"
Add-Content $fileName "border-left: 1px solid #F0F0F0;"
Add-Content $fileName "padding-top: 0px;"
Add-Content $fileName "padding-right: 0px;"
Add-Content $fileName "padding-bottom: 0px;"
Add-Content $fileName "padding-left: 0px;"
Add-Content $fileName "}"
Add-Content $fileName "body {"
Add-Content $fileName "margin-left: 5px;"
Add-Content $fileName "margin-top: 5px;"
Add-Content $fileName "margin-right: 0px;"
Add-Content $fileName "margin-bottom: 10px;"
Add-Content $fileName ""
Add-Content $fileName "table {"
Add-Content $fileName "border: thin solid #000000;"
Add-Content $fileName "}"
Add-Content $fileName "-->"
Add-Content $fileName "</style>"
Add-Content $fileName "</head>"
Add-Content $fileName "<body>"
Add-Content $fileName "<table width='100%' cellspacing='0' cellpadding='2' >"
Add-Content $fileName "<tr bgcolor='#F0F0F0'>"
Add-Content $fileName "<td colspan='7' height='25' align='center'>"
Add-Content $fileName "<font face='tahoma' color='#003399' size='3'><strong>Site Collection Recycle Bin as of $date</strong></font>"
Add-Content $fileName "</td>"
Add-Content $fileName "</tr>"
Add-Content $fileName "</table>"
}

# Function to write the HTML Header to the file
Function writeTableHeader
{
param($fileName, $groupHeader)
Add-Content $fileName "<table cellspacing='0' cellpadding='2' width='100%'><tr bgcolor=#F0F0F0>"
Add-Content $fileName "<td colspan=6><b>$groupHeader</b></td>"
Add-Content $fileName "</tr>"
Add-Content $fileName "<tr bgcolor=#F0F0F0>"
Add-Content $fileName "<td>Deleted Date</td>"
Add-Content $fileName "<td>Deleted By</td>"
Add-Content $fileName "<td>Type</td>"
Add-Content $fileName "<td>Name</td>"
Add-Content $fileName "<td>Original Path</td>"
Add-Content $fileName "<td>Owner</td>"
Add-Content $fileName "</tr>"
}

# Function to write the Table Footer
Function writeTableFooter
{
param($fileName, $groupFooter)
Add-Content $fileName "<tr>"
Add-Content $fileName "<td colspan=6><b>$groupFooter</b></td>"
Add-Content $fileName "</tr>"
Add-Content $fileName "</table></br>"
}

# Function to write the HTML Footer to the file
Function writeHtmlFooter
{
param($fileName)
Add-Content $fileName "</body>"
Add-Content $fileName "</html>"
}

# Function to write the Recycle Bin Item
Function writeRecycleBinItem
{
param($fileName, $deletedDate, $deletedBy, $itemType, $itemName, $OriginalPath, $itemOwner)
Add-Content $fileName "<tr>"
Add-Content $fileName "<td>$deletedDate</td>"
Add-Content $fileName "<td>$deletedBy</td>"
Add-Content $fileName "<td>$itemType</td>"
Add-Content $fileName "<td>$itemName</td>"
Add-Content $fileName "<td>$OriginalPath</td>"
Add-Content $fileName "<td>$itemOwner</td>"
Add-Content $fileName "</tr>"
}


# Function to send email
Function sendEmail
{
param($from,$to,$subject,$smtphost,$htmlFileName)
$body = Get-Content $htmlFileName
$smtp= New-Object System.Net.Mail.SmtpClient $smtphost
$msg = New-Object System.Net.Mail.MailMessage $from, $to, $subject, $body
$msg.isBodyhtml = $true
$smtp.send($msg)
}

writeHtmlHeader $recycleBinTmpFile

#Querying First Stage recyle bin
$recycleQuery = New-Object Microsoft.SharePoint.SPRecycleBinQuery;
$recycleQuery.OrderBy = [Microsoft.SharePoint.SPRecycleBinOrderBy]::DeletedDate;
$firstStageRecycledItems = $siteCollection.GetRecycleBinItems($recycleQuery);

$count = $firstStageRecycledItems.Count;
write-host "Total item after query: $count";

$LastWeek = [DateTime]::Now.AddDays(-$RecentDeletionDay);
$y = 0;

writeTableHeader $recycleBinTmpFile "<a href='http://SP2010/_layouts/AdminRecycleBin.aspx'>First Stage Recycle Bin </a>since last week ($LastWeek)"

for($x = $count; $x -ge 0; $x--)
{
if ( $firstStageRecycledItems[$x].DeletedDate -ge $LastWeek)
{
writeRecycleBinItem $recycleBinTmpFile $firstStageRecycledItems[$x].DeletedDate.ToString() $firstStageRecycledItems[$x].DeletedByName $firstStageRecycledItems[$x].ItemType $firstStageRecycledItems[$x].Title $firstStageRecycledItems[$x].DirName $firstStageRecycledItems[$x].AuthorName;
$y++
}
}
writeTableFooter $recycleBinTmpFile "Total $y items"



$WarningDate = [DateTime]::Now.AddDays(-$WarningDay);
$j = 0;

writeTableHeader $recycleBinTmpFile "First Stage Recycle Bin - These items has been deleted for longer than $WarningDay days ($WarningDate)";

for($i = 0; $i -lt $count; $i++)
{
if ( $firstStageRecycledItems[$i].DeletedDate -le $WarningDate)
{
writeRecycleBinItem $recycleBinTmpFile $firstStageRecycledItems[$i].DeletedDate.ToString() $firstStageRecycledItems[$i].DeletedByName $firstStageRecycledItems[$i].ItemType $firstStageRecycledItems[$i].Title $firstStageRecycledItems[$i].DirName $firstStageRecycledItems[$i].AuthorName;
$j++
}
}
writeTableFooter $recycleBinTmpFile "Total $j items"

#querying second stage recyle bin
$recycleQuery.ItemState = [Microsoft.SharePoint.SPRecycleBinItemState]::SecondStageRecycleBin;
$secondStageRecycledItems = $siteCollection.GetRecycleBinItems($recycleQuery);

$count = $secondStageRecycledItems.Count;
write-host "Total item after query: $count";

$LastWeek = [DateTime]::Now.AddDays(-$RecentDeletionDay);
$y = 0;


writeTableHeader $recycleBinTmpFile "<font color='Red'><a href='http://SP2010/_layouts/AdminRecycleBin.aspx?View=2'>Deleted from end user Recycle Bin </a> since last week </font>($LastWeek)"

for($x = $count; $x -ge 0; $x--)
{
if ( $secondStageRecycledItems[$x].DeletedDate -ge $LastWeek)
{
writeRecycleBinItem $recycleBinTmpFile $secondStageRecycledItems[$x].DeletedDate.ToString() $secondStageRecycledItems[$x].DeletedByName $secondStageRecycledItems[$x].ItemType $secondStageRecycledItems[$x].Title $secondStageRecycledItems[$x].DirName $secondStageRecycledItems[$x].AuthorName;
$y++
}
}
writeTableFooter $recycleBinTmpFile "Total $y items"

writeHtmlFooter $recycleBinTmpFile

#send out email
sendEmail $emailFrom $emailTo $emailSubject $smtpServer $recycleBinTmpFile

$siteCollection.Dispose();

##==================================================================##


Note: the script will not change anything in your system but send out email.
Please feel free to copy the script and save to the harddrive (example c:\Powershell\RecycleBin_Status.ps1), then create a Windows Tasks Scheduler to execute the script weekly...

Please TEST the script multiple times before using in production...

Hope this help!


Tuesday, August 16, 2011

Colour coding to your SharePoint 2007 calendar

One of my recent project was to support and perform some enhancement on a MOSS 2007 for our provincial government. Due to the nature of the government, every production deployment is a complex process, it requires different level of QA, different approval from IT and infrastructure approval and lengthy change request... In short, if you have created a simple "Hello World" webpart, it will take at least a month to be deployed to production environment.

So my task was to add some colour coding to SharePoint Calendar for different event type... in the past, I have done it by creating a calendar webpart using the Calendar control in ASP.Net, which I have full control of the calendar. Due to limited timeline, I turned to Christophe's blog who have published an article on how to style MOSS 2007 calender without deploy a single WSP... and I love it!

Add color coding to your SharePoint 2007 calendar in 15 minutes
http://blog.pathtosharepoint.com/2010/04/06/tutorial-add-color-coding-to-your-sharepoint-2007-calendar-in-15-minutes/

Also, check out the HTML Calculated Column: solutions for SP 2010
http://blog.pathtosharepoint.com/2010/06/16/html-calculated-column-solutions-for-sp-2010-part-iv

The biggest benefit of this approach is the quick turn around time for the client, not complex deployment process, my client is welling to negotiate due to some styling limitation, but they love the quick turn around and happy with the result.

Thanks Christophe! you are my hero!

Friday, April 8, 2011

Change ADS Password Webpart

I got an interesting request to build a SharePoint webpart that allow user to change their AD password. Mean, the webpart need to authenticate and update user password against LDAP.

Supposed if your environment have Office Outlook Web Access (OWA), the same feature can be found here.
  • In OWA.
  • Click "Options" button on the top right corner
  • The click the "Change Password" in left navigation.
  • The change password screen should look like this.

There are third party vendors out there who sell the similar webpart as well...

Anyway, to fulfill the request, I have built a SP 2010 webpart which will provide the same functionality, and because it is a webpart, you can deploy the feature to your site collection and place it on any page. The visual of the webpart will look like image below.


This sample webpart will
  • Show user login name
  • Password last update
  • Allow user to change existing password
A copy of this project can be download from here (Coming Soon)… 

If you are keen of building one from scratch... please follow the instruction below. Before you start, you need to obtain the LDAP information from your network administrator.

Change ADS Password Webpart

1) Launch Visual Studio 2010
2) On File menu, select New Project
3) Under Visual C#, SharePoint, 2010, Select Visual Web Part
4) Project Name: ChangeADSPassword
5) Click “OK” to create the project files

6) In Solution Explorer
7) Delete “VisualWebPart1” (created by default)
8) Add a New Item
9) Under Visual C#, SharePoint, 2010, select “Visual Web Part”
10) Named ChangePasswordWebPart, click OK
11) You may be prompt to specific your SharePoint URL
  • Once the project is created, these are the files (in green and red) you need to make changes


12) Right click Reference and Add “System.DirectoryServices

13) Double click “ChangePasswordWebPartUserControl.ascx
14) Paste the following code at the end of the page

<style type="text/css">
    .requiredField
    {
        color: #ff0000;
    }
    .xxSmallfont
    {
        font-size: xx-small;
    }
</style>
<br />
<table border="0">
    <tr>
        <td>You are logged on as:</td>
        <td><asp:Label ID="lblUser" runat="server" Text="" style="font-weight: 700"></asp:Label></td>
    </tr>
    <tr>
        <td>Password last updated on:</td>
        <td><asp:Label ID="lblPwdLastSet" runat="server" Text="" style="font-weight: 700"></asp:Label></td>
    </tr>
    <tr>
        <td></td>
        <td></td>
    </tr>
    <tr>
        <td>Current Password:<span class="requiredField">*</span></td>
        <td><asp:TextBox ID="oldpassword" runat="server" TextMode="Password"></asp:TextBox></td>
    </tr>
    <tr>
        <td>New Password:<span class="requiredField">*</span></td>
        <td><asp:TextBox ID="newpassword" runat="server" TextMode="Password"></asp:TextBox></td>
    </tr>
    <tr>
        <td>Confirm New Password:<span class="requiredField">*</span></td>
        <td><asp:TextBox ID="checknewpassword" runat="server" TextMode="Password"></asp:TextBox></td>
    </tr>
    <tr>
        <td>
        </td>
        <td>
            <asp:Button ID="btnChangePassword" runat="server" Text="Change Password"
                onclick="btnChangePassword_Click" /></td>
    </tr>
    <tr>
        <td colspan=2><asp:Label ID="lblOutput" runat="server" Text=""></asp:Label>
        </td>
    </tr>
</table>

15) Right Click and select “View Code” or open ChangePasswordWebPartUserControl.ascx.cs
16) Add the following code at the using section

using System.DirectoryServices;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;

17) Highlight the
        protected void Page_Load(object sender, EventArgs e)
        {
        }

18) Replace with 

        private string ldapPath;

        public string LdapPath
        {
            get { return ldapPath; }
            set { ldapPath = value; }
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            try
            {
                if (!Page.IsPostBack)
                {
                    string strLoginName = GetCurrentUser();
                    lblUser.Text = strLoginName;
                    lblPwdLastSet.Text += SearchUserByProperties(strLoginName, "pwdLastSet");
                }
            }
            catch (Exception ex)
            {
                lblOutput.Text = ex.Message.ToString();
            }
        }

        protected void btnChangePassword_Click(object sender, EventArgs e)
        {
            if ((oldpassword.Text.Length > 0) || (newpassword.Text.Length > 0) || (checknewpassword.Text.Length > 0))
            {
                if (newpassword.Text.ToString() == checknewpassword.Text.ToString())
                {
                    string strLoginName = GetCurrentUser();
                    lblOutput.Text = ChangeAcctPassword(strLoginName, oldpassword.Text, checknewpassword.Text);
                }

                else
                {
                    lblOutput.Text = "Passwords don't match";
                }
            }
            else
            {
                lblOutput.Text = "You must provide all require field";
            }
        }

        /// <summary>
        /// Update Account Password
        /// </summary>
        /// <param name="strLoginName"></param>
        /// <param name="oldPassword"></param>
        /// <param name="newPassword"></param>
        /// <returns></returns>
        private string ChangeAcctPassword(string strLoginName, string oldPassword, string newPassword)
        {
            string strOutput = string.Empty;
            string domainPath = ldapPath;

            try
            {
                DirectoryEntry domain = new DirectoryEntry(domainPath, strLoginName, oldPassword, AuthenticationTypes.Secure);

                DirectorySearcher search = new DirectorySearcher(domain);
                search.Filter = string.Format("(sAMAccountName={0})", strLoginName);
                search.SearchScope = SearchScope.Subtree;
                search.CacheResults = false;

                SearchResultCollection results = search.FindAll();
                if (results != null)
                {
                    try
                    {
                        foreach (SearchResult result in results)
                        {
                            domain = result.GetDirectoryEntry();
                        }

                        domain.Invoke("ChangePassword", new object[] { oldPassword, newPassword });
                        domain.CommitChanges();
                        strOutput += "<BR>Password is changed";
                    }
                    catch (Exception ex)
                    {
                        strOutput += string.Format("<BR>{0}<BR>Password couldn't be changed due to Password Policy restrictions", ex.Message.ToString());
                    }
                }
                else
                {
                    strOutput += "<BR>User not found or bad password";
                }
            }
            catch (Exception er)
            {
                strOutput += er.Message.ToString();
            }
            return strOutput;
        }

        /// <summary>
        /// Search AD User by properties
        /// </summary>
        /// <param name="LoginName"></param>
        /// <param name="strProperty"></param>
        /// <returns></returns>
        public string SearchUserByProperties(string LoginName, string strProperty)
        {
            string strReturn = string.Empty;
            string domainPath = ldapPath;

            using (DirectoryEntry domain = new DirectoryEntry(domainPath))
            {
                try
                {
                    DirectorySearcher search = new DirectorySearcher(domain);
                    search.Filter = string.Format("(&(objectclass=user)(objectcategory=person)(sAMAccountName={0}))", LoginName);
                    search.SearchScope = SearchScope.Subtree;
                    search.PropertiesToLoad.AddRange(new string[] { "cn", "sn", strProperty });

                    SearchResultCollection results = search.FindAll();
                    if (results != null)
                    {
                        foreach (SearchResult result in results)
                        {
                            if (result != null)
                            {
                                TimeSpan pwdLastSet = TimeSpan.MinValue;

                                if (result.Properties.Contains(strProperty))
                                {
                                    pwdLastSet = TimeSpan.FromTicks((long)result.Properties[strProperty][0]);

                                    long fileTime = (long)result.Properties[strProperty][0];
                                    DateTime pwdSet = DateTime.FromFileTime(fileTime);

                                    //strReturn += result.Properties["cn"][0];
                                    strReturn = pwdSet.ToString();
                                }
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    strReturn = ex.Message;
                }
            }
            return strReturn;
        }

        /// <summary>
        /// Strip off domain and return current user login name
        /// </summary>
        /// <returns></returns>
        private string GetCurrentUser()
        {
            SPWeb webContext = SPControl.GetContextWeb(Context);
            string strLoginName = webContext.CurrentUser.LoginName;

            int iPosition = strLoginName.IndexOf("\\") + 1;
            strLoginName = strLoginName.Substring(iPosition);
            return strLoginName;
        }

19) Then double click on ChangePasswordWebpart.cs
20) Highlight the

        protected override void CreateChildControls()
        {
            Control control = Page.LoadControl(_ascxPath);
            Controls.Add(control);
        }

21) Replace with

        // Visual Studio might automatically update this path when you change the Visual Web Part project item.
        private const string _ascxPath = @"~/_CONTROLTEMPLATES/ChangeADSPassword/ChangePasswordWebPart/ChangePasswordWebPartUserControl.ascx";

        //default LDAP (check with administrator for actual dns)
        private string strDefaultLDAPPath = "LDAP://DC01.SP2010.com";

        private string ldapDomain;
        [Category("Custom Properties"),
        Personalizable(PersonalizationScope.Shared),
        WebBrowsable, WebDisplayName("Domain Path:(LDAP://domain.com)"),
        WebDescription("LDAP Domain Path")]
        public string LdapDomain
        {
            get { return ldapDomain; }
            set { ldapDomain = value; }
        }

        protected override void CreateChildControls()
        {
            Control control = Page.LoadControl(_ascxPath);
            ChangePasswordWebPartUserControl ctl = control as ChangePasswordWebPartUserControl;
            ctl.LdapPath = ((ldapDomain != null && ldapDomain.Length > 0)) ? ldapDomain : strDefaultLDAPPath;
            Controls.Add(ctl);
            this.Title = "Change Password";
        }


22) Save All files then click “Build” to check for error


23) If successful, then click “deploy” to test your webpart
24) If you follow the instruction correctly, you should find ChangeADSPassword.wsp locate at the BIN folder (debug/release) of your project file location, which you can deploy manually.

25) Go to your development site
26) Add a webpart, under custom (should look like this).

27) After added onto the page, the visual should look like this.

28) Edit the webpart, under the Custom Properties section, is where you specify the LDAP information.

29) Stop Editing and start testing.

Done!

As said, OWA has the same built in password page... but if OWA is not available in your project for whatever reason, I hope you find this webpart useful! Good luck!

Also checkout
Codeplex for Change Password web part:
http://changepassword.codeplex.com/
Or, if you want the complete SharePoint AD TOOL
http://adselfservice.codeplex.com/

Disclaimer:
The information and code sample provide as it. I hope it will help you get going… The webpart should work in typical ADS and SP2010 environment; if not, please get further support from MSDN library, search engine or check out 3rd party webparts.
 
Thanks!

Monday, March 21, 2011

Where Is MOSS 2007 Site Directory Template in SharePoint 2010

If you have a MOSS 2007 environment, it is easy to create a Site Directory Site using the template... it is commonly used for cataloging sites, or some time served as sitemap in many organization...

Other day I was tasked to create Site Directory site in SP 2010...but the template is disappeared in SharePoint 2010... With some research. I come across a posting by Usman Afzal... the feature was hidden in SharePoint 2010, it require a few steps to re-activate the template.

Check this link for more details instructions.
http://uafzal.wordpress.com/2010/11/18/activate-site-directory-site-template-in-sharepoint-2010/

Hope this help.

Keeping SharePoint VM lean - Simple Recovery Model for Sharepoint


Just received a forwarded blog from my lead (Winson Woo), about the keeping the SharePoint Development VM small…
Since you typically don't care about potential data loss in SharePoint development VMs -- and consequently never bother to configure scheduled database backups -- you might as well always use the Simple recovery model for all of your development databases.” – Jeremy Jameson;


SQL databases are default to Full Recovery Model, which will take up lot of disk space, for SharePoint Development VM, we mostly concern about the VM size and will try to minimize the amount of disk space consumed, so the following script will be handy.


In SQL Management Studio, just execute the following script.
Note: This script will set every database recovery model to Simple, except the 'master', 'msdb' and 'tempdb'.

IF OBJECT_ID('tempdb..#CommandQueue') IS NOT NULL DROP TABLE #CommandQueue

CREATE TABLE #CommandQueue
(
ID INT IDENTITY ( 1, 1 ) , SqlStatement VARCHAR(1000)
)

INSERT INTO #CommandQueue
(
SqlStatement
)
SELECT
'ALTER DATABASE [' + name + '] SET RECOVERY SIMPLE'
FROM
sys.databases
WHERE
name NOT IN ( 'master', 'msdb', 'tempdb' )

DECLARE @id INT

SELECT @id = MIN(ID)
FROM #CommandQueue

WHILE @id IS NOT NULL
BEGIN
DECLARE @sqlStatement VARCHAR(1000)

SELECT
@sqlStatement = SqlStatement
FROM
#CommandQueue
WHERE
ID = @id

PRINT 'Executing ''' + @sqlStatement + '''...'

EXEC (@sqlStatement)

DELETE FROM #CommandQueue
WHERE ID = @id

SELECT @id = MIN(ID)
FROM #CommandQueue
END



Alternative:
You can set the Recovery Model in the each database, just follow the step below.
  • Right Click on the database name
  • Click on Properties
  • Click on Options
  • In the right panel, change the “Recovery Model” to Simple


References of this article were from:

Friday, March 18, 2011

Additions to this Web site have been blocked - Error Message in SharePoint 2010

After Restored a site, when I tried to edit anything on the site, I received the following error message.

"Additions to this Web site have been blocked."

In both browser and Powershell console.



This is a common issue after every restore...

Possible cause
1) Site Collection Quotas is reached.
2) Site Collection is set to Read-only (blocks additions, updates, and deletions)\

Resolution:

  • Go to Central Administration.
  • Click Application Management
  • Click Configure Quotas and Locks
  • Set the Site Lock Information to "Not Lock"



  • Hope this help... Good luck!

    Wednesday, March 9, 2011

    Changes and New in SharePoint 2010

    I was trying to find a list of all the improvement in SharePoint 2010, then I stumbled into this site.

    Overview of SharePoint 2010 Changes and New Functionality - Thanks Michelle Pakron for compiled a list of the all the changes.

    Wednesday, February 2, 2011

    Sharepoint 2010 Application Pool Stops After First Hit

    Received a Error 503 after first/initial hit on the site.

    Service Unavailable


    HTTP Error 503. The service is unavailable.


    Solutions:
    1) In the Control Panel, open Administrative Tools, then Local Security Policy.
    2) Beneath Security Settings, open Local Policies and highlight User Rights Assignment.
    3) Locate Log on as a batch job. Open the properties and add any users that need this right.


    Reference of the article from:

    SharePoint 2010 Farm Backup and Cleanup Script

    I have spent some time playing with the Powershell Script. I used to have 3 different scripts, but then I decided to combine all of them to one. Which will backup the farm, site collection, then clean and notify administrator about the status.

    Here is the script that I want to share.
    1) Backup the Site Collection
    2) Backup the SharePoint Farm
    3) Clean up the old Farm backup and Site Collection backup files
    4) Log deleted file into cleanup.log
    5) Notify Administrator about the Status.

    * Updated on 04 Jan 2012 - improve error handling *
    ##==================================================================##
    ## PowerShell SharePoint 2010 Farm Backup, Cleanup and Email Script ##
    ## Version: 2.0 ##
    ## Last updated: 04 Jan, 2012 ##
    ## Description: This script will perform the following ##
    ## 1) Backup Site Collection ##
    ## 2) Backup SharePoint Farm ##
    ## 3) Cleanup old backup and old farm backup ##
    ## 4) Log deleted file in cleanup.log ##
    ## 5) Send out status email ##
    ##==================================================================##

    $snapin = Get-PSSnapIn -name "Microsoft.SharePoint.PowerShell" -registered
    if(!$snapin)
    {
    Add-PSSnapIn Microsoft.SharePoint.PowerShell
    }

    $today = (Get-Date -Format yyyy-MM-dd)
    $collectionBackupPath ="\\SP2010\E$\Scheduled_Backups\Site-80\"
    $farmBackupPath = "\\SP2010\E$\Scheduled_Backups\Farm\"
    $webSite = "http://SP2010"

    ##Cleanup Backup that is older then x day.
    $days = 7

    ##Variables for Email
    $emailFrom = "SP2010@SharePoint4Newbie.com"
    $emailTo = "administrator@SharePoint4Newbie.com"
    $smtpServer = "MailServer.SharePoint4Newbie.com"


    ## Send Mail Function
    Function sendEmail
    {
    param($from,$to,$subject,$smtphost,$body)
    $smtp= New-Object System.Net.Mail.SmtpClient $smtphost
    $msg = New-Object System.Net.Mail.MailMessage $from, $to, $subject, $body
    #$msg.isBodyhtml = $true
    $smtp.send($msg)
    }

    Try
    {
    $backupFile = $collectionBackupPath + $today + ".Bak"

    ## Backing up SharePoint Collection
    Backup-SPSite -Identity $webSite -Path $backupFile -Force -ea Stop

    ## Backing up SharePoint Farm
    Backup-SPFarm -directory $farmBackupPath -backupmethod Full

    ## Location of spbrtoc.xml
    $spbrtoc = $farmBackupPath + "spbrtoc.xml"

    ## Import the Sharepoint backup report xml file
    [xml]$sp = gc $spbrtoc

    ## Find the old backups in spbrtoc.xml
    $old = $sp.SPBackupRestoreHistory.SPHistoryObject | Where { (Get-Date $_.SPStartTime) -lt ((get-date).adddays(-$days)) }
    if ($old -eq $Null) { write-host "No reports of backups older than " + $days + " days found in spbrtoc.xml.`nspbrtoc.xml isn't changed and no files are removed.`n" ; break}

    if ($old -eq $Null)
    {
    Add-Content $farmBackupPath"cleanup.log" "$date - Farm BK Script - No reports of backups older than $days days found in spbrtoc.xml. spbrtoc.xml isn't changed and no files are removed.`n";
    }
    else
    {
    ## Delete the old backups from the Sharepoint backup report xml file
    $old | % { $sp.SPBackupRestoreHistory.RemoveChild($_) }

    ## Delete the physical folders in which the old backups were located
    $old | % { Remove-Item $_.SPBackupDirectory -Recurse }

    ## Save the new Sharepoint backup report xml file
    $sp.Save($spbrtoc)

    ##Write-host "Backup(s) entries older than " + $portalRecycleDays + " days are removed from spbrtoc.xml and harddisc."
    Add-Content $farmBackupPath"cleanup.log" "$date - Farm BK Script - Backup(s) entries older than $portalRecycleDays days are removed from spbrtoc.xml and harddisc.";
    }

    ## Remove Previous Site Collection Backup
    $LastWrite = [DateTime]::Now.AddDays(-$days)
    $Files = get-childitem $collectionBackupPath"*.bak" | Where {$_.LastWriteTime -le "$LastWrite"}
    if (!$Files)
    {
    Add-Content $collectionBackupPath"cleanup.log" "$date - Farm BK Script - No backup file is older than $LastWrite ";
    }
    else
    {
    foreach ($File in $Files)
    {
    ##Deleting old backup file
    Remove-Item $File -Force
    Add-Content $collectionBackupPath"cleanup.log" "$date - Farm BK Script - Deleted file $File ...";
    }
    }

    ## Composed a status update email for administration
    $emailSubject = "SharePoint Farm Backup is completed"
    $emailBody = "SharePoint Farm Backup is completed successfully on "+ $today + "`r`n`r`nFarm backup can be found at $webSite:9999/_admin/BackupHistory.aspx`r`n`r`nBackup files are located at $collectionBackupPath";
    sendEmail $emailFrom $emailTo $emailSubject $smtpServer $emailBody
    }
    Catch
    {
    ## Composed a status update email for administration
    $ErrorMessage = $_.Exception.Message
    $emailSubject = "SharePoint Farm Backup Job failed on "+$today
    $emailBody = "SharePoint Farm Backup Job failed on "+ $today + "`r`n`r`nThe reason of the failure was: $ErrorMessage."
    sendEmail $emailFrom $emailTo $emailSubject $smtpServer $emailBody
    }
    Finally
    {
    ## Unlock the site
    Set-SPSite -Identity $webSite -LockState "Unlock"
    }

    ##==================================================================##


    You can copy the script and save to the harddrive (example c:\Powershell\SPBackupCleanup.ps1), then create a Windows Tasks Scheduler to execute the script everyday... With that, you should sleep better at night :)
    Please TEST the script multiple times before using in production...
    Tips:
    use Windows Powershell to restore a farm
    1) On the Start menu, click All Programs.
    2) Click Microsoft SharePoint 2010 Products.
    3) Click SharePoint 2010 Management Shell.

    4) Enter the following Script to get the Backup GUID
    Get-SPBackupHistory -Directory \\SP2010\E$\Scheduled_Backups\Farm -ShowBackup

    5) Enter the following to Restore the farm
    Restore-SPFarm -Directory \\SP2010\E$\Scheduled_Backups\Farm -RestoreMethod Overwrite -BackupId [backup GUID]

    Good Luck!
    Reference of my script were from "Automate SharePoint 2010 Farm Backups with Powershell" by Alex