Sunday, October 7, 2012

PowerShell - Solution deployment simplified


As a SharePoint developer or Admin, what we do most frequently in our day-to-day activities; is to play around with SharePoint solutions (WSPs) and Features..
And since we cannot memorize all the available operations, here are simplified PowerShell commands:

SPSolution Operations:

Add Solution to SharePoint Farm- You are ready with solution package (WSP) for deployment and this is a fresh deployment; first time deployment to the farm. SharePoint can consume a solution only if it is uploaded in SP solution store, and the operation to upload a solution is Add-SPSolution

Command:
Add-SPSolution -LiteralPath -Confirm
Parameters:
LiteralPath: Mandatory, Takes full path of WSP as string
Confirm: Optional, Ask for confirmation before operation
Example:
Add-Spsolution -Literalpath "C:\Solutions\MySolution.wsp"

Install Solution - Now solution is available in farm, next step is to use it. Installing a WSP comes with many options based on scope (for specific web application or all) and content (DLL(s) for GAC or files).
Command:
Install-SPSolution -Identity -GACDeployment
Install-SPSolution -Identity -AllWebApplication -GACDeployment
Install-SPSolution -Identity -WebApplication -GACDeployment
Parameters:
Identity: Required, Full name/GUID of WSP as string
AllWebapplications: Optional, If WSP to be used by all webapplications in farm
WebApplication: Optional, If WSP is for specific web application, URL as string
GACDeployment: Optional, If WSP includes some DLL to GAC
Force: this parameter can be used if installation is throwing some error due to feature activation or time job issue.
Confirm: Optional, Ask for confirmation before operation
Example:
Install-SPSolution -Identity "MySolution.wsp" -GACDeployment
Install-SPSolution -Identity "MySolution.wsp" -AllWebApplication -GACDeployment
Install-SPSolution -Identity "MySolution.wsp" -WebApplication "http://mysite.mydomain.com/" -GACDeployment

Upgrade a deployed Solution- Your solution package (WSP) is already deployed and installed; now you did some changes in code and want to deploy the latest, here comes Update-SPSolution. This command will upgrade the deployed solution with latest DLL(s) and files.
Command:
Update-SPSolution -Identity -LiteralPath -GACDeployment
Parameters:
Identity: Required, Full name/GUID of WSP as string
LiteralPath: Mandatory, Takes full path of WSP as string
GACDeployment: Optional, If WSP includes some DLL to GAC
Force: this parameter can be used if installation is throwing some error due to feature activation or time job issue.
Confirm: Optional, Ask for confirmation before operation
Example:
Update-SPSolution -Identity MySolution.wsp -LiteralPath "C:\Solutions\MySolution.wsp" -GACDeployment

Deployment Rollback: There are scenarios where we may need to rollback the deployment; If the deployment caused some major issue in farm/application or you added a big component (i.e.-site/list definition) in you solution and want a fresh deployment. To rollback a deployment, first retract/uninstall the WSP and then remove it from farm.
Retract Solution - Similar to installation, uninstalling a WSP comes with many options based on scope (for specific web application or all).
Command:
Uninstall-SPSolution -Identity -Confirm
Uninstall-SPSolution -Identity -AllWebApplication -Confirm
Uninstall-SPSolution -Identity -WebApplication -Confirm
Parameters:
Identity: Required, Full name/GUID of WSP as string
AllWebapplications: Required, If WSP to be retracted from all webapplications in farm
WebApplication: Required, If WSP is for specific web application, URL as string
Confirm: Optional, Ask for confirmation before operation
Example:
Uninstall-SPSolution -Identity "MySolution.wsp"
Uninstall-SPSolution -Identity "MySolution.wsp" -AllWebApplication -Confirm:$false
Uninstall-SPSolution -Identity "MySolution.wsp" -Webapplication "http://mysite.mydomain.com/" -Confirm:$false

Remove Solution to SharePoint Farm- After retraction, solution can be removed from SharePoint farm solution store.
Command:
Remove-SPSolution -Identity -Confirm
Parameters:
Identity: Required, Full name/GUID of WSP as string
Confirm: Optional, Ask for confirmation before operation
Example:
Remove-SPSolution "MySolution.wsp"
Remove-SPSolution -Identity "Unilever.Global.CDF.wsp" -Confirm:$false

SPFeature Operations:
Install/Uninstall Features in SharePoint Farm- Normally feature installation/un-installation are taken care with SPSolution operations which contains the feature. But we come across situations where features needs to be Installed/Uninstalled separately. Below are the commands and examples.

Install Feature Command:
Install-SPFeature -Path -Force -Confirm
Parameters:
Path: Mandatory, Takes relative path from the folder “$env:ProgramFiles\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\FEATURES\” to the feature as string
Confirm: Optional, Ask for confirmation before operation ($true/$false)
Force: Optional, this parameter can be used for force installation.
Example:
Install-SPFeature -Path "MyFeature"

Un-Install Feature Command:
Uninstall-SPFeature -Path -Force -Confirm
Parameters:
Path: Mandatory, Takes relative path from the folder “$env:ProgramFiles\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\FEATURES\” to the feature as string
Confirm: Optional, Ask for confirmation before operation ($true/$false)
Force: Optional, this parameter can be used for force operation.
Example:
Uninstall-SPFeature -Path "MyFeature"

Activate/Deactivate Feature- Below are the commands and examples.
Activate Feature Command:
Enable-SPFeature –Identity -URL Force -Confirm
Parameters:
Identity: Required, Name or GUID of feature as string
URL: Optional,Site/Web URL depending on feature scope
Confirm: Optional, Ask for confirmation before operation ($true/$false)
Force: Optional, this parameter can be used for force installation.
Example:
Enable-SPFeature –Identity "MyFeature" -URL "http://mysite.mydomain.com"

Deactivate Feature Command:
Disable-SPFeature –Identity -URL Force -Confirm
Parameters:
Identity: Required, Name or GUID of feature as string
URL: Optional,Site/Web URL depending on feature scope
Confirm: Optional, Ask for confirmation before operation ($true/$false)
Force: Optional, this parameter can be used for force operation.
Example:
Disable-SPFeature –Identity "MyFeature" -URL "http://mysite.mydomain.com"

Friday, September 21, 2012

PowerShell : Start up


Windows PowerShell (PS) is a shell scripting, provided by Microsoft. This is mainly used for Windows and Server administration. This is very powerful tool for administrators. Here we’ll cover basics of PowerShell. Refer http://msdn.microsoft.com/en-us/library/windows/desktop/aa973757(v=vs.85).aspx for more details.
In this post I would give a start-up that how PowerShell is used for SharePoint.
Start: Assuming PowerShell is installed on your machine. Open the PS (PowerShell) console. [Start-> All Programs->Accessories -> Windows PowerShell]
Type Dir on blue console and enter-> Enter : It will show your directory information. Similarly we can do all kind of windows activities from this console. PS is packaged with large number of cmdlets, which are light weight commands to perform different activities, check http://msdn.microsoft.com/en-us/library/windows/desktop/ms714395(v=vs.85).aspx and http://technet.microsoft.com/en-us/scriptcenter/dd772285.aspx for details on PS cmdlets.
So now we know what PowerShell is and how to use it. Now, how to use this for SharePoint? SharePoint cmdlets are available in a snap in which should be added to Windows PS first to use it.
Type below command on console and enter [on SharePoint server]:
Add-PSSnapin Microsoft.SharePoint.PowerShell
Now, we are all set..
Enter Get-SPSite on console, hit Enter and you’ll get list of all the site collections available in your SP Farm.
Some important points:
1. SharePoint 2010 Management Shell is a PS console for SP packaged and installed with SP2010 and can be used directly, no additional snap-in required.
2. Login account need appropriate permissions on SP Server and SP Databases to execute cmdlets/scripts.
It’s just a beginning..
Happy scripting :) 

Saturday, May 12, 2012

SharePoint Incoming Email Configuration - PowerShell

PowerShell is a strong windows shell scripting, which is widely used in SharePoint 2010. Here I am sharing small and useful scripts we use for SharePoint Farm administration.

Requirement:
Configure Incoming Email settings in SharePoint farm.


Script:

# *******************************************************************
## Function - Configure Incoming Email settings in SharePoint farm
## Author - Deepak Solanki
## Checks to ensure that Microsoft.SharePoint.Powershell is loaded, if not, adding pssnapin
## Configure Incoming Email settings in SharePoint farm
# ********************************************************************
#Add SharePoint Add-ins
Add-PSSnapin Microsoft.SharePoint.PowerShell -erroraction SilentlyContinue 
# if snapin is not installed then use this method
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")


Write-Host "Script started to Configure Incoming Email." 


  #Variables to get config values    
 [boolean]$Enabled = $true;
 [boolean]$UseAutomaticSettings = $false;
 [boolean]$UseDirectoryManagementService = $true;
[boolean]$RemoteDirectoryManagementService = $false;
$ServerAddress = "EXCHANGE.DOMAIN.COM";
[boolean]$DLsRequireAuthenticatedSenders = $true;
[boolean]$DistributionGroupsEnabled = $true;
$ServerDisplayAddress = "sharepoint.company.com";
$DropFolder = "c:\inetpub\mailroot\drop";

#Test the drop folder location exists before proceeding with any changes
$dropLocationTest = Get-Item $DropFolder -ErrorAction SilentlyContinue
if ($dropLocationTest -eq $null)
{
Throw "The drop folder location $DropFolder does not exist - please create the path and try the script again."
}

    #Configuring Incoming E-mail Settings
    try
    {
$type = "Microsoft SharePoint Foundation Incoming E-Mail"
$svcinstance = Get-SPServiceInstance | where { $_.TypeName -eq $type}
$inmail = $svcinstance.Service


if ($inmail -ne $null)
        {
Write-Log "Configuring Incoming E-mail Settings."            
#Enable sites on this server to receive e-mail
                $inmail.Enabled = $Enabled
            
#Automatic Settings mode
                $inmail.UseAutomaticSettings = $UseAutomaticSettings
            
#Use the SharePoint Directory Management Service to create distribution groups
                $inmail.UseDirectoryManagementService = $UseDirectoryManagementService
            
#Use remote: Directory Management Service
                $inmail.RemoteDirectoryManagementService = $RemoteDirectoryManagementService
            
#SMTP mail server for incoming mail
                $inmail.ServerAddress = $ServerAddress            

#Accept messages from authenticated users only
                $inmail.DLsRequireAuthenticatedSenders = $DLsRequireAuthenticatedSenders
            
#Allow creation of distribution groups from SharePoint sites
                $inmail.DistributionGroupsEnabled = $DistributionGroupsEnabled
            
#E-mail server display address
                $inmail.ServerDisplayAddress = $ServerDisplayAddress
            
#E-mail drop folder
                $inmail.DropFolder = $DropFolder;  

$inmail.Update();
Write-Host "Incoming E-mail Settings completed."
        } 
    }
    #Report if there is a problem setting Incoming Email
    catch { Write-Host "There was a problem setting Incoming Email: $_" }

SharePoint Diagnostic Configuration - PowerShell

PowerShell is a strong windows shell scripting, which is widely used in SharePoint 2010. Here I am sharing small and useful scripts we use for SharePoint Farm administration.

Requirement:
Setting SharePoint Diagnostic and logging configuration.


Script:

# *******************************************************************
## Function - Configure Diagnostic Setting in SharePoint Farm
## Author - Deepak Solanki
## Checks to ensure that Microsoft.SharePoint.Powershell is loaded, if not, adding pssnapin
## Check log file location exists before proceeding with changes
## Reset all log levels to default
## Change Trace Log settings
# ********************************************************************


#Add SharePoint Add-ins
Add-PSSnapin Microsoft.SharePoint.PowerShell -erroraction SilentlyContinue 
# if snapin is not installed then use this method
[Void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")


Write-Log "Script started to Configure SharePoint Diagnostic settings."  
#Reset all log levels to default
Write-Host "Resetting all log levels to default."
Clear-SPLogLevel
Write-Host "Reset Completed."


#Declare variable for config values
$LogLocation = "E:\Logs"
$DaysToKeepLogs ="14"
[boolean]$LogMaxDiskSpaceUsageEnabled = [Boolean]::Parse("true")
$LogDiskSpaceUsageGB = "10"

#Test the log file location exists before proceeding with any changes
$logLocationTest = Get-Item $LogLocation -ErrorAction SilentlyContinue
if ($logLocationTest -eq $null)
{
Throw "The log file location $LogLocation does not exist - please create the path and try the script again."
}
#Trace Log settings
Write-Host  "Starting SharePoint Diagnostic configuration."
try
{
    Set-SPDiagnosticConfig -LogLocation $LogLocation `
                        -DaysToKeepLogs $DaysToKeepLogs `
                        -LogMaxDiskSpaceUsageEnabled:$LogMaxDiskSpaceUsageEnabled `
                        -LogDiskSpaceUsageGB $LogDiskSpaceUsageGB
}
#Report if there is a problem stopping the service instance
catch {  Write-Host  "There was a problem in diagnostic setting: $_" }
Write-Host  "Diagnostic settings changed successfully."


Friday, April 15, 2011

Access Active Directory in .NET

During one of my client requirements, I played a lot with active directory to display user data in a web application from active directory.

Here goes a generic class with some functions to get get user information from active directory:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.DirectoryServices;
using System.Collections;

namespace Common
{
public class ActiveDirectory
{
string domainPath = "GC://YourGC/DC=YourDomain,dc=YourOU,dc=com";

//Get Single user info
public DataTable GetUserInfo(string sLogonName)
{
/*Getting user data from active directory*/
DirectoryEntry entry = new DirectoryEntry(domainPath);
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = "(&(objectClass=user)(sAMAccountName=" + sLogonName + "))";
SearchResult result = searcher.FindOne();

/*Creating a datatable to store user data*/
DataTable resultTable = new DataTable("UserData");
resultTable.Columns.Add("givenname");
resultTable.Columns.Add("sn");
resultTable.Columns.Add("title");
resultTable.Columns.Add("telephoneNumber");
resultTable.Columns.Add("company");
resultTable.Columns.Add("mail");
resultTable.Columns.Add("department");
resultTable.Columns.Add("manager");

DataRow resultRow = resultTable.NewRow();

if (result.Properties.Count > 0)
{
try
{
resultRow["givenname"] = result.Properties["givenname"][0].ToString();
}
catch (Exception)
{
resultRow["givenname"] = string.Empty;
}
try
{
resultRow["sn"] = result.Properties["sn"][0].ToString();
}
catch (Exception)
{
resultRow["sn"] = string.Empty;
}
try
{
resultRow["title"] = result.Properties["title"][0].ToString();
}
catch (Exception)
{
resultRow["title"] = string.Empty;
}
try
{
resultRow["telephoneNumber"] = result.Properties["telephoneNumber"][0].ToString();
}
catch (Exception)
{
resultRow["telephoneNumber"] = string.Empty;
}
try
{
resultRow["company"] = result.Properties["company"][0].ToString();
}
catch (Exception)
{
resultRow["company"] = string.Empty;
}
try
{
resultRow["mail"] = result.Properties["mail"][0].ToString();
}
catch (Exception)
{
resultRow["mail"] = string.Empty;
}
try
{
resultRow["department"] = result.Properties["department"][0].ToString();
}
catch (Exception)
{
resultRow["department"] = string.Empty;
}
try
{
resultRow["manager"] = result.Properties["manager"][0].ToString();
}
catch (Exception)
{
resultRow["manager"] = string.Empty;
}
resultTable.Rows.Add(resultRow);
}
else
{

}
return resultTable;
}

//Get all domain users multiple properties in data table
public DataTable GetAllUserInfo()
{
/*Getting user data from active directory*/
DirectoryEntry entry = new DirectoryEntry(domainPath);
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.PageSize = 1000;

searcher.Filter = "(&(objectClass=user)(objectCategory=person))"; //to get users only
SearchResultCollection resultCollection = searcher.FindAll();

/*Creating a datatable to store user data*/
DataTable resultTable = new DataTable("UserData");
resultTable.Columns.Add("givenname");
resultTable.Columns.Add("sn");
resultTable.Columns.Add("title");
resultTable.Columns.Add("telephoneNumber");
resultTable.Columns.Add("company");
resultTable.Columns.Add("mail");
resultTable.Columns.Add("department");

SearchResult result;
if (resultCollection != null)
{
for (int counter = 0; counter < resultCollection.Count; counter++)
{
DataRow resultRow = resultTable.NewRow();

result = resultCollection[counter];
if (result.Properties.Contains("givenname"))
{
resultRow["givenname"] = result.Properties["givenname"][0].ToString();
}
if (result.Properties.Contains("sn"))
{
resultRow["sn"] = result.Properties["sn"][0].ToString();
}
if (result.Properties.Contains("title"))
{
resultRow["title"] = result.Properties["title"][0].ToString();
}
if (result.Properties.Contains("telephoneNumber"))
{
resultRow["telephoneNumber"] = result.Properties["telephoneNumber"][0].ToString();
}
if (result.Properties.Contains("company"))
{
resultRow["company"] = result.Properties["company"][0].ToString();
}
if (result.Properties.Contains("mail"))
{
resultRow["mail"] = result.Properties["mail"][0].ToString();
}
if (result.Properties.Contains("department"))
{
resultRow["department"] = result.Properties["department"][0].ToString();
}
resultTable.Rows.Add(resultRow);
}
}
return resultTable;
}


//Get all domain users single property in array list
public ArrayList GetAllADDomainUsers()
{
ArrayList allUsers = new ArrayList();

DirectoryEntry searchRoot = new DirectoryEntry(domainPath);
DirectorySearcher search = new DirectorySearcher(searchRoot);
search.Filter = "(&(objectClass=user)(objectCategory=person))";

SearchResult result;
SearchResultCollection resultCol = search.FindAll();
if (resultCol != null)
{
for (int counter = 0; counter < resultCol.Count; counter++)
{
result = resultCol[counter];
if (result.Properties.Contains("samaccountname"))
{
allUsers.Add((String)result.Properties["samaccountname"][0]);
}
}
}
return allUsers;
}
}
}



Happy Coding!!!

Wednesday, April 13, 2011

Error & information logging; event viewer or text file

We can log different errors or information while writing code for any .NET application, here is a generic class written for logging:

Code:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Security.Permissions;

namespace Common
{
/*This Class is used for logging messages to either a custom EventViewer or in a plain text file located on web server.
* Owner : Deepak Solanki
* Year : 2011 */

public class Logging
{
#region "Variables"

private string sLogFormat;
private string sErrorTime;

#endregion


#region "Local methods"

/* Write to Txt Log File*/
public void WriteToLogFile(string sErrMsg)
{
try
{
//sLogFormat used to create log format :
// dd/mm/yyyy hh:mm:ss AM/PM ==> Log Message
sLogFormat = DateTime.Now.ToShortDateString().ToString() + " " + DateTime.Now.ToLongTimeString().ToString() + " ==> ";

//this variable used to create log filename format "
//for example filename : ErrorLogYYYYMMDD
string sYear = DateTime.Now.Year.ToString();
string sMonth = DateTime.Now.Month.ToString();
string sDay = DateTime.Now.Day.ToString();
sErrorTime = sYear + sMonth + sDay;

//writing to log file
string sPathName = "C:\\Logs\\ErrorLog" + sErrorTime;
StreamWriter sw = new StreamWriter(sPathName + ".txt", true);
sw.WriteLine(sLogFormat + sErrMsg);
sw.Flush();
sw.Close();
}
catch (Exception ex)
{
WriteToEventLog("MySite", "Logging.WriteToLogFile", "Error: " + ex.ToString(), EventLogEntryType.Error);
}
}

/* Write to Event Log*/
public void WriteToEventLog(string sLog, string sSource, string message, EventLogEntryType level)
{
//RegistryPermission regPermission = new RegistryPermission(PermissionState.Unrestricted);
//regPermission.Assert();

if (!EventLog.SourceExists(sSource))
EventLog.CreateEventSource(sSource, sLog);

EventLog.WriteEntry(sSource, message, level);
}

#endregion
}
}


Happy Coding!!!

Web part to show data from list

Requirement is to show news on web page, data saved in custom list . A webpart to get data from list and show on page:

1. Open Visual Studio and create a class file or webpart (if sharepoint extension is there with VS.
2. Code of ShowNews.cs file below:


using System;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;
using System.Drawing;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;

namespace MyWebParts
{
[Guid("6e1a7cf2-bf98-4926-81c9-0c0184ba6a60")]
public class ShowNews : System.Web.UI.WebControls.WebParts.WebPart
{

//Controls
private Label lblTitle;
private Label lblNews;

// A table that is used to layout the controls
private Table tbMain;
private TableRow tRow;
private TableCell tCell;

#region "Web Part Properties"

private const string defaultSiteCollection = "NewTeamSite";
private string _siteName = defaultSiteCollection;
[Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
WebDisplayName("Site Name"),
WebDescription("Site from where to fetch News")]

public string SiteName
{
get { return this._siteName; }
set { this._siteName = value; }
}

private const string defaultList = "News";
private string _listName = defaultList;
[Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
WebDisplayName("List Name"),
WebDescription("List from where to fetch News")]

public string ListName
{
get { return this._listName; }
set { this._listName = value; }
}
#endregion

public ShowNews()
{
}

protected override void CreateChildControls()
{
base.CreateChildControls();

// TODO: add custom rendering code here.
tbMain = new Table();
tbMain.BorderStyle = BorderStyle.Double;
tbMain.BorderColor = Color.Silver;
tbMain.BackColor = Color.WhiteSmoke;


lblTitle = new Label();
lblTitle.Text = "News";
lblTitle.Font.Bold = true;

tRow = new TableRow();
tCell = new TableCell();
tCell.VerticalAlign = VerticalAlign.Top;
tCell.HorizontalAlign = HorizontalAlign.Center;
tCell.Controls.Add(lblTitle);
tRow.Controls.Add(tCell);
tbMain.Controls.Add(tRow);


lblNews = new Label();
lblNews.Text = "Blank";

tRow = new TableRow();
tCell = new TableCell();
tCell.VerticalAlign = VerticalAlign.Top;
tCell.HorizontalAlign = HorizontalAlign.Left;
tCell.Controls.Add(lblNews);
tRow.Controls.Add(tCell);
tbMain.Controls.Add(tRow);

this.Controls.Add(tbMain);

CreateNewsHtml();
}

private void CreateNewsHtml()
{
SPList newsList;
SPListItemCollection listitemcoll;

try
{
using (SPSite site = new SPSite(this.Context.Request.Url.ToString()))
{
using (SPWeb web = site.AllWebs[this._siteName])
{
newsList =web.Lists[this._listName];
listitemcoll = newsList.Items;
if (newsList.ItemCount > 0)
foreach(SPListItem item in listitemcoll)
{
lblTitle.Text = item["Title"].ToString();
lblNews.Text = item["News"].ToString();
}

}
}
}
catch (Exception ex)
{

}
}
}
}


3. Create ShowNews.webpart file (get created itself with Sharepoint extension)

<?xml version="1.0" encoding="utf-8"?>
<webParts>
<webPart xmlns="http://schemas.microsoft.com/WebPart/v3">
<metaData>
<type name="MyWebparts.ShowNews, ShowNews, Version=1.0.0.0, Culture=neutral, PublicKeyToken=XXXXXXXXXXXXXXXXX" />
<importErrorMessage>Cannot import ShowNews Web Part.</importErrorMessage>
</metaData>
<data>
<properties>
<property name="Title" type="string">ShowNews Web Part</property>
<property name="Description" type="string">Show News from List on Page</property>
<property name="AllowZoneChange" type="bool">True</property>
<property name="AllowHide" type="bool">True</property>
</properties>
</data>
</webPart>
</webParts>


4. Now, your webpart is ready, deploy it either [1] manually; GAC the DLL, upload webpart file to webpart gallery and made safecontrol entry for webpart in web.config file. or [2] create a deployment package to deploy the webpart.

Happy Coding!!!