SOLVED

Save as template again!!!

Copper Contributor

I am wondering if I can save a team site on SharePoint online as a template in a simple way??!!

not using the PnP provisioning its too complicated...

or is there a complete example of pnp code that works in visual studio??

11 Replies

Do you want to save a template from modern or classic team site?

What needs to be included in the template?

best response confirmed by Beau Cameron (MVP)
Solution
Just wondering, why do you consider the PnP provisioning to be too complicated? You are looking for a solution from a Developer's perspective. With the PnP NuGet packages installed in your project, "Save as template" is basically just 1 line of code (web.GetProvisioningTemplate). Applying a template to another site is also just 1 line of code (web.ApplyProvisioningTemplate).

A sample console application using the above techniques can be found here:
https://docs.microsoft.com/en-us/sharepoint/dev/solution-guidance/provisioning-console-application-s...

For SharePoint Online modern sites you can make use of Site Designs and Site Scripts. You could combine these with PnP provisioning as well.
https://docs.microsoft.com/en-us/sharepoint/dev/declarative-customization/site-design-overview

Hope this helps!
modern team site

hi@Paul Pascha 

this is my code

It works fine when i press start and go to the end of it but my destination site doesn't change...!!

 

 

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.SharePoint.Client;
using OfficeDevPnP.Core.Framework.Provisioning.Connectors;
using OfficeDevPnP.Core.Framework.Provisioning.Model;
using OfficeDevPnP.Core.Framework.Provisioning.ObjectHandlers;
using OfficeDevPnP.Core.Framework.Provisioning.Providers.Xml;
using System.Net;
using System.Security;
using System.Threading;
namespace Program
{
class Program
{
static void Main(string[] args)
{
ConsoleColor defaultForeground = Console.ForegroundColor;

// Collect information
string templateWebUrl = GetInput("Enter the URL of the template site: ", false, defaultForeground);
string targetWebUrl = GetInput("Enter the URL of the target site: ", false, defaultForeground);
string userName = GetInput("Enter your user name:", false, defaultForeground);
string pwdS = GetInput("Enter your password:", true, defaultForeground);
SecureString pwd = new SecureString();
foreach (char c in pwdS.ToCharArray()) pwd.AppendChar(c);

// GET the template from existing site and serialize
// Serializing the template for later reuse is optional
ProvisioningTemplate template = GetProvisioningTemplate(defaultForeground, templateWebUrl, userName, pwd);

// APPLY the template to new site from
ApplyProvisioningTemplate(defaultForeground, targetWebUrl, userName, pwd, template);

// Pause and modify the UI to indicate that the operation is complete
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("We're done. Press Enter to continue.");
Console.ReadLine();
}

private static void ApplyProvisioningTemplate(ConsoleColor defaultForeground, string targetWebUrl, string userName, SecureString pwd, ProvisioningTemplate template)
{

}

private static string GetInput(string label, bool isPassword, ConsoleColor defaultForeground)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("{0} : ", label);
Console.ForegroundColor = defaultForeground;

string value = "";

for (ConsoleKeyInfo keyInfo = Console.ReadKey(true); keyInfo.Key != ConsoleKey.Enter; keyInfo = Console.ReadKey(true))
{
if (keyInfo.Key == ConsoleKey.Backspace)
{
if (value.Length > 0)
{
value = value.Remove(value.Length - 1);
Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
Console.Write(" ");
Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
}
}
else if (keyInfo.Key != ConsoleKey.Enter)
{
if (isPassword)
{
Console.Write("*");
}
else
{
Console.Write(keyInfo.KeyChar);
}
value += keyInfo.KeyChar;

}

}
Console.WriteLine("");

return value;
}

private static ProvisioningTemplate GetProvisioningTemplate(ConsoleColor defaultForeground, string webUrl, string userName, SecureString pwd)
{
using (var ctx = new ClientContext(webUrl))
{
// ctx.Credentials = new NetworkCredentials(userName, pwd);
ctx.Credentials = new SharePointOnlineCredentials(userName, pwd);
ctx.RequestTimeout = Timeout.Infinite;

// Just to output the site details
Web web = ctx.Web;
ctx.Load(web, w => w.Title);
ctx.ExecuteQueryRetry();

Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("Your site title is:" + ctx.Web.Title);
Console.ForegroundColor = defaultForeground;

ProvisioningTemplateCreationInformation ptci
= new ProvisioningTemplateCreationInformation(ctx.Web);

// Create FileSystemConnector to store a temporary copy of the template
ptci.FileConnector = new FileSystemConnector(@"c:\temp\pnpprovisioningdemo", "");
ptci.PersistComposedLookFiles = true;
ptci.ProgressDelegate = delegate (String message, Int32 progress, Int32 total)
{
// Only to output progress for console UI
Console.WriteLine("{0:00}/{1:00} - {2}", progress, total, message);
};

// Execute actual extraction of the template
ProvisioningTemplate template = ctx.Web.GetProvisioningTemplate(ptci);

// We can serialize this template to save and reuse it
// Optional step
XMLTemplateProvider provider =
new XMLFileSystemTemplateProvider(@"c:\temp\pnpprovisioningdemo", "");
provider.SaveAs(template, "PnPProvisioningDemo.xml");


ProvisioningTemplate temp= ctx.Web.GetProvisioningTemplate(ptci);

// ctx.Credentials = new NetworkCredentials(userName, pwd);
ctx.Credentials = new SharePointOnlineCredentials(userName, pwd);
ctx.RequestTimeout = Timeout.Infinite;
ProvisioningTemplateApplyingInformation ptai
= new ProvisioningTemplateApplyingInformation();
ptai.ProgressDelegate = delegate (String message, Int32 progress, Int32 total)
{
Console.WriteLine("{0:00}/{1:00} - {2}", progress, total, message);
};

// Associate file connector for assets
FileSystemConnector connector = new FileSystemConnector(@"c:\temp\pnpprovisioningdemo", "");
template.Connector = connector;

// Because the template is actual object, we can modify this using code as needed
template.Lists.Add(new ListInstance()
{
Title = "PnP Sample Contacts",
Url = "lists/PnPContacts",
TemplateType = (Int32)ListTemplateType.Contacts,
EnableAttachments = true
});

web.ApplyProvisioningTemplate(template, ptai);

return template;


}


}

}

}

 

I can see from your code you don't do anything to your destination site. Your ApplyProvisioningTemplate method is empty and in your GetProvisioningTemplate method you're applying a modified template to the web you've specified in templateWebUrl. So it seems (haven't run your code) your source site is ultimately changed to include a List with Title "PnP Sample Contacts".

@Paul Pascha 

what to write in my ApplyProvisioningTemplate?
the code opens up a console and it gets the template and target site and it works on them.

but what happens at the end is exactly what u said. 

@Soheilsd wondering if you have used site designs. We can configure new sites based on templates.

https://docs.microsoft.com/en-us/sharepoint/dev/declarative-customization/site-design-overview

Something like this should work:

private static void ApplyProvisioningTemplate(ConsoleColor defaultForeground, string targetWebUrl, string userName, SecureString pwd, ProvisioningTemplate template)
{
using (var ctx = new ClientContext(targetWebUrl))
{
ctx.Credentials = new SharePointOnlineCredentials(userName, pwd);

ctx.Web.ApplyProvisioningTemplate(template);
}
}

@Paul Pascha 

thanks a lot it works perfectly.

@Paul Pascha 

 

Hi my code were just doing fine until it gets this error at the end of it in the  "web.ApplyProvisioningTemplate(template, ptai);" section:

 

error code:

 “The formula refers to a column that does not exist. Check the formula for spelling mistakes or change the non-existing column to an existing column.”

 

what should I do with that??!!

This is an error related to a Calculated Field in your template. As the error says it refers to a column that doesn't exist. You should check the name of your column and make sure it exists before your calculated field is created.

Does this make sense?
1 best response

Accepted Solutions
best response confirmed by Beau Cameron (MVP)
Solution
Just wondering, why do you consider the PnP provisioning to be too complicated? You are looking for a solution from a Developer's perspective. With the PnP NuGet packages installed in your project, "Save as template" is basically just 1 line of code (web.GetProvisioningTemplate). Applying a template to another site is also just 1 line of code (web.ApplyProvisioningTemplate).

A sample console application using the above techniques can be found here:
https://docs.microsoft.com/en-us/sharepoint/dev/solution-guidance/provisioning-console-application-s...

For SharePoint Online modern sites you can make use of Site Designs and Site Scripts. You could combine these with PnP provisioning as well.
https://docs.microsoft.com/en-us/sharepoint/dev/declarative-customization/site-design-overview

Hope this helps!

View solution in original post