SharePoint 2013 - Add-ins
1. App Web & Host Web
The special website to which the app is deployed is called an App Web.
The website to which the app is installed is called the Host Web.
例子:
ClientContext context = new ClientContext(url); System.Security.SecureString passWord = new System.Security.SecureString(); foreach (char c in password.ToCharArray()) { passWord.AppendChar(c); } context.Credentials = new SharePointOnlineCredentials(userName, passWord);
3. App中的Client Web Part是否加入到Feature或者package中不影响App中的内容,App文件会将项目中的所有信息加入进去;
4. App的链接中已默认包含了这个参数:SPHostUrl, SPLanguage, SPClientTag, SPProductNumber, 和SPAppWebUrl。
5. 动态创建Tile的方法:
在页面上定义一个container:
<!-- Tiles will be rendered here --> <div id="tileArea" style="width:600px;"></div>
定义CSS:
/* Custom styles for the tiles */ .tile{ width:150px; height:150px; color:#FFFFFF; margin-top:5px; margin-left:0px; margin-right:10px; margin-bottom:10px; cursor:pointer; padding:5px; float:left; text-align:right; font-family:Segoe UI, sans-serif; font-size:14px; background-image:url(../images/MetroPlay.png); background-repeat: no-repeat; background-position:bottom right; background-color:#FFAAAA; } .tileNumber{ text-align:center; font-family:Segoe UI Light, sans-serif; font-size:72px; margin-top:10px; margin-bottom:10px; } a, a:hover, a:visited {text-decoration:none; outline:none;color:#FFFFFF} a:active, a:focus {outline:0}
使用JavaScript,根据列表数目动态生成tile:
// Variables used to hold objects for use in callback functions var context; var lists; //var list; var listItems; var tileArea; // This code runs when the DOM is ready and creates a context object which is needed // to use the SharePoint object model $(document).ready(function () { context = SP.ClientContext.get_current(); var web = context.get_web(); lists = web.get_lists(); context.load(lists); context.executeQueryAsync(Function.createDelegate(this, renderListTiles), Function.createDelegate(this, errorLoadingLists)); }); function errorLoadingLists(sender, args) { tileArea = document.getElementById("tileArea"); // Remove all nodes from the chart <DIV> so we have a clean space to write to while (tileArea.hasChildNodes()) { tileArea.removeChild(tileArea.lastChild); } // Write a message to let the user know the operation has failed var errMessage = document.createElement("div"); errMessage.appendChild(document.createTextNode("Lists could not be retrieved."+args.get_message())); } function renderListTiles(sender, args) { var listEnumerator = lists.getEnumerator(); while (listEnumerator.moveNext()) { var list = listEnumerator.get_current(); var listTitle = list.get_title(); if ((listTitle == "Employees") || (listTitle == "MarketSize") || (listTitle == "Sales")) { var itemCount = list.get_itemCount(); var tile = document.createElement("a"); tile.setAttribute("class", "tile"); tile.setAttribute("href", "../Lists/" + listTitle); tile.appendChild(document.createTextNode(listTitle)); $("#tileArea").appendChild(tile); var tileBody = document.createElement("div"); tileBody.setAttribute("class", "tileNumber"); tileBody.appendChild(document.createTextNode(itemCount.toString())); tile.appendChild(tileBody); } } }
效果:
6. 向SharePoint Log中添加记录时,需要用到SP.Analytics命名空间(SP.js);
var eventGuid = new SP.Guid("101c16a5-3abd-4020-921f-cc40090c6ff7"); // When you have entered a valid GUID, you can then call the logAnalyticsAppEvent // as follows: SP.Analytics.AnalyticsUsageEntry.logAnalyticsAppEvent(context, eventGuid, "Test App Page");
context.executeQueryAsync( // This is the success callback: function () { status.innerText = "Success! The event for 'Test App Page' has been logged."; }, // This is the failure callback: function (sender, e) { status.innerText = "Failed to log event for 'Test App Page': " + e.get_message(); });
7. sp.userprofiles.js中包含了获取user profle信息的方法和对象(但除了accountName属性,其它属性可能为null);
// This code runs when the DOM is ready and creates a context object // which is needed to use the SharePoint object model. We also wire // up the click event handler of the listProfiles button in default.aspx. $(document).ready(function () { context = SP.ClientContext.get_current(); $('#listProfiles').click(function () { listProfilesClick();}); }); // This function handles the click event of the listProfiles button in default.aspx function listProfilesClick() { // Our way into the current user's profile is through the PeopleManager class // so we'll instantiate a new object and pass in the current context, peopleMgr = new SP.UserProfiles.PeopleManager(context); // We'll then load the object... context.load(peopleMgr); // ... and we'll get the user profile properties and load them as well. profileProperties = peopleMgr.getMyProperties(); context.load(profileProperties); // Next we ask SharePoint to run all of our previously batched commands... context.executeQueryAsync( // ... and if we're successful, the following success callback will run function () { if (peopleMgr.get_isMyPeopleListPublic()) { publicDiv.appendChild(document.createTextNode("Your followers and those you follow are publicly visible")); } else { publicDiv.appendChild(document.createTextNode("Your followers and those you follow are not publicly visible")); } $('#profileList').append(publicDiv); // Then we'll add a few simple properties from the profile. // The first one is the account name var accountName = profileProperties.get_accountName(); // The second one is the display name. // Note that we'll check for null in case this property hasn't been set. var displayName = profileProperties.get_displayName(); // The third thing to show the user is their profile picture. // Note that we'll check for null in case this property hasn't been set. // If it's not null, we'll render the actual picture. var myPicture = profileProperties.get_pictureUrl(); // Finally, we'll add three links: // One for editing the profile... var myProfileLink = peopleMgr.get_editProfileLink(); // One for going to the user's personal site... var myPersonalSite = profileProperties.get_personalUrl(); // And one that shows the user what their page looks like when // viewed by other users. var myPublicPersona = profileProperties.get_userUrl(); }, // If we haven't been successful, the following failure callback // will run, so we'll clear the profile list div, and then use it // to tell the user what went wrong function (sender, e) { $('#profileList').children().remove(); $('#profileList').append(docume.createTextNode(e.get_message())); }); }
8. SP.RequestExecutor.js文件就是cross-domain library,SP.RequestExecutor对象用于执行与其它数据源交互的操作,比如host-web,app-web;但需要注意的是,如果在APP中使用SP.RequestExecutor对象与Host Web的数据进行交互,由于在App中,默认的context site是app web, 所以我们还需要使用SP.AppContextSite()来设置当前的context site为 host web,然后还要再AppManifest.xml中设置app在host web中的权限(在使用SP.AppContextSite时必须使用SP.AppContextSite(@target) 和?@target='<host web url>'的格式,不能直接将host web地址写在参数括号中);
var hostweburl; var appWebUrl; // Load the required SharePoint libraries $(document).ready(function () { //Get the URI decoded URLs. hostweburl = decodeURIComponent( getQueryStringParameter("SPHostUrl") ); appWebUrl = decodeURIComponent(getQueryStringParameter("SPAppWebUrl")); // Load the js file and continue to the // success event handler $.getScript(hostweburl+"/_layouts/15/SP.RequestExecutor.js", execCrossDomainRequest); }); // Function to prepare and issue the request to get // SharePoint data function execCrossDomainRequest() { var executor; // Initialize the RequestExecutor with the app web URL. executor = new SP.RequestExecutor("/"); // Issue the call against the host web. // To get the title using REST we can hit the endpoint: // app_web_url/_api/SP.AppContextSite(@target)/web/title?@target='siteUrl' // The response formats the data in the JSON format. // The functions successHandler and errorHandler attend the // success and error events respectively. executor.executeAsync( { url: appWebUrl+"/_api/SP.AppContextSite(@target)/web/title?@target='" + hostweburl + "'", method: "GET", headers: { "Accept": "application/json; odata=verbose" }, success: successHandler, error: errorHandler } ); } // Function to handle the success event. // Prints the host web's title to the page. function successHandler(data) { var jsonObject = JSON.parse(data.body); document.getElementById("HostwebTitle").innerHTML = "<b>" + jsonObject.d.Title + "</b>"; } // Function to handle the error event. // Prints the error message to the page. function errorHandler(data, errorCode, errorMessage) { document.getElementById("HostwebTitle").innerText = "Could not complete cross-domain call: " + errorMessage; }
9. 在app中搜索host web内容时,需要在AppManifest.xml文件的Permissions中为app添加Search权限,搜索时使用的url路径就是app web的路径,但依然可以搜索到host web的内容;
function executeQuery() { alert(_spPageContextInfo.webAbsoluteUrl); $.ajax( { url: _spPageContextInfo.webAbsoluteUrl + "/_api/search/query?querytext='" + $("#queryTerms").val() + "'", method: "GET", headers: { "Accept": "application/json;odata=verbose" }, success: onSuccess, error: onError } ); } function onSuccess(data) { var results = data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results; alert(JSON.stringify(data.d.query)); //display json as string var html = "<table>"; for (var i = 0; i < results.length; i++) { html += "<tr><td>"; html += results[i].Cells.results[3].Value; html += "</td><td>" html += results[i].Cells.results[6].Value; html += "</td><tr>"; } html += "</table>"; $("#resultsDiv").append($(html)); } function onError(err) { alert(JSON.stringify(err)); }
10. _spPageContextInfo.webAbsoluteUrl可以直接在app中获取到当前站点的web绝对路径;
11.