Friday, April 29, 2016

SharePoint 2013 Dev Environment Installation and Configuration

Here's a refresher on how to install and configure SharePoint 2013 dev environment.



Domain = contoso.edu
  • Domain controller  = DC01 (DNS server,  Email Server, WorkflowMgr server, too.)
  • SharePoint Server = SP13 (Database Server, too)
  • Database Server instance = SP13\SQL12 on SP13 server.
  • Service Accounts:
    • contoso\SPFarm
      • local admin to sharepoint 2013 server.
      • db_creator and security_admin roles on SQL Server instance
    • contoso\SPAppPool
      • account used for Web Application's application pool.

Prerequisite Installation

  1. Log on to SP13 as local admin
  2. Run powershell command as follows (3 lines)
  3. Import-Module ServerManager
    
    Add-WindowsFeature NET-WCF-HTTP-Activation45, NET-WCF-TCP-Activation45, NET-WCF-Pipe-Activation45
    
    Add-WindowsFeature Net-Framework-Features,Web-Server,Web-WebServer,Web-Common-Http,Web-Static-Content,Web-Default-Doc,Web-Dir-Browsing,Web-Http-Errors,Web-App-Dev,Web-Asp-Net,Web-Net-Ext,Web-ISAPI-Ext,WEB-ISAPI-Filter,Web-Health,Web-Http-Logging,Web-Log-Libraries,Web-Request-Monitor,Web-Http-Tracing,Web-Security,Web-Basic-Auth,Web-Windows-Auth,Web-Filtering,Web-Digest-Auth,Web-Performance,Web-Stat-Compression,Web-Dyn-Compression,Web-Mgmt-Tools,Web-Mgmt-Console,Web-Mgmt-Compat,Web-Metabase,Application-Server,AS-Web-Support,AS-TCP-Port-Sharing,AS-WAS-Support,AS-HTTP-Activation,AS-TCP-Activation,AS-Named-Pipes,AS-Net-Framework,WAS,WAS-Process-Model,WAS-NET-Environment,WAS-Config-APIs,Web-Lgcy-Scripting,Windows-Identity-Foundation,Server-Media-Foundation,Xps-Viewer
    
  4. Reboot SP13.
  5. Logon to SP13 as local admin
  6. Install the following prerequisites
    1. dotnetfx45_full_x86_x64.exe 
    2. MicrosoftIdentityExtensions-64.msi
    3. setup_msipc_x64.msi (Active Directory Rights Management Services Client 2.0)
    4. sqlncli.msi (SQL Server 2008 R2 Native Client Setup)
    5. Synchronization.msi (Microsoft Sync Framework Runtime v1.0 SP1 (x64)
    6. WcfDataServices.exe (WCF Data Services 5.0 (OData v3)
    7. WcfDataServices56.exe (WCF Data Services 5.6 Tools)
    8. Windows6.1-KB974405-x64.msu --> if error 0x80096002 occurs, do not install it.
    9. Windows6.1-KB2506143-x64 --> if "not-applicable-to-your-computer" error occurs, do not install it.
    10. Use command prompt as local admin and run the following (single-line):
      C:\>WindowsServerAppFabricSetup_x64.exe  /i  CacheClient,CachingService,CacheAdmin  /gac
      
    11. Install "AppFabric1.1-RTM-KB2671763-x64-ENU.exe" (Windows Server AppFabric v1.1 CU1 [KB 2671763]). If error occurs, reboot and try again.
  7. Reboot SP13.

SharePoint 2013 Installation
  1. Log on to SP13 as local admin.
  2. Mount SharePoint 2013 server iso file.
  3. Run setup.exe.
  4. Enter product key.
  5. On Server Type, select "Complete".
  6. Finish installation.
SharePoint 2013 Configuration
  1. Run SharePoint Products Configuration Wizard.
  2. Select "Create a new server farm".
  3. Specify configuration database settings
    1. Database Server = "SP13\SQL12"
    2. Database Name = "SharePoint_Config
    3. Username = contoso\spfarm
  4. Specify port number: 13311
  5. Select NTLM
  6. Do not use configuration wizard when "Welcome" page opens. Click Cancel.
  7. Change auto-generated Central Admin Content database name if needed as follows..
    1. Run the following powershell script in SharePoint Management Shell.
      $db = Get-SPContentDatabase -WebApplication "http://sp13:13311"
      Dismount-SPContentDatabase $db
      
    2. Rename the database in SQL Management Studio from "SharePoint_AdminContent_d95a4420-b726-4a73-a433-df83a2058890" to "SP13_AdminContent"
    3. User powershell to re-mount the database:
      Mount-SPContentDatabase "SP13_AdminContent" -DatabaseServer "SP13\SQL12" 
      -WebApplication "http://sp13:13311"

  8. Provision Usage and Health Data Collection Service Application
  9. $UsageService = Get-SPUsageService
    New-SPUsageApplication -Name "Usage Service Application" -DatabaseServer "SP13\SQL12" 
     -DatabaseName "SP13_UsageService" -UsageService $UsageService
    
  10. Provision State Service Application
  11. New-SPStateServiceDatabase -Name "SP13_StateService" |New-
    SPStateServiceApplication -Name "State Service" | New-
    SPStateServiceApplicationProxy -DefaultProxyGroup
    
  12. Provision Subscription Settings Service Application (required for SharePoint App)
  13. $Account = New-SPManagedAccount
    ##Enter "contoso\SPAppPool" with password on prompt.##
    
    $AppPool = New-SPServiceApplicationPool -Name
     "SP_ServiceApplicationDefaultAppPool" -Account $Account
    
    $App = New-SPSubscriptionSettingsServiceApplication 
     -ApplicationPool $AppPool -Name "Subscription Settings Service" 
     -DatabaseName "SP13_SubscriptionSettingsService"
    
    New-SPSubscriptionSettingsServiceApplicationProxy -ServiceApplication $App
    
  14. Provision App Management Service Application (required for SharePoint App)
  15. $Account = Get-SPManagedAccount "Contoso\SPAppPool"
    ## If not found, try creating a new one via "New-SPManagedAccount" ##
    
    $AppPool = New-SPServiceApplicationPool -Name 
      "AppMgmtServiceAppPool" -Account $Account
    
    $App = New-SPAppManagementServiceApplication 
      -ApplicationPool $AppPool -Name "App Management Service Application"
      -DatabaseName "SP13_AppMgmtService"
    
    New-SPAppManagementServiceApplicationProxy -ServiceApplication $App
    
  16. (Optional) Provision Search Service Application
    Important: Not recommended for dev environment. Search Service Application will make the single-server dev environment very very slow.  It is not recommended to configure and run Search Service Application for dev environment.
    $AppPool = Get-SPServiceApplicationPool 
     -Identity "SP_ServiceApplicationsDefaultAppPool"
    
    $ServerName = (Get-ChildItem env:computername).Value
    $ServiceAppName = "Search Service"
    $DatabaseName = "SP13_SearchService"
    Start-SPEnterpriseSearchServiceInstance $ServerName
    Start-SPEnterpriseSearchQueryAndSiteSettingsServiceInstance $ServerName
    
    $App = New-SPEnterpriseSearchServiceApplication -Name $ServiceAppName 
     -ApplicationPool $AppPool -DatabaseName $DatabaeName
    New-SPEnterpriseSearchServiceApplicationProxy -Name "$ServiceAppName Proxy" 
     -SearchApplication $App
    
    $clone = $App.ActiveTopology.Clone()
    $Instance = Get-SPEnterpriseSearchServiceInstance
    
    New-SPEnterpriseSearchAdminComponent -SearchTopology $clone 
     -SearchServiceInstance $Instance
    
    New-SPEnterpriseSearchContentProcessingComponent -SearchTopology $clone 
     -SearchServiceInstance $Instance
    
    New-SPEnterpriseSearchAnalyticsProcessingComponent -SearchTopology $clone
     -SearchServiceInstance $Instance
    
    New-SPEnterpriseSearchCrawlComponent -SearchTopology $clone 
     -SearchServiceInstance $Instance
    
    New-SPEnerpriseSearchIndexComponent -SearchTopology $clone 
     -SearchServiceInstance $Instance
    
    New-SPEnterpriseSearchQueryProcessingComponent -SearchTopology $clone
     -SearchServiceInstance $Instance
    
    $clone.Activate()
    
    
  17. Configure the Outgoing e-mail server information
    1. In Central Admin site, go to System Settings
    2. Click Configure outgoing e-mail settings
    3. On the Outgoing E-Mail Settings page
      1. Outbound SMTP server = dc01.contoso.edu
      2. From address = "sharepoint@contoso.edu"
  18. Create a Web Application
    1. Central Admin --> Application Management --> Web Application --> Manage Web Applications
    2. Click New
    3. Under Create New Web Application
      1. Select "Create a new IIS web site" 
        1. Name = SharePoint - 80
        2. Port = 80
        3. Path = (default) C:\inetpub\wwwroot\wss\VirtualDirectories\80
      2. Allow Anonymous = No
      3. Use Secure Sockets Layer (SSL) = No
      4. Enable Windows Authentication = checked
        1. Integrated Windows Authentication (checked) = NTLM
      5. Default Sign In Page (selected)
      6. Url = http://sp13:80/
      7. Zone = Default
      8. Create a new application pool
        1. application pool name = SharePoint - 80
        2. security account for this application pool
          1. Configurable
            1. contoso\SPAppPool
      9. Database Server = SP13\SQL12
      10. Database Name = SP13_Content
      11. Database authentication = Windows authentication (recommended)
      12. Service Application Connections = default
      13. Click [OK].
  19. Create Managed Metadata Service Application
    1. Central Admin --> Manage Service Application 
    2. Click [New]
    3. Click "Managed Metadata Service Application"
      1. Name = "Managed Metadata Service"
      2. Database Server = "SP13\SQL12"
      3. Database Name = "SP13_ManagedMetadataService"
      4. Windows Authentication (checked)
      5. New application pool name = "ManagedMetadataServiceAppPool"
      6. Security Account = configurable (checked): contoso\SPAppPool.
    4. Click [OK].
  20. Create User Profile Service Application
    1. Managed Metadata Service Application instance above (#15) is prerequisite to the User Profile Service Application. Make sure Managed Metadata Service Application has been created.
    2. Reboot SP13 (recommended) or restart SharePoint Timer Services.
    3. Log on as Contoso\SPFarm. Important!
    4. Right-click on IE and run IE as local admin. 
    5. Go to http://sp13:13311.
    6. Go to Manage Services on Server.
    7. Click "Start" link on User Profile Service. This should start really quick.
    8. Go to Manage Service Applications.
    9. Click [New] from the ribbon menu. 
    10. Select "New User Profile Service Application".
      1. Name = "User Profile Service Application"
      2. New app pool name = "UserProfileServiceAppPool"
      3. security account for app pool = configurable (Contoso\SPFarm)
      4. Profile Database:
        1. Database Server = SP13\SQL12
        2. Database Name = SP13_UserProfileService_Profile
        3. Database authentication = Windows auth
      5. Synchronization Database:
        1. Database Server = SP13\SQL12
        2. Database Name = SP13_UserProfileService_Sync
        3. Database authentication = Windows auth
      6. Social Tagging Database:
        1. Database Server = SP13\SQL12
        2. Database Name = SP13_UserProfileService_Social
        3. Database authentication = Windows auth
      7. Profile Synchronization Instance = SP13
      8. My Site Host URL = (skip this for now)
      9. My Site Managed Path = (skip this for now)
      10. Site Naming Format = User name (resolve conflicts by using domain_username)
      11. Default Proxy Group = Yes
      12. Click [Create].
    11. After successful creation of User Profile Service Application, reboot SP13 (recommended) or at least restart SharePoint Timer Service.
    12. Log on to DC01 server as domain admin.
    13. Assign "Replicating Directory Change" permission to contoso\spfarm account.
      1. Run "Active Directory Users and Computers" on DC01 as domain admin (Contoso\Administrator).
      2. Right-click the domain (contoso.edu) and select Delegate Control.
      3. Click [Next]. Click [Add]. 
      4. Enter SPFarm. Click [Check Names]. Click [OK].
      5. Click [Next]. 
      6. On the "Tasks to Delegate" page, select "Create a custom task to delegate" radio button. Click [Next].
      7. On the "Active Directory Object Type" page, 
        1. Select "This folder, existing objects in this folder, and creation of new objects in this folder" radio button. Click [Next].
      8. On the "Permissions" page, scroll down and select "Replicating Directory Changes". Click [Next]. Click [Finish].
    14. Log on to SP13 server as Contoso\SPFarm.
    15. Run IE as local admin. Go to "http://sp13:13311".
    16. Go to Manage Services on Server.
    17. Locate User Profile Synchronization Service. Click Start link.
    18. Enter SPFarm's password. Click OK.
    19. User Profile Synchronization Service is a pre-requisite for User Profile Service Application. Without it, User Profile Service Application cannot have any connections to the AD at all. 
    20. If User Profile Synchronization Service is stuck in Starting, 
      1. Restart SharePoint Timer Service and check again. Repeat a few times.
      2. We have to make sure both "User Profile Service" and "User Profile Synchronization Service" are in Started status. If "User Profile Synchronization Service" is stopped, start it. Enter credential of "contoso\spfarm" account on the next page. It may take several minutes to start the User Profile Sync Service.
        If it is stuck on Starting, restart the SharePoint Timer Service or Reboot (recommended). If still stuck on "Starting" status,  use the following PowerShell to stop the service and repeat this step to start it. Often enough, it takes more than a couple of trials of start - stop - start - stop until it finally starts. (source)
        #First, get GUID of the User Profile Synchronization Service 
        Get-SPServiceInstance | Where { $_.TypeName -like "User Profile Synchronization*"} | Select TypeName, Id
        #Then copy the Id value and paste it on the next command.
        Stop-SPServiceInstance [id-value]
      3. If all repeated efforts fail, we need to delete User Profile Service Application and start over its configurations.
    21. If User Profile Synchronization Service successfully started,  go to "Manage Profile Service" page by clicking "User Profile Service Application" in Service Applications Page and start setting up Profile Sync Settings.
    22. Configure SharePoint Profile Sync
      1. Go to "Manage Service Application"
      2. Click "User Profile Service Application"
      3. On "Manage Profile Service" page, click "Configure Synchronization Connections" and "Create New Connection".
        1. Connection Name = Contoso_AD
        2. Type = Active Directory
        3. Forest Name = contoso.edu
        4. Connection Settings
          1. Auto discover domain controller (checked)
          2. Authentication Provider Type = Windows Authentication
          3. Account name = contoso\spfarm
          4. port = 389
        5. Containers: Users directly below CONTOSO
      4. Click [OK].
      5. Go to "Manage Profile Service" page.
        1. Go to "Manage User Properties".
        2. Edit mapped attributes of the following:
          1. Work Email
            1. Direction = Import
            2. Attribute = mail
          2. Picture (optional)
            1. Direction = Export
            2. Attribute = thumbnailPhoto
            3. Description: Upload a photo to help others easily recognize you at meetings and events. Your picture will show up for contacts in Outlook and Lync as well as in different parts of SharePoint, but it may take a day or so to sync your changes with all of these systems.
          3. SIP Address (optional)
            1. Direction = Import
            2. Attribute = msRTCSIP-PrimaryUserAddress
        3. Find "Work Email". Click and Select [Edit].
        4. Go to "Add New Mapping" section 
          1. Manually enter Attribute = mail  and click [Add].
          2. Click [OK]. This will map mail field in AD to Work Email in User Profile Synchronization Properties.
      6. Go to Manage Profile Service page. 
        1. Click "Start Profile Synchronization"
        2. Choose "Start Full Synchronization". Click [OK].
    23. Go to "Services on Server". 
    24. If "User Profile Synchronization Service" is not in "Started" status, Click [Start] link on "User Profile Synchronization Service".
    25. Enter password for selected account name (contoso\SPFarm).
    26. Wait for ~5 min. Monitor activities on Task manager until activities slows down. 
    27. Check if status of "User Profile Synchronization Service" is in "Started".
  21. Create a new web application
    1. Create a new IIS web site: Name = SharePoint - 80
    2. Port = 80
    3. Take all default options. Click OK.
  22. Create a new Site Collection using URL http://sp13. 
  23. Install and hMailServer on DC01 server and add new domain for contoso.edu. Set up email accounts for spfarm and other SharePoint users in the active directory.
  24. Install and set up email client like Mozilla Thunderbird to test email accounts.
  25. Install Visual Studio as needed on SharePoint server.

Wednesday, March 23, 2016

How to Update DataTable and Commit it to the Database

In SSIS Script Task, it seems helpful to use a select statement to consturct a DataTable, update the DataTable as necessary, and commit the DataTable back to database's table. Below is a working example of this case.

protected int updateDeathLog()
{
    int retVal = 0;
    string connString = Dts.Variables["ConnString1"].Value.ToString();
    string sql = "SELECT ObjectId, NewFileName, PageNum, TargetFileExists, TargetFileExistsDT " +
            "FROM MyTable WHERE SourceFileExists = 1 AND TargetFileExists = 0";

    using (SqlConnection conn = new SqlConnection(connString))
    {
        try
        {
            conn.Open();
            SqlCommand cmd = new SqlCommand(sql, conn);
            SqlDataAdapter adapter = new SqlDataAdapter(cmd);
            string newFileFullPath = String.Empty;

            DataTable dt = new DataTable();
            adapter.Fill(dt);
            foreach(DataRow row in dt.Rows)
            {
                // If files exists in target folder, update flags and 
                // datetime field in the DataTable.
                newFileFullPath = Dts.Variables["TargetFolderDeath"].Value.ToString()
                  + "\\" + row["NewFileName"].ToString();

                if(File.Exists(newFileFullPath))
                {
                    row["TargetFileExists"] = true;
                    row["TargetFileExistsDT"] = DateTime.Now;
                }
                else
                {
                    row["TargetFileExists"] = false;
                    row["TargetFileExistsDT"] = System.DBNull.Value;
                }
            }

            // Commit update DataTable to the MyTable.
            // Prior to the commit, set up primary keys in the DataTable 
            // and create a CommandBuilder.
            DataColumn[] keyColumns = new DataColumn[2];
            keyColumns[0] = dt.Columns["ObjectId"];
            keyColumns[1] = dt.Columns["PageNum"];
            dt.PrimaryKey = keyColumns;

            SqlCommandBuilder cmdBuilder = new SqlCommandBuilder(adapter);
            adapter.Update(dt);

            retVal = dt.Rows.Count;
        }
        catch (Exception ex)
        {
            retVal = -1;
            errorMsg = ex.Message;
            stackTrace = ex.StackTrace;
        }
    }

return retVal;
}

Tuesday, March 15, 2016

Change [all files and folders] of a [directory] to 644 and 755

I came across a situation to run chmod 755 [folder] and chmod 644 [file] on my php dev folder. The following would handle all files and folders of current working folder.

sudo find . -type f -exec chmod 644 {} \;
sudo find . -type d -exec chmod 755 {} \;

Thursday, March 10, 2016

Use Bootstrap Popover to load external content through Ajax

It seemed easy to load an external contents through Ajax onto Bootstrap Popover at first. In fact, it wasn't as straightforward as I would like.
Using the content() function, it is straightforward to load any static content.
<a class="pop">Link</a>
<div id="contentDiv" style="display:none;">Some content...</div>

<script type="text/javascript">
$('.pop').popover({
    html: true,
    content: function(){
        return $('#contentDiv').html();
    }
});
</script>
However, loading an external content dynamically through Ajax plus "Loading ..." message took some thoughts.
<a class="pop" href="Product/100">Link</a>

<script type="text/javascript">
$('.pop').popover({
    html: true,
    content: function(){
        // Create a random temporary id for the content's parent div
        // with a unique number just in case.
        var content_id = "content-id-" + $.now();

        $.ajax({
            type: 'GET',
            url: $(this).prop('href'),
            cache: false,
        }).done(function(d){
            $('#' + content_id).html(d);
        });

        return '<div id="' + content_id + '">Loading...</div>';

        // Initially, the content() function returns a parent div, 
        // which shows "Loading..." message.
        // As soon as the ajax call is complete, the parent div inside 
        // the popover gets the ajax call's result.

    }
});
</script>

Keeping the Bootstrap Popover alive while mouse moves from triggering element to the popover

I tried to use the Bootstrap Popover as a quick dynamic ajax form within which I can perform further actions. By default, when popover appears as a result of hovering over the triggering element, it disappears when the triggering element is no longer hovered.

One easy way to keep the popover stay put until I am no longer hovering over the triggering element or the popover itself is to use { trigger: "manual", html: true, animation: false } and handle the trigger via "mouseenter" and "mouseleave" event.

// the triggering element has class of 'pop'
$(".pop").popover({
    trigger: "manual", 
    html: true,
    animation: false
}).on("mouseenter", function(){
    var _this = this;
    $(this).popover("show");
    $(".popover").on("mouseleave", function(){
      $(_this).popover('hide');
    });
}).on("mouseleave", function(){
    var _this = this;
    setTimeout(function(){
      if (!$(".popover:hover").length){
        $(_this).popover("hide");
      }
    }, 50); 
    // If popover is not hovered within 50 milliseconds 
    // after the mouse leaves the triggering element, 
    // the popover will be hidden. 
    // 50 milliseconds seem fine in most cases.
});

Thursday, March 3, 2016

@Html.CheckBoxFor( ) helper method problem when posting to Controller method with Bind(Include=...) annotation

@Html.CheckBoxFor(model => model.IsDeposit) will generate html
<input type="checkbox" name="IsDeposit" value="true" />
<input type="hidden"   name="IsDeposit" value="false" />
given that IsDeposit is a boolean value. Then the form is submitted to a method in controller such as
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include ="PaymentID,IsDeposit")] Payment payment)
{ . . . }        
The problem is that the checkbox value will always be false whether the checkbox is checked or not.

The cause of this problem seems to be the IsDeposit in the Bind(Include=...) annotation is not handled properly yet in ASP.NET MVC. @Html.CheckBoxFor(..) creates two elements with the same name attribute (checkbox and hidden). Possibly only the hidden element is accepted to the method when Bind(Include=..) is used. I tried including "IsDeposit" twice (for example, Bind[Include=IsDeposit,IsDeposit,,]) just in case, but it did not help.

The best bet is to avoid using the Bind(Include=...) annotation when @Html.CheckBoxFor() helper method is used. Alternatively, you can also create the checkbox element manually yourself instead of using the helper method @Html.CheckBoxFor(). For example,
<input type="checkbox" name="IsDeposit" id="IsDeposit" value="true" />

Tuesday, February 9, 2016

jQuery UI Library - necessary jQuery file and version

The latest versions of QueryUI library have some quirks to note when adding its necessary references in the section. Below is examples that work and examples that do not work.

Basically when using jQuery UI version 1.11.4 or later, do not use jQuery version 2.x.x. Use jQuery version 1.10.x ~ 1.12.x without the jquery-migrate library.

// Works!
<link rel="stylesheet" href="jquery-ui-1.11.4/jquery-ui.css">
<script type="text/javascript" src="jquery-1.12.0.js"></script>
<script type="text/javascript" src="jquery-ui-1.11.4/jquery-ui.js"></script>

// Works!
<link rel="stylesheet" href="jquery-ui-1.11.4/jquery-ui.css">
<script type="text/javascript" src="jquery-1.10.2.js"></script>
<script type="text/javascript" src="jquery-ui-1.11.4/jquery-ui.js"></script>

// Does Not Work!
<link rel="stylesheet" href="jquery-ui-1.11.4/jquery-ui.css">
<script type="text/javascript" src="jquery-1.12.0.js"></script>
<script type="text/javascript" src="jquery-migrate-1.2.1.js"></script>
<script type="text/javascript" src="jquery-ui-1.11.4/jquery-ui.js"></script>

// Does Not Work!
<link rel="stylesheet" href="jquery-ui-1.11.4/jquery-ui.css">
<script type="text/javascript" src="jquery-1.10.2.js"></script>
<script type="text/javascript" src="jquery-migrate-1.2.1.js"></script>
<script type="text/javascript" src="jquery-ui-1.11.4/jquery-ui.js"></script>

// Does Not Work!
<link rel="stylesheet" href="jquery-ui-1.11.4/jquery-ui.css">
<script type="text/javascript" src="jquery-2.1.4.js"></script>
<script type="text/javascript" src="jquery-ui-1.11.4/jquery-ui.js"></script>