SPMeta2 System.InvalidCastException while trying to deploy model - c#

I am currently trying to create a simple list in Sharepoint 365, using SPMeta2 framework. I have tried doing it according to the documentation, and got an exception, then I've decided to simply copy code from their samples, located here Now, when I'm trying to deploy my model I keep getting System.InvalidCastException. It says it needs SiteModelHost, but when I give it SiteModelHost I get same exception, saying WebModelHost. Obviously, when I give it web it asks for site. I would be grateful for any pointers.
Here is my code for deploying the model:
class Program
{
static void Main (string[] args)
{
var targetSite = new Uri("https://url.com/");
var login = "***.com";
var password = "1234";
var securePassword = new SecureString();
foreach (char c in password)
{
securePassword.AppendChar(c);
}
var onlineCredentials = new SharePointOnlineCredentials(login, securePassword);
var model = SPMeta2Model
.NewSiteModel(site =>
{
site
.WithFields(fields =>
{
fields
.AddField(FieldModels.Contact)
.AddField(FieldModels.Details);
})
.WithContentTypes(contentTypes =>
{
contentTypes
.AddContentType(ContentTypeModels.CustomItem)
.AddContentType(ContentTypeModels.CustomDocument);
})
.WithLists(lists =>
{
lists
.AddList(ListModels.TestLibrary)
.AddList(ListModels.TestList)
.AddList(ListModels.TestLinksList);
});
});
using (var context = new ClientContext(targetSite))
{
context.Credentials = onlineCredentials;
var povisionService = new CSOMProvisionService();
povisionService.DeployModel(WebModelHost.FromClientContext(context), model); // WebModelHost/SiteModelHost - same exception
}

Easy.
SPMeta2 allows to several types of models.
Site model reflects and contains all artefacts which could be deployed at site level - site features, custom actions, site fields, content types and so on.
Site model should be deployed within 'site model host'.
Web model reflects and contains all artefacts which could be deployed at web level - web features, lists, list views and so on.
Web model should be deployed within 'web model host'.
With CSOM provision, SiteModelHost.FromClientContext(context) and WebModelHost.FromClientContext(context) should be used to push site or web model accordingly.
Here is a working code for site model.
We removed lists (they belong to web) and use SiteModelHost.
var siteModel = SPMeta2Model
.NewSiteModel(site =>
{
site
.WithFields(fields =>
{
fields
.AddField(FieldModels.Contact)
.AddField(FieldModels.Details);
})
.WithContentTypes(contentTypes =>
{
contentTypes
.AddContentType(ContentTypeModels.CustomItem)
.AddContentType(ContentTypeModels.CustomDocument);
});
});
using (var context = new ClientContext(targetSite))
{
var povisionService = new CSOMProvisionService();
povisionService.DeployModel(SiteModelHost.FromClientContext(context), siteModel);
}
And the web model here.
We removed fields/content types, and use only lists plus WebModelHost.
var webModel = SPMeta2Model
.NewWebModel(web =>
{
web
.WithLists(lists =>
{
lists
.AddList(ListModels.TestLibrary)
.AddList(ListModels.TestList)
.AddList(ListModels.TestLinksList);
});
});
using (var context = new ClientContext(targetSite))
{
var povisionService = new CSOMProvisionService();
povisionService.DeployModel(WebModelHost.FromClientContext(context), webModel);
}
Finally, here are several links to get started:
Sample project at github
https://github.com/SubPointSolutions/spmeta2.contoso
SPMeta2 Yammer Group
http://yammer.com/spmeta2feedback
Some documentation (work in progress)
http://docs.subpointsolutions.com/spmeta2
Let me know how it goes, and if you need further assistance.
Cheers!

Related

Dropbox C# API v2 List Team folder contents

Good afternoon,
I am trying to get the list of directories from the dropbox API for the team members in business dropbox.
The documentation seems to be somewhat unclear and frustratingly difficult to get contents. Therefore my code could off somewhat and hence why i am having issues. I have a development token for both Team and my admin user.
using (var client = new DropboxTeamClient("my token"))
{
var teamInfo = await client.Team.GetInfoAsync();
var teamName = teamInfo.Name;
var numberOfUsers = teamInfo.NumProvisionedUsers;
var memListResult = await client.Team.MembersListAsync();
foreach (var m in memListResult.Members)
{
var accountId = m.Profile.AccountId;
var email = m.Profile.Email;
Console.WriteLine($"Id {accountId} - email is {email}");
}
var accId = memListResult.Members.First(x => x.Profile.Email.Equals("myEmail"))
?.Profile.AccountId;
var memId = memListResult.Members.First(x => x.Profile.Email.Equals("myEmail"))
?.Profile.TeamMemberId;
var dbx = client.AsAdmin(memId);
try
{
var full = await dbx.Users.GetCurrentAccountAsync();
Console.WriteLine("{0} - {1}", full.Name.DisplayName, full.Email);
await ListRootFolder(dbx, true);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private async Task ListRootFolder(DropboxClient dbx)
{
var list = await dbx.Files.ListFolderAsync(string.Empty);
//var tlist = await dbx.
// show folders then files
foreach (var item in list.Entries.Where(i => i.IsFolder))
{
Console.WriteLine("D {0}/", item.Name);
}
foreach (var item in list.Entries.Where(i => i.IsFile))
{
Console.WriteLine("F{0,8} {1}", item.AsFile.Size, item.Name);
}
}
No my issue is that i can only ever get what shows in my directory. So for example i have logged into the web browser into dropbox i have three directories.
|
|- User Dir 'This is my home directory'
|- Team Dir 'This is a directory for the team'
|- Sample folder
The team has access to the Team dir and sample folder. All i would like to do is simply get the list of directories.
I must be doing something completely wrong, i have also tried the overrides for dbx.Files.ListFolderAsync i have set includeMountedFolders parameter to true and still only lists two files in my profile.
I have also tried using the user token instead of the Team token and setting asAdmin.
Apologies on the code is somewhat untidy i just want to get it working before i refactor.
Any help would be appreciated.
Thanks
It sounds like you want to access your "team space". You need to explicitly specify this when calling the API. I recommend reading the Namespace Guide, which covers this in detail.
The .NET SDK supports setting the Dropbox-Api-Path-Root header, via DropboxClient.WithPathRoot.
First, you can get the root info from GetCurrentAccountAsync:
var accountInfo = await dbx.Users.GetCurrentAccountAsync();
Console.WriteLine(accountInfo.RootInfo.RootNamespaceId);
Then, you can access the team shared space by using DropboxClient.WithPathRoot to set the Dropbox-Api-Path-Root header as desired, like:
dbx = dbx.WithPathRoot(new PathRoot.NamespaceId(accountInfo.RootInfo.RootNamespaceId));
var res = await this.client.Files.ListFolderAsync(path: "");
foreach (var entry in res.Entries)
{
Console.WriteLine(entry.Name);
}

Google AdWords library doesn't create an access token

I'm trying to make a targetingIdeaService API call to Google AdWords. This is my code so far:
[HttpGet]
public IEnumerable<string> Get()
{
var user = new AdWordsUser();
using (TargetingIdeaService targetingIdeaService = (TargetingIdeaService)user.GetService(AdWordsService.v201802.TargetingIdeaService))
{
// Create selector.
TargetingIdeaSelector selector = new TargetingIdeaSelector();
selector.requestType = RequestType.IDEAS;
selector.ideaType = IdeaType.KEYWORD;
selector.requestedAttributeTypes = new AttributeType[] {
AttributeType.KEYWORD_TEXT,
AttributeType.SEARCH_VOLUME,
AttributeType.AVERAGE_CPC,
AttributeType.COMPETITION,
AttributeType.CATEGORY_PRODUCTS_AND_SERVICES
};
// Set selector paging (required for targeting idea service).
var paging = Paging.Default;
// Create related to query search parameter.
var relatedToQuerySearchParameter =
new RelatedToQuerySearchParameter
{ queries = new String[] { "bakery", "pastries", "birthday cake" } };
var searchParameters = new List<SearchParameter> { relatedToQuerySearchParameter };
var page = new TargetingIdeaPage();
page = targetingIdeaService.get(selector);
return new string[] { "value1", "value2" };
}
}
it looks ok, compiles at last, and so on. But then I went into debug mode. And I saw this:
So as you can see, the variable doesn't have the access token. The other data comes from app.config file.
I am quite certain the keys passed in are correct.
Then the code throws the famous invalid_grand error. In my case, I believe that's because the access token is not being generated. I'm new to AdWords and ASP.NET, so I probably missed something, but I have no idea what.
I used the
docs,
Code Structure instructions, and
code examples to put it all together.
I had my configuration wrong. I had to recreate all the credentials using incognito window. If you have any issues just open a thread here: https://groups.google.com/forum/#!forum/adwords-api

Sharepoint 2013 On-Premises C# CSOM Cross Site Collection List Access

I see this has sorta been asked a bunch times before, but all the examples I am running across will not work or are in JavaScript , I NEED HELP WITH C# please.
I have a farm with server site collections on it, I successfully created a provider hosted addin/App, When it trys to access lists on the Web that launched it everything is fine! I need to try to access lists on other webs in the same farm and on the same domain does anyone have an example of C# code that can do this
You can create a repository method like this:
public class SharepointRepository
{
public ListItemCollection ListTopN(string urlSite, string listName, bool ascending, string column, int rowLimit)
{
using (var context = new ClientContext(urlSite))
{
context.Credentials = CredentialCache.DefaultCredentials;
List list = context.Web.Lists.GetByTitle(listName);
string myQuery = string.Format("<View><Query><OrderBy><FieldRef Name='{0}' Ascending='{1}' /></OrderBy></Query><RowLimit>{2}</RowLimit></View>", column, ascending.ToString(), rowLimit);
CamlQuery query = new CamlQuery();
query.ViewXml = myQuery;
ListItemCollection collection = list.GetItems(query);
context.Load(list);
context.Load(collection);
context.ExecuteQuery();
return collection;
}
}
}
this approach uses the managed csom.
and if you are facing problems with ADFS, try adding after this line
context.Credentials = CredentialCache.DefaultCredentials;
this
context.ExecutingWebRequest += new EventHandler<WebRequestEventArgs>(MixedAuthRequestMethod);
and this function
void MixedAuthRequestMethod(object sender, WebRequestEventArgs e)
{
e.WebRequestExecutor.RequestHeaders.Add("X-FORMS_BASED_AUTH_ACCEPTED", "f");
}
This is a basic referente:
https://msdn.microsoft.com/en-us/library/office/fp179912.aspx
You should also look at the Sharepoint App Model and the Rest OData API.
I figured this out, so I am posting it just in case someone else needs it, also please understand that I am total noob to SharePoint and this may not be the best way or even the SharePoint Accepted way of doing things.
First you need to give you app Tenant Permission (Full Control or Manage) ! - Very Important
Second I created this function that make a n SharePoint Context to a site other then the one the app is running on
public ClientContext CreateRemoteSharePointContext(string TargetWebURL, SharePointContext CurrentSharePointContext)
{
//In order for us to create a share point client context that points to
//site other then the site that this app is running we need to copy some url parameters from the current
//context. These parameters are found on the current share-point context
NameValueCollection QueryString = Request.QueryString;
//Since, The Query string is a read only collection, a use of reflection is required to update the
//values on the request object, we must use the current request object because it contains
//security and other headers/cookies that we need for the context to be created, Grab the url params that we need
//other then TargetWebUrl, that will be the url of the site we want to manipulate
Utility.AddToReadonlyQueryString(QueryString, "SPHostUrl", CurrentSharePointContext.SPHostUrl.ToString(), System.Web.HttpContext.Current.Request);
Utility.AddToReadonlyQueryString(QueryString, "SPAppWebUrl", TargetWebURL, System.Web.HttpContext.Current.Request);
Utility.AddToReadonlyQueryString(QueryString, "SPLanguage", CurrentSharePointContext.SPLanguage, System.Web.HttpContext.Current.Request);
Utility.AddToReadonlyQueryString(QueryString, "SPClientTag", CurrentSharePointContext.SPClientTag, System.Web.HttpContext.Current.Request);
Utility.AddToReadonlyQueryString(QueryString, "SPProductNumber", CurrentSharePointContext.SPProductNumber, System.Web.HttpContext.Current.Request);
//This is a special line, we need to get the AppOnly access token and pass it along to the target site, its is a little counter intuitive
//Because we are using TokenHelper.GetS2SAccessToeknWithWindowsIdentity - but we pass NULL as the User identity, this will
//check the app manifest and if the app has a CERT and AppOnly Permission it will return a valid app only token to use
Utility.AddToReadonlyQueryString(QueryString, "AppContextToken", TokenHelper.GetS2SAccessTokenWithWindowsIdentity(new Uri(TargetWebURL), null), System.Web.HttpContext.Current.Request);
//Return the newly created context
return SharePointContextProvider.Current.CreateSharePointContext(HttpContext.Request, TargetWebURL).CreateAppOnlyClientContextForSPAppWeb();
}
As you can see the I had to kinda hack up the Querystring and grab some values so here is the Utility class that does that :
public class Utility
{
public static void UpdateReadonlyQueryString(NameValueCollection collectionToUpdate, string paramName, string paramValue, HttpRequest Request)
{
collectionToUpdate = (NameValueCollection)Request.GetType().GetField("_queryString", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(Request);
PropertyInfo readOnlyInfo = collectionToUpdate.GetType().GetProperty("IsReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);
readOnlyInfo.SetValue(collectionToUpdate, false, null);
collectionToUpdate[paramName] = paramValue;
readOnlyInfo.SetValue(collectionToUpdate, true, null);
}
public static void AddToReadonlyQueryString(NameValueCollection collectionToUpdate, string paramName, string paramValue, HttpRequest Request)
{
collectionToUpdate = Request.QueryString;
collectionToUpdate = (NameValueCollection)Request.GetType().GetField("_queryString", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(Request);
PropertyInfo readOnlyInfo = collectionToUpdate.GetType().GetProperty("IsReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);
readOnlyInfo.SetValue(collectionToUpdate, false, null);
collectionToUpdate.Add( paramName, paramValue);
readOnlyInfo.SetValue(collectionToUpdate, true, null);
}
}
and Finally the SharePoint access code the looks like much of the same SharePoint code out there on the web, I had to remove some stuff from it that would identify the project or who its for, but it should be easy to pick out what you need from in side
try
{
//Get the name of the sharepoint list that needs to be updated from settings
var ListName = ConfigurationManager.AppSettings[Constants.SettingsConstants.SPLaunchQueList];
var TargetSiteToUpdate = "URL TO THE SITE YOUR TRYING TO UPDATE";
//Get the sharepoint context from session
var spContext = <SOME HOW CREATE YOUR CONTEXT>
//Lets create a client context from the current sharepoint context to the target site
//NOTE this requires the application to HAVE Tenant level permission, it must be trusted by
//the farm admin
using (var spClientContext = CreateRemoteSharePointContext(TargetSiteToUpdate, spContext))
{
//Get the current Web (Sharepoint Web) from the client context
var web = spClientContext.Web;
//Load all the webs properties including title , url all the lists and get the subwebs if any as well
spClientContext.Load(web, x => x.Title, x => x.Url, x => x.Lists, x => x.Webs.Include(w => w.Title, w => w.Url));
spClientContext.ExecuteQuery();
//Lets grab the list that needs to be updated
SP.List OrgList = web.Lists.GetByTitle(ListName);
//Construct a caml query Where the groupID of the SQL Server record is the same
//as the list GroupID
var caml = new Caml<DetailParts>().Where(o => o.GroupID == updateRecord.GroupID);
CamlQuery camlQuery = new CamlQuery();
camlQuery.ViewXml = caml.ToString();
//Load the CAML query
ListItemCollection Rows = OrgList.GetItems(camlQuery);
spClientContext.Load(Rows);
spClientContext.ExecuteQuery();
//The CAML Query should only return one row because GroupID should be UNQIUE
//however due to how sharepoint returns list data we are forcing the first value
//here
ListItem RowToUpdate = Rows[0];
//Get a list of sharepoint columns that match the local detail parts
var ColumnsToUpdate = GetSharePointColumns(typeof(DetailParts));
RowToUpDate["SomeColumn"] = "YOUR NEW VALUE";
RowToUpdate.Update();
//Commit the changes
spClientContext.ExecuteQuery();
}
}
}
catch (Exception ex)
{
//Log any exception and then throw to the caller
logger.Error("Sharepoint exception", ex);
}
That last section of code should be in a function or method of some sort I just pull out the relevant parts. As I Stated this is the only way I found that works and if someone has a better way please share it as I am not a SharePoint expert.

CanvasAuthorize C# SDK Infinite Loop

I'm looking to try and get an MVC3 Canvas app working with the Facebook C# SDK, but am struggling to allow permissions - Below is my code, and when I open the app I get the 'Allow / Deny' dialog but when I click allow I get redirected to my app and the same dialog appears again (And again and so on no matter how many times I click allow)?
I guess I am missing something obvious... If I take the user_groups permission out it works fine, I just can't access the persons groups.
[CanvasAuthorize(Permissions = "user_groups")]
public class HomeController : Controller
{
public ActionResult Index()
{
IFacebookApplication settings = FacebookApplication.Current;
if (settings != null)
{
//CanvasPage = settings.CanvasPage;
//AppId = settings.AppId;
}
FacebookWebContext facebookContext = FacebookWebContext.Current;
FacebookSignedRequest signedRequest = facebookContext.SignedRequest;
var client = new FacebookWebClient(facebookContext.AccessToken);
dynamic me = client.Get("me");
var friends = client.Get("me/friends");
var groups = client.Get("me/groups");
ViewBag.Name = me.name;
ViewBag.Id = me.id;
JavaScriptSerializer sr = new JavaScriptSerializer();
var fbFriends = sr.Deserialize<FBFriends>(friends.ToString());
ViewData["friends"] = fbFriends.data;
return View("Friends");
}
}
Any help, tips or code samples greatly appreciated.
make sure u have set the appid and appsecret correctly.
download the source code and checkout the "samples" folder, there are a bunch of asp.net mvc samples.

SPNavigationNode.IsVisible property not working in sharepoint as expected

I want to hide a quick launch node in a sharepoint site, but it's not working as expected. :(
My code is as under:
using (SPSite spSiteTest = new SPSite(serverUrl))
{
using (SPWeb mySite = spSiteTest.OpenWeb())
{
SPNavigationNodeCollection quickLaunchNodes = mySite.Navigation.QuickLaunch;
SPNavigationNode navQuickNode = new SPNavigationNode("Title", "www.stackoverflow.com", true);
foreach (SPNavigationNode node in quickLaunchNodes)
{
if (node.Title == navQuickNode.Title)
{
node.Url = navQuickNode.Url;
node.IsVisible = isVisible;
node.Update();
//mySite.Update();
return;
}
}
quickLaunchNodes.AddAsFirst(navQuickNode);
}
}
Am I missing something or is it a bug?
You can delete the nodes like this:
node.Delete();
mySite.Update();
or check the ExcludeFromNavigation method mentioned in this post (its author suggests that not being able to hide a navigation node by setting IsVisible to false is a SharePoint bug, too).
How to show/hide navigation nodes using SharePoint Server Side Object Model (SSOM)
PortalNavigation Class exposes the following methods for that purpose:
PortalNavigation.ExcludeFromNavigation - explicitly excludes a
specific subsite or page from being displayed in navigation
PortalNavigation.IncludeInNavigation - Explicitly includes a
specific subsite or page for display in navigation
Examples
How to hide site from Current navigation:
using (var site = new SPSite(siteUrl))
{
using (var web = site.OpenWeb())
{
var subWeb = web.Webs["Announcements"];
var publishingWeb = PublishingWeb.GetPublishingWeb(web);
publishingWeb.Navigation.ExcludeFromNavigation(false,subWeb.ID);
publishingWeb.Update();
}
}
How to show/hide navigation nodes using SharePoint Client Side Object Model (CSOM)
It is not supported to modify navigation settings via SharePoint CSOM.
Regarding SharePoint 2013:
In SharePoint 2013 was introduced a new
Microsoft.SharePoint.Client.Publishing and
Microsoft.SharePoint.Client.Publishing.Navigation namespaces in CSOM
API. But unfortunately it is not supported to modify navigation
settings using WebNavigationSettings class since properties are
exposes as a read-only.
For CSOM, you could utilize the ClientPortalNavigation.cs class that represents a CSOM counterpart for SSOM PortalNavigation Class. Follow Access and Manipulate Navigation Settings via SharePoint Client Object Model post for more details.
Examples
How to hide a page from Global navigation:
using (var ctx = new ClientContext(webUri))
{
//Get page file
var pageFile = ctx.Web.GetFileByServerRelativeUrl("/news/Pages/Announcements.aspx");
ctx.Load(pageFile);
ctx.ExecuteQuery();
//Hide page from Global navigation
var navigation = new ClientPortalNavigation(ctx.Web);
navigation.ExcludeFromNavigation(true, pageFile.UniqueId);
navigation.SaveChanges();
}
How to hide a site from Global navigation:
using (var ctx = new ClientContext(webUri))
{
//Get sub site
var result = ctx.LoadQuery(ctx.Web.Webs.Where(w => w.Title == "Archive"));
ctx.ExecuteQuery();
var subWeb = result.FirstOrDefault();
//Hide web from Global navigation
var navigation = new ClientPortalNavigation(ctx.Web);
navigation.ExcludeFromNavigation(true, subWeb.Id);
navigation.SaveChanges();
}

Categories