How to create new sub site in SharePoint 2010 using web services? - sharepoint

In SharePoint 2010, there is a new method CreateWeb in the Sites web service with the purpose to create new SharePoint sub sites. There are, however, a lot of issues with it - well, that is my experience at least. Here is the code that should utilize the web service and its method properly:
Uri site = new Uri("http://sp2010site/");
string webServicePath = "_vti_bin/Sites.asmx";
string webServiceUrl = Path.Combine(site.AbsoluteUri, webServicePath);
Sites webService = new Sites();
webService.Credentials = CredentialCache.DefaultNetworkCredentials;
webService.Url = webServiceUrl;
//the following line creates the sub site
string result = webService.CreateWeb("newsite", "New Site", "Site desc", "STS#0",
1033, true, 1033, true, 1033, true, true, true, true, true, true, true);
The following code returns Soap exception if something wrong happened (e.g. a sub site with the address "newsite" already exists, or the specified template doesn't exist).
If everything is ok, an InvalidOperation exception is fired with the message "There is an error in XML document (1, 310).", but the site is actually created!
If everything is ok, but I use my own solution instead of the non-default SharePoint template (such as the Team site, i.e. STS#0), I get a Soap exception and the site does not get created.
This has been a terrible experience so far. Please post your experiences with the sub site creation in SP 2010, and even better, post your resolutions to these problems if you have any. Cheers all!

Steps in SP2010 beta something to create a [sub] site from a [custom] template:
Activate your site template
Browse to betasoftwaresucks/_layouts/templatepick.aspx
Click on the "Custom" tab. You should be able to see your template
Open up a tool that can inspect the DOM (e.g. IE Developer Tools or Firebug). The DOM is modified dynamically so just viewing the HTML just won't work unless you're interested in one of the default templates in the default selected tab.
Find the value of the option element representing your template. It will look like "{guid}#templatename". Make sure the "Custom" tab is still open or you might not be able to find this.
Use that value as the template parameter to CreateWeb.
[I roll my SP SOAP "by hand" so it's trivial to see the full request and response.]

SharepointSiteService.Sites siteWS = new SharepointSiteService.Sites();
siteWS.Credentials = new System.Net.NetworkCredential("user1", "password", "domain");
try
{
SharepointSiteService.Template[] templates;
siteWS.GetSiteTemplates(1033, out templates);
SharepointSiteService.Template template = (from SharepointSiteService.Template t
in templates
where t.Title == "Test Template"
select t).FirstOrDefault();
siteWS.CreateWeb("<parent web name>" + "/" + <sub web name>" + "/" + <sub web name>", "Test web", "Test Web", template.Name, 1033, true, 1033, true, 1033, true, false, false, false, false, false, false);
}
catch (Microsoft.SharePoint.SoapServer.SoapServerException e)
{
System.Diagnostics.EventLog.WriteEntry("SharePoint Foundation", "soap exception" + e.Detail + e.Message + e.Source + e.Node);
}
catch (Exception ex)
{
System.Diagnostics.EventLog.WriteEntry("SharePoint Foundation", "Site Created");
System.Diagnostics.EventLog.WriteEntry("SharePoint Foundation", ex.Message + ex.Source + ex.StackTrace);
}

Related

SharePoint 2016 Custom Action to trigger a workflow

I am trying to create a custom action that can be chosen by right-clicking on a file in a any folder of a particular SharePoint Library. This custom action would copy the file into the same folder with the user's login name appended to the end of the file name.
I currently have an event receiver that will perform the custom action when a file is being updated but that's not when I want it to happen. I was able to add a custom action to the right-click file menu using SharePoint Designer but SharePoint Designer only lets the custom action trigger special SharePoint 2010 compatible workflows or load a web page. I need to make it so the event handler (or possibly a workflow) fires when the user chooses the custom action after right-clicking on the file. I'm not sure what approach or what kind of project or app I need to create in Visual Studio 2017 to get this functionality.
Your custom action should call javascript function or perform GET request to your SharePoint hosted WCF or ASMX WebService.
ASMX
Official MSDN Walktrought: Creating a Custom ASP.NET Web Service
For additional resources with more screenshots check this blog post: Walkthrough: Creating a Custom ASP.NET (ASMX) Web Service in SharePoint 2010
WCF
Official Technet Walkthrough: SharePoint 2013: Create a Custom WCF REST Service Hosted in SharePoint and Deployed in a WSP
Note: with GET request you will need web.AllowUnsafeUpdate = true
With javascript u need AJAX call ie jQuery.ajax()
/edit
To hook up web service and your custom action use SharePoint Desinger, delete or change your existing custom action, change type to Navigate to URL and in the textbox type:
javascript: (function() { console.log('Testing...' + {ItemId}); /* your web service call */ })();
Use {ItemId} alias to pass proper item id to your AJAX call.
On the other hand, on web service side use SPWorkflowManager class to start a workflow on item. Check the code belowe (link):
public void StartWorkflow(SPListItem listItem, SPSite spSite, string wfName) {
SPList parentList = listItem.ParentList;
SPWorkflowAssociationCollection associationCollection = parentList.WorkflowAssociations;
foreach (SPWorkflowAssociation association in associationCollection) {
if (association.Name == wfName){
association.AutoStartChange = true;
association.AutoStartCreate = false;
association.AssociationData = string.Empty;
spSite.WorkflowManager.StartWorkflow(listItem, association, association.AssociationData);
}
}
}
I found a way to do this using JavaScript, without SharePoint Designer. I put the following script in a Content Editor web part on the page where the listview webpart is and now I can right click on a file and get the option to "Get My Copy". If you have a Comments sub folder, the renamed copy will get put there.
<script type="text/javascript">
// adds the menu option to Get My Copy
function Custom_AddDocLibMenuItems(m, ctx)
{
var strDisplayText = "Get My Copy"; //Menu Item Text
var strAction = "copyFile()";
var strImagePath = ""; //Menu item Image path
CAMOpt(m, strDisplayText, strAction, strImagePath); // Add our new menu item
CAMSep(m); // add a separator to the menu
return false; // false means standard menu items should also be rendered
}
// append current user account to filename and copy to subfolder named Comments
function copyFile()
{
// get web and current user from context
var context = new SP.ClientContext.get_current();
var web = context.get_web();
this.currentUser = web.get_currentUser();
context.load(currentUser);
// load the folder
var currentFolder = decodeURIComponent(ctx.rootFolder);
var folderSrc = web.getFolderByServerRelativeUrl(currentFolder);
context.load(folderSrc,'Files');
context.executeQueryAsync(
function() {
// get the first (and hopefully only) file in the folder
var files = folderSrc.get_files();
var e = files.getEnumerator();
e.moveNext()
var file = e.get_current();
// get user account
var curUserAcct = currentUser.get_loginName();
curUserAcct = curUserAcct.substring(curUserAcct.indexOf("\\") + 1);
// get file without extension
var file_with_ext = file.get_name();
var name_without_ext = file_with_ext.substr(0, file_with_ext.lastIndexOf("."));
var destLibUrl = currentFolder + "/Comments/" + name_without_ext + " " + curUserAcct + ".docx";
file.copyTo(destLibUrl, true);
context.executeQueryAsync(
function() { alert("Success! File File successfully copied to: " + destLibUrl); },
function(sender, args) { alert("error: " + args.get_message()) }
);
},
function(sender, args){ alert("Something went wrong with getting current user or getting current folder '" + currentFolder + "'. " + args.get_message()); }
);
}
</script>

The workflow template has specified no FormURN for this page Error when Associating Out of the box SharePoint 2010 publihsing approval workflow

Hello i have associated a imported a out of the box SharePoint 2010 Publishing Approval Workflow from Sharepoint Designer 2013 to Visual studio 2012 with the import Sharepoint 2010 workflow project in Visual Studio.
I have also Assocaited the workflow with C# like you see below:
public void AddWorkflowAssociationToPagesList(SPList assoicationList, string spWorkflowTemplateGuid)
{
try
{
SPWorkflowTemplate workflowTemplate =
_web.WorkflowTemplates.GetTemplateByBaseID(new Guid(spWorkflowTemplateGuid));
if (workflowTemplate == null) { _logger.LogError(LoggingCategory.Workflow, "Error in associating workflow with guid: " + spWorkflowTemplateGuid + " The workflowtemplate does not exist in: " + _web.MasterUrl); }
SPList pagesList = _web.Lists[PublishingWeb.GetPagesListName(_web)];
if (workflowTemplate != null)
{
var taskList = _listProvisioner.TryGetList(_web, _localaizedWorkFlowTaskList, string.Empty, true, SPListTemplateType.Tasks);
var historyList = _listProvisioner.TryGetList(_web, _localaizedWorkFlowHistoryList, string.Empty, true, SPListTemplateType.WorkflowHistory);
SPWorkflowAssociation workflowAssocaition = SPWorkflowAssociation.CreateListAssociation(workflowTemplate,_workflowAssociationName, taskList, historyList);
assoicationList.WorkflowAssociations.Add(workflowAssocaition);
_web.AllowUnsafeUpdates = true;
assoicationList.DefaultContentApprovalWorkflowId = workflowAssocaition.Id;
assoicationList.Update();
_web.AllowUnsafeUpdates = false;
}
}
catch (Exception ex)
{
_logger.LogError(LoggingCategory.Workflow, "Error: " + ex + " in associating workflow with id: " + spWorkflowTemplateGuid);
}
}
The error occurs when i try to publish a page in the pages library:
The workflow template has specified no FormURN for this page
The ULS Log gives me this on the correlation id:
Application error when access /_layouts/15/IniWrkflIP.aspx, Error=The workflow template has specified no FormURN for this page.
at Microsoft.Office.Workflow.IniWrkflIPPage.LoadFromWorkflowAssociation()
at Microsoft.Office.Workflow.IniWrkflIPPage.OnLoad(EventArgs ea)
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
This does not occur if i change the start condition of the workflow to start when creating a new item in the library, only when starting the workflow by publishing a page.
Has anyone experienced this kind of problem before ?
Regards.
Are you trying to publish it from the ribbon on the page itself? Unfortunately that part is fully dependent on the start form being an InfoPath form instead of an ASP.Net form. Yet if you try and publish it via checkin/major or Publish a Major Version via the context menu on the Pages library, you'll find that it will work correctly. Let me know if you're getting the same behavior both ways, or if its solely on the buttons in the ribbon on the page itself.

Add Contact Page Owner EMAIL ONLY link to Master Page in SharePoint 2010

I'm in a SharePoint 2010 Publishing site, full license. I'm attempting to add a "Contact Page Owner" email link in a custom footer of a copy of the v4.master that grabs the Page Owner's email address from the page properties: Page Owner, and uses that email address in the contact link. I've tried adding the Page Contact web part; this forces in the page owner's name, and on click pops up some info about the page owner (which includes that email). However, I need that link to say, "Contact Page Owner". Any ideas?
If you are using a customized masterpage, you should be able add a simple javascrip\jquery to get hold of the anchor tag and change it's text to "Contact Page Owner".
I've tried adding the Page Contact web part as well, and it became a mess. Try tapping into the Page Properties > Page Owner and/or Page Owner Contact Email. This question is answered here, but here's the answer again:
You need jQuery 1.7.x+ and the SPServices jQuery library version 0.7.2 or greater installed on your site.
Use GetListItems as the operation from SPServices.
I'm searching for pages within the Pages directory, so listName is "Pages". Note that list pages and system pages will need a different treatment.
The CAML View Fields are basically the columns for PublishingContactEmail and PublishingContact. I found those using u2u's CAML builder version 4.0.0.0
The ows_ variables can be found in the xml view of the POST object in firebug.
The ows_PublishingContact returns a long nasty string of the contact's information. Fortunately the email address is surrounded by ,#, which made splitting it into an array and then searching for an email # easy, but that's why that's there.
function get_page_contact_email() {
var thisPageID = _spPageContextInfo.pageItemId;
var e;
$().SPServices({
operation: "GetListItems",
async: false,
listName: "Pages",
CAMLViewFields: "<ViewFields><FieldRef Name='PublishingContactEmail' /><FieldRef Name='PublishingContact' /></ViewFields>",
CAMLQueryOptions: "<QueryOptions><ExpandUserField>True</ExpandUserField></QueryOptions>",
completefunc: function (xData, Status) {
$(xData.responseXML).SPFilterNode("z:row").each(function () {
if (thisPageID == $(this).attr("ows_ID")) {
if ($(this).attr("ows_PublishingContactEmail")) { // if page email is set
e = $(this).attr("ows_PublishingContactEmail");
} else if ($(this).attr("ows_PublishingContact")) { //otherwise use contact info
var contact = $(this).attr("ows_PublishingContact").split(",#");
for (var c = 0; c < contact.length; c++) {
if (contact[c].indexOf("#") != -1) {
e = contact[c];
}
}
} else { //or nothing is set.
e = false;
}
}
});
}
});
return e;
}

How to programmatically add target lists to the what's new web part in Sharepoint (or how to handle undocumented namespaces)

From code I've automatically created a lot of similar sites (SPWeb) in my site collection from a site template (in Sharepoint Foundation). Every site has a home page on which I've added the "what's new" web part (found under "Social collaboration").
Even though the web part has several "target lists" (I'd have called it "source lists") added to it on the template site, this connection is lost on the sites created from the template. So I need to programmatically find all these web parts and add the target lists to them. Looping the web parts is not an issue - I've done that before - but I can't seem to find a word on the net on how to go about modifying this particular web part. All I have is a brief intellisense.
I've found out that it recides in the
Microsoft.SharePoint.Applications.GroupBoard.WebPartPages
namespace, but on the lists provided on MSDN this is one of very few namespaces that doesn't have a link to a reference documentation.
Does anyone have any experience of modifying this web part from code? If not, how would you go about to find out? I can't seem to figure out a method for this..
Here is how I did it. It worked really well. I had a feature that created several list instances and provisioned the What's New web part. In the Feature Receiver, I looped through all of the list instances, indexed the Modified field, and then added the list to the web part:
private void ConfigureLists(SPWeb web, SPFeatureReceiverProperties properties)
{
List<Guid> ids = new List<Guid>();
SPElementDefinitionCollection elements =
properties.Feature.Definition.GetElementDefinitions(new CultureInfo((int)web.Language, false));
foreach (SPElementDefinition element in elements)
{
if ("ListInstance" == element.ElementType)
{
XmlNode node = element.XmlDefinition;
SPList list = web.Lists[node.Attributes["Title"].Value];
SPField field = list.Fields[SPBuiltInFieldId.Modified];
if (!field.Indexed)
{
field.Indexed = true;
field.Update();
}
ids.Add(list.ID);
}
}
string targetConfig = string.Empty;
foreach (Guid id in ids)
{
targetConfig += string.Format("'{0}',''\n", id);
}
SPFile file = web.GetFile("Pages/default.aspx");
file.CheckOut();
using (SPLimitedWebPartManager manager = file.GetLimitedWebPartManager(PersonalizationScope.Shared))
{
WhatsNewWebPart webpart = null;
foreach (System.Web.UI.WebControls.WebParts.WebPart eachWebPart in manager.WebParts)
{
webpart = eachWebPart as WhatsNewWebPart;
if (null != webpart)
{
break;
}
}
if (null != webpart)
{
webpart.TargetConfig = targetConfig;
manager.SaveChanges(webpart);
}
}
file.CheckIn("ConfigureWebParts");
file.Publish("ConfigureWebParts");
file.Approve("ConfigureWebParts");
}
If you are unsure about the property, export the web part from the browser, then open the .webpart/.dwp file with a text editor. Somewhere in the xml will be a reference to the source list.
*.webparts are usually easier to modify, just set the property.
*.dwps are harder because you sometimes have to get the property (eg ViewXML), then load it into an XmlDocument, then replace the property, and write the xml document string value back to ViewXML.

How to change web part property values

In a SharePoint 2007 web part, I want to delete an existing property and replace it with a property using a different name. I want to get the value from the existing property and assign it to the new property.
How should I do this?
In summary:
Get a reference to the page containing the web part.
Get a reference to the web part itself.
Change the property value.
Save the change.
In code:
using (SPSite site = new SPSite("http://sharepoint"))
using (SPWeb web = site.OpenWeb("Web Title"))
using (SPLimitedWebPartManager webPartManager =
web.GetLimitedWebPartManager("default.aspx", PersonalizationScope.Shared))
{
try
{
foreach (WebPart webPart in webPartManager.WebParts)
{
if ((webPart.Title == "Web Part Title") && (!webPart.IsClosed))
{
YourWebPart wp = (YourWebPart)webPart;
wp.NewProperty = wp.OldProperty;
webPartManager.SaveChanges(wp);
web.Update();
break;
}
}
}
finally
{
webPartManager.Web.Dispose();
}
}
Replace the following in this code example:
"http://sharepoint" - the address of your SharePoint site
"Web Title" - the title of the SharePoint web containing the web part to be changed (or use one of the other OpenWeb overloads
"default.aspx" - filename of the page containing the web parts
"Web Part Title" - title given to the web part on the page
YourWebPart - class name of the web part to change
NewProperty/OldProperty - names of the properties to change

Resources