Programmatically obtain Storage Space Allocation - Lists - sharepoint

I am tasked with writing a report that pulls data out of a the "Storage Space Allocation" section within various SharePoint sites. I am able to screen scrape the general "Document Libraries" values by performing a general GET call, but I cannot programmatically obtain the "Lists" values. When I navigate to the SharePoint site (*/_layouts/storman.aspx) "Document Libraries" is the default selection. I think I need to send a POST call in order to change it to "Lists" [then I can scrape the values]. Creating the appropriate POST call is becomine a hassle because SharePoint does not seem to recognize my key/value pair (or maybe I'm not supplying all of the necessary parameters?).
I tried this code, but no luck - only the "Document Libraries" data is returned.
using (System.Net.WebClient client = new System.Net.WebClient() { UseDefaultCredentials = true })
{
NameValueCollection myQueryStringCollection = new NameValueCollection();
myQueryStringCollection.Add(queryParameterName, queryParameterValue);
client.QueryString = myQueryStringCollection;
return client.DownloadString(url);
}
I also tried this (alongside other ideas):
private static string GetWebResponse(string url, NameValueCollection parameters)
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.UseDefaultCredentials = true;
httpWebRequest.ContentType = "application/x-www-form-urlencoded";
httpWebRequest.Method = "POST";
var sb = new StringBuilder();
foreach (var key in parameters.AllKeys)
sb.Append(key + "=" + parameters[key] + "&");
sb.Length = sb.Length - 1;
byte[] requestBytes = Encoding.UTF8.GetBytes(sb.ToString());
httpWebRequest.ContentLength = requestBytes.Length;
using (var requestStream = httpWebRequest.GetRequestStream())
{
requestStream.Write(requestBytes, 0, requestBytes.Length);
requestStream.Close();
}
Task<WebResponse> responseTask = Task.Factory.FromAsync<WebResponse>(httpWebRequest.BeginGetResponse, httpWebRequest.EndGetResponse, null);
using (var responseStream = responseTask.Result.GetResponseStream())
{
var reader = new StreamReader(responseStream);
return reader.ReadToEnd();
}
}
Viewing the source code of the _layouts/storman.aspx page, I can see the name/value pair i need to send is ct100$PlaceHolderMain$m_filterDropdown and Lists respectively. I determined this by this view source code:
<select name="ctl00$PlaceHolderMain$m_filterDropdown" id="ctl00_PlaceHolderMain_m_filterDropdown" class="ms-viewselect">
<option selected="selected" value="Document Libraries">Document Libraries</option>
<option value="Documents">Documents</option>
<option value="Lists">Lists</option>
<option value="Recycle Bin">Recycle Bin</option>
</select>
Any ideas on how to get the List values from this page?

I finally figured this out...
I have to send every name/value query parameter pair that SharePoint is expecting. To complicate matters, I must first perform a GET request in order to obtain the __REQUESTDIGEST, __VIEWSTATE, __EVENTVALIDATION values that SharePoint is expecting. Also, all parameters must be UrlEncoded when I send them back to SharePoint.

Related

Empty Adobe PDF fields stripped from document when returned from DocuSign

We use Adobe Acrobat to add fields to a PDF. We want to be able to access these fields and reference their exact location so we can stamp content in their location after getting the PDF back from DocuSign. We do this by manipulating the PDF's bytes using the C# ITextSharp Text PDF library. Unfortunately, opening the PDF in Adobe Acrobat once it's returned reveals that all fields are removed from our document.
Our C# populates most of these fields with text data from our database. 2 fields are left blank, because we want to use C# to superimpose a static image of someone's signature over their location after the document has been signed via DocuSign.
This is okay for what's populated before DocuSign gets our document, because removing the field doesn't visually omit data previously entered into these fields. This is NOT fine when it comes time for us to stamp our static signature image into the document. Our usage of ITextSharp relies on finding a field with a particular Adobe Acrobat ID, getting its location, and "stamping" a static image in that location.
Is there a way to tell DocuSign that we want to maintain all of our PDF fields, their locations, and their IDs?
public byte[] StampStaticSignature(byte[] documentBytes)
{
var signatureContainer = new SignatureContainer();
var signatureBytes = signatureContainer.GetSignatureBytes();
var reader = new PdfReader(documentBytes);
var updatedForm = new byte[] { };
using var stream = new MemoryStream();
var stamper = new PdfStamper(reader, stream);
var pdfFormFields = stamper.AcroFields;
var signatureImage = GetImageFromStream(signatureBytes);
//_adobeSignatureFields is a list of strings used to
//identify the signature fields by their ID set in Adobe Acrobat.
foreach (var signatureField in _adobeSignatureFields)
{
var sigPosition = pdfFormFields.GetFieldPositions(signatureField);
var page = sigPosition[0];
var x1 = sigPosition[1];
var y1 = sigPosition[2];
var x2 = sigPosition[3];
var y2 = sigPosition[4];
var contentBytes = stamper.GetOverContent((int)page);
var signatureFieldHeight = y2 - y1;
var signatureFieldWidth = x2 - x1;
signatureImage.ScaleToFit(signatureFieldWidth, signatureFieldHeight);
signatureImage.SetAbsolutePosition(x1, y1);
contentBytes.AddImage(signatureImage);
}
stamper.FormFlattening = false;
stamper.Close();
reader.Close();
updatedForm = stream.ToArray();
stream.Dispose();
return updatedForm;
}
Hank, as Inbar mentioned already we will need to examine your code and your API logs to see what could be going wrong. It is very hard to say what went wrong, especially when using 3rd party software like ITextSharp. I'd suggest opening a case with us by visiting our support center and adding a reference to case 09033616

Creating list content type using csom

I am not able to create a list content type using the below snippet. It throws a ServerException with additional information - "The site content type has already been added to this list."
var list = clientContext.Web.Lists.GetByTitle("sometitle");
var documentCT = clientContext.Web.ContentTypes.GetById("0x0101");
clientContext.Load(list,l=> l.ContentTypes);
clientContext.Load(documentCT);
clientContext.ExecuteQuery();
var test = new ContentTypeCreationInformation(){
Name = "TestCT", ParentContentType =documentCT };
list.ContentTypes.Add(test);
list.Update();
clientContext.ExecuteQuery();
Basically, I want to create a list content type whose parent is the "Document" CT.
I encountered this same problem.
What is happening here is that you have added the content type to the list successfully but you haven't turned on "allow management of content types" in Library settings > Advanced Settings > First setting. You will not see the content type via the UI.
Once you turn on this setting you will see your content type was in fact added.
Here's how I create a library
public static List CreateLibrary(ClientContext context, string title, bool allowContentTypes)
{
ListCreationInformation lci = new ListCreationInformation
{
Description = "Library used to hold Dynamics CRM documents",
Title = title,
TemplateType = 101,
};
List lib = context.Web.Lists.Add(lci);
lib.ContentTypesEnabled = allowContentTypes ? true : false;
lib.Update();
context.Load(lib);
context.ExecuteQuery();
return lib;
}
For your case just add in the line:
list.ContentTypesEnabled = true;
Don't forget the list.Update(), I see you have it in your code but for anyone else, this part is essential before you use ExecuteQuery()

Customizing the sharepoint document set

I have to customize sharepoint 2013 document set welcome page(document set properties web part) and
I am not able to find the document set home aspx page
Can I use any client context model to fetch the data and display doc set properties.
Just create a new content type inheriting from Document Set, add the extra columns and set which ones need to be shown on the welcome page via the Document Set settings link when editing the Content Type. Also under the Document Set settings page you can click on 'Customize the Welcome Page' which allows you to edit the page just like any other web part page.
On the second point the client context will need to be connected to the web that contains the list and specific document set you're after, the identity used to connect will need permissions to the document set.
Edit:
To customize the look and feel by injecting JavaScript/CSS you'll need to make use of the ScriptLink custom action.
This lets you inject a custom piece of JavaScript into all pages. In the script you'll need logic to determine if the custom CSS should be applied, and if so Inject it.
The C# for injecting a script block via ScriptLink Custom Action:
public void AddJsLink(ClientContext ctx, Web web)
{
string scenarioUrl = String.Format("{0}://{1}:{2}/Scripts", this.Request.Url.Scheme,
this.Request.Url.DnsSafeHost, this.Request.Url.Port);
string revision = Guid.NewGuid().ToString().Replace("-", "");
string jsLink = string.Format("{0}/{1}?rev={2}", scenarioUrl, "injectStyles.js", revision);
StringBuilder scripts = new StringBuilder(#"
var headID = document.getElementsByTagName('head')[0];
var");
scripts.AppendFormat(#"
newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = '{0}';
headID.appendChild(newScript);", jsLink);
string scriptBlock = scripts.ToString();
var existingActions = web.UserCustomActions;
ctx.Load(existingActions);
ctx.ExecuteQuery();
var actions = existingActions.ToArray();
foreach (var action in actions)
{
if (action.Description == "injectnavigation" &&
action.Location == "ScriptLink")
{
action.DeleteObject();
ctx.ExecuteQuery();
}
}
var newAction = existingActions.Add();
newAction.Description = "injectnavigation";
newAction.Location = "ScriptLink";
newAction.ScriptBlock = scriptBlock;
newAction.Update();
ctx.Load(web, s => s.UserCustomActions);
ctx.ExecuteQuery();
}
Then your JavaScript would have something like:
if(window.location.href.indexOf(patternToMatchToDocSetpage)>-1) {
var link = document.createElement("link");
link.href = "http://example.com/mystyle.css";
link.type = "text/css";
link.rel = "stylesheet";
document.getElementsByTagName("head")[0].appendChild(link);
}
I'd advise you to take a look at the relevant PnP sample on script link injection

How to more easily access a ContentItem's fields in a CSHTML view?

I have written a helper function (I'm quite proud of this it - look what I can do!) that does what I need. Is there a built in way, though, to access a ContentItem's fields without having first to get the "main" ContentPart? The word "main" here means the ContentPart with the same name as the ContentType.
#functions
{
dynamic GetMainPartFromContentItem(ContentItem item)
{
var contentType = item.TypeDefinition.Name;
var parts = item.Parts as List<Orchard.ContentManagement.ContentPart>;
dynamic mainPart = parts.First(p => p.PartDefinition.Name.Equals(contentType));
return mainPart;
}
}
dynamic mainPart = GetMainPartFromContentItem(contentItem);
var shortTitle = mainPart.ShortTitle.Value; // access an InputField's value
If you have a ContentType called Page with a field called Topic you can do:
dynamic item = Model.ContentItem;
string topic = item.Page.Topic.Value;
Basically when you add fields directly to the content item, they are being added to a part on your content item called whatever your content type is, in this case Page

How to create a wiki page (=item) in Sharepoint programmatically?

how do I create a wiki page and add a title, as well as some content in sharepoint (via webservices)?
This is my SOAP message so far:
<soapenv:Body>
<soap:UpdateListItems>
<soap:listName>Cooking Wiki</soap:listName>
<soap:updates>
<Batch OnError="Continue">
<Method ID="1" Cmd="New">
<Field Name="WikiField">Mix two eggs and a cup of milk.</Field>
</Method>
</Batch>
</soap:updates>
</soap:UpdateListItems>
</soapenv:Body>
It creates a new page, but it has no content and no title.
Grab a copy of SharePoint Manager it can show you heaps of interesting info.
you want the Name field (it includes the ".aspx").
The title field is not relevant in a wiki (blank), pages are indexed by thier name instead.
--update--
Using the copy.asmx allows you to upload a new document. The template page is a page that has been downloaded previously (it stores no information, equivalent to a layout page).
private byte[] GetTemplatePage()
{
FileStream fs = new FileStream("templatePage.aspx", FileMode.Open);
byte[] fileContents = new byte[(int)fs.Length];
fs.Read(fileContents, 0, (int)fs.Length);
fs.Close();
return fileContents;
}
private void UploadDoc(string pageName)
{
byte[] wikiBytes = GetTemplatePage();
string dest = "http://[website]/wiki/Wiki%20Pages/" + pageName + ".aspx";
string[] destinationUrlArray = new string[] { dest };
IntranetCopy.Copy copyService = new IntranetCopy.Copy();
copyService.UseDefaultCredentials = true;
copyService.Url = "http://[website]/wiki/_vti_bin/copy.asmx";
IntranetCopy.FieldInformation fieldInfo = new IntranetCopy.FieldInformation();
IntranetCopy.FieldInformation[] fields = { fieldInfo };
IntranetCopy.CopyResult[] resultsArray;
copyService.Timeout = 600000;
uint documentId = copyService.CopyIntoItems(dest, destinationUrlArray, fields, wikiBytes, out resultsArray);
}
Then you can call the lists.asmx to update the wikifield.
Note: I have not figure out how to rename a document once it has been uploaded using webservices.
If nothing else is working you should develop your own web service to provide this feature. The out-of-the-box options are notoriously limited in functionality but there is nothing stopping you from adding to them.
I would wrap Nat's solution into the web service code.

Resources