Поделиться через


Создание надстройки Project Server с размещением в SharePoint

Из трех типов приложений, которые можно создать для Project Online (с автоматическим размещением, с размещением у поставщика и с размещением в SharePoint), приложение, размещенное в SharePoint, является самым простым в создании и развертывании. Приложение, размещенное в SharePoint, не требует проверки подлинности OAuth, не использует Azure или не требует обслуживания локального сайта для ресурсов, размещенных у поставщика. Шаблон App for SharePoint 2013 в Visual Studio — это удобная платформа для разработки приложений, которые можно публиковать и продавать в Магазине Office или развертывать в частном каталоге приложений в SharePoint.

В Project состояние — это процесс, в котором участник команды может использовать страницу Задачи в Project Web App для отправки состояния назначенной задачи, например количества часов, отработанного в день недели, затраченного на выполнение задачи. Владелец назначения (обычно руководитель проекта) может утвердить или отклонить состояние. После утверждения состояния Project изменяет расписание. Приложение QuickStatus отображает назначенные задачи, при этом пользователь может быстро обновить процент завершения и отправить отчет о состоянии выбранных назначений для утверждения. Хотя страница "Задачи" в Project Web App имеет гораздо больше возможностей, приложение QuickStatus является примером, который предоставляет упрощенный интерфейс.

Приложение QuickStatus — это пример для разработчиков, оно не предназначено для использования в рабочей среде. Основная цель — показать пример разработки приложений для Project Online, а не создавать полностью функциональное приложение с состоянием. Более эффективный способ определения состояния см. рекомендациях в разделе Дальнейшие действия.

Общие сведения о состоянии см. в разделе Ход выполнения задачи. Дополнительные сведения о разработке надстроек для SharePoint и Project Server см. в разделе Надстройки SharePoint.

Необходимые компоненты для создания приложения для Project Server 2013

Для разработки относительно простых приложений, которые можно развернуть в Project Online или в локальной установке Project Server 2013, можно использовать Napa, которая предоставляет среду разработки в сети. Для более сложных приложений, изменения ленты Project Web App и упрощения отладки во время разработки можно использовать Visual Studio 2012 или Visual Studio 2013. Например, в локальной установке можно вручную проверить черновики таблиц данных на наличие изменений в базе данных Project Server. В этой статье показано, как разрабатывать приложения с помощью Visual Studio.

Для разработки приложений Project Server с использованием Visual Studio требуются следующие компоненты:

  • Проверьте, что на локальном компьютере разработчика установлены самые последние пакеты обновления и обновления Windows. Операционной системой может быть Windows 7, Windows 8, Windows Server 2008 или Windows Server 2012.

  • У вас должен быть компьютер, на котором установлены SharePoint Server 2013 и Project Server 2013, где компьютер настроен для изоляции приложений и загрузки неопубликованных приложений. Загрузка неопубликованных приложений позволяет Visual Studio временно установить приложение для отладки. Вы можете использовать локальную установку SharePoint и Project Server. Дополнительные сведения см. в статье Настройка локальной среды разработки для приложений для SharePoint.

    Примечание.

    Для локальной установки настройте изолированный домен приложений перед созданием корпоративного каталога приложений.

  • Компьютером разработки может быть удаленный компьютер, на котором установлены инструменты разработчика Office для Visual Studio 2012. Убедитесь, что установлена последняя версия; См. раздел Сервис для скачивания Приложений для Office и SharePoint.

  • Убедитесь, что экземпляр Project Web App, который вы будете использовать для разработки и тестирования, доступен в браузере.

Сведения об использовании онлайн-инструментов см. в статье Настройка среды разработки для надстроек SharePoint в Microsoft 365. Пошаговое руководство по созданию простого приложения для Project Server, использующего онлайн-инструменты, см. в серии блогов EPMSource Создание первого приложения Project Server.

Создание приложения для Project Server с помощью Visual Studio

Средства разработчика Office для Visual Studio 2012 содержат шаблон для приложений SharePoint, которые можно использовать с Project Server 2013. При создании решения оно содержит следующие файлы для пользовательского кода:

  • AppManifest.xml с параметрами для названия приложения, области запроса разрешений и другими свойствами. Процедура 1 содержит действия для установки свойств с помощью конструктора манифеста.

  • Default.aspx в папке "Страницы" — это главная страница приложения. В процедуре 2 показано добавление HTML5-контента для приложения QuickStatus.

  • App.js в папке Scripts является основным файлом для пользовательского кода JavaScript. Процедура 3 объясняет код JavaScript для приложения QuickStatus .

    При добавлении коммерческих элементов управления, таких как сетка на основе jQuery или средство выбора даты, вы можете добавить ссылки на дополнительные файлы JavaScript в файл Default.aspx.

  • App.css в папке "Контент" — это основной файл для настраиваемых стилей CSS3. Процедура 2 и 3 процедуры содержат сведения о каскадных таблицах стилей (CSS) для приложения QuickStatus. В файл Default.aspx можно добавить ссылки на дополнительные CSS-файлы.

  • AppIcon.png в папке "Изображения" отображается значок 96 x 96, отображаемый приложением в Магазине Office или каталоге приложений.

Чтобы изменить ленту Project Web App, можно добавить настраиваемое действие ленты. В разделе Пример кода приложения QuickStatus представлен полный код измененных файлов Default.aspx, App.js, App.css, Elements.xml и AppManifest.xml.

Процедура 1. Создание проекта приложения в Visual Studio

  1. Запустите Visual Studio 2012 от имени администратора и выберите Создать проект на начальной странице.

  2. В диалоговом окне Новый проект разверните узлы Шаблоны, Visual C# и Office/SharePoint , а затем выберите Приложения. Используйте платформа .NET Framework по умолчанию 4.5 в раскрывающемся списке целевой платформы в верхней части центральной области, а затем выберите Приложение для SharePoint 2013 (см. рис. 1).

  3. В поле Имя введите QuickStatus, перейдите в расположение, в котором вы хотите сохранить приложение, а затем нажмите кнопку ОК.

    Рис. 1. Создание приложения Project Server в Visual Studio

    Создание приложения Project Server в Visual Studio

  4. В диалоговом окне Создание приложения для SharePoint заполните следующие три поля:

    • В верхнем текстовом поле введите имя, которое приложение будет отображать в Project Web App. Например, введите Быстрое обновление состояния.

    • Чтобы сайт использовался для отладки, введите URL-адрес экземпляра Project Web App. Например, введите https://ServerName/ProjectServerName (заменив ServerName и ProjectServerName собственными значениями), а затем нажмите кнопку Проверить. Если все пройдет успешно, Visual Studio отображает сообщение Подключение выполнено успешно. Если появляется сообщение об ошибке, убедитесь, что URL-адрес Project Web App правильный и что компьютер Project Server настроен для изоляции приложений и загрузки неопубликованных приложений. Дополнительные сведения см. в разделе Предварительные требования для создания приложения для Project Server 2013 .

    • В раскрывающемся списке Как разместить приложение для SharePoint? выберите Размещение в SharePoint.

    Предостережение

    Если вы по ошибке выбрали значение по умолчанию, С размещением у поставщика, Visual Studio создает два проекта в решении: QuickStatus и QuickStatusWeb. Если вы видите два проекта, удалите решение и начните заново.

  5. Нажмите кнопку ОК для создания решения QuickStatus, проекта QuickStatus и файлов по умолчанию.

  6. Откройте представление Designer манифеста (например, дважды щелкните файл AppManifest.xml). На вкладке Общие в текстовом поле Название должно отображаться имя приложения, введенное на шаге 4. Перейдите на вкладку Разрешения , чтобы добавить следующие запросы разрешений для приложения (см. рис. 2):

    • В первой строке списка Запросы разрешений в столбце Область выберите Определение состояния в раскрывающемся списке. В столбце Разрешения выберите SubmitStatus.

    • Добавьте строку, где Область — это Несколько проектов, а РазрешениеЧтение.

    Рис. 2. Установка области разрешений для приложения определения состояния

    Настройка область разрешений для приложения с состоянием

Приложение QuickStatus позволяет пользователю Project Web App считывать назначения для этого пользователя из нескольких проектов, изменять процент завершения назначения и отправлять обновление. Другие области запроса разрешений, показанные в раскрывающемся списке на рис. 2, не требуются для этого приложения. Области запроса разрешений — это разрешения, которые приложение запрашивает от имени пользователя. Если у пользователя нет этих разрешений в Project Web App, приложение не запускается. Приложение может иметь несколько областей запроса разрешений, в том числе для других разрешений SharePoint, но должно иметь только минимальный необходимый для функциональных возможностей приложения. Ниже приведены области запроса разрешений, связанные с Project Server.

  • Корпоративные ресурсы: разрешения Resource Manager для чтения или записи сведений о других Project Web App пользователей.

  • Несколько проектов: чтение или запись в несколько проектов, для которых у пользователя есть запрашиваемые разрешения.

  • Project Server: требуется, чтобы у пользователя приложения были разрешения администратора для Project Web App.

  • Отчеты. Чтение службы OData ProjectData для Project Web App (требуется только разрешение на вход для Project Web App).

  • Один проект: чтение или запись в проект, для которого у пользователя есть запрашиваемые разрешения.

  • Определение состояния: отправка обновлений состояния назначений, таких как время работы, процент завершения и новых назначений.

  • Рабочий процесс: если у пользователя есть разрешения для запуска рабочих процессов Project Server, приложение выполняется с повышенными привилегиями для рабочего процесса.

Дополнительные сведения о областях запросов разрешений для Project Server 2013 см. в разделе Приложения Projectв Обновления для разработчиков в Project 2013 и Разрешения приложений в SharePoint 2013.

Создание HTML-контента для приложения QuickStatus

Прежде чем приступить к написанию html-содержимого, спроектируйте пользовательский интерфейс и интерфейс для приложения QuickStatus (на рисунке 3 показан пример завершенной страницы). Структура также может включать структуру функций JavaScript, взаимодействующих с HTML-кодом. Общие сведения см. в статье Проектирование пользовательского интерфейса для приложений в SharePoint 2013.

Рис. 3. Проект страницы приложения QuickStatus

Проектирование страницы приложения QuickStatus

Приложение отображает имя в верхней части, а именно значение из элемента Title файла AppManifest.xml.

По умолчанию страница использует HTML5. Ниже приведены стандартные HTML-элементы для основных объектов пользовательского интерфейса, которые содержатся в тексте страницы приложения QuickStatus:

  • Элемент form содержит все другие элементы пользовательского интерфейса.

  • Элемент fieldset создает контейнер и границу таблицы назначений, дочерний элемент legend предоставляет метку для контейнера.

  • Элемент table содержит название и заголовок таблицы. Функции JavaScript изменяют подпись таблицы и добавляют строки для назначений.

    Примечание.

    Чтобы легко добавить разбиение по страницам и сортировки, рабочее приложение, скорее всего, будет использовать коммерческий элемент управления сетки на основе jQuery вместо таблицы.

    Таблица содержит столбцы имени проекта, имени задачи с флажком, фактических трудозатрат, процента завершения, оставшихся трудозатрат и даты окончания назначения. Функции JavaScript создают поле проверка и поле текстового ввода для процента выполнения каждой задачи.

  • Элемент input для текстового поля задает процента завершения всех выбранных назначений.

  • Элемент button отправляет изменения состояния.

  • Элемент button обновляет страницу.

  • Элемент button выходит из приложения и возвращается на страницу Задачи в Project Web App.

Нижние текстовые поля и кнопки находятся в элементах div, чтобы таблица CSS могла легко управлять положением и внешним видом объектов пользовательского интерфейса. Функция JavaScript добавляет абзац в нижней части страницы, содержащий результаты успешного или неудачного обновления состояния.

Процедура 2. Создание HTML-контента

  1. В Visual Studio откройте файл Default.aspx.

    Файл содержит два элемента asp:Content : элемент с атрибутом ContentPlaceHolderID="PlaceHolderAdditionalPageHead" добавляется в заголовок страницы, а элемент с атрибутом ContentPlaceHolderID="PlaceHolderMain" помещается в элемент текста страницы.

  2. <asp:Content ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server"> В элементе управления для заголовка страницы добавьте ссылку на файл PS.js на компьютере Project Server. Для тестирования и отладки можно использовать PS.debug.js.

      <script type="text/javascript" src="/_layouts/15/ps.debug.js"></script>
    

    Инфраструктура приложений использует виртуальный /_layouts/15/ каталог для сайта SharePoint в IIS. Физический файл — %ProgramFiles%\Common Files\Microsoft Shared\Web Server Extensions\15\TEMPLATE\LAYOUTS\PS.debug.js.

    Примечание.

    Перед развертыванием приложения для использования в рабочей среде удалите .debug из скрипта ссылки, чтобы повысить производительность.

  3. В элементе <asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server"> управления для текста страницы удалите созданный элемент div , а затем добавьте HTML-код для объектов пользовательского интерфейса. Элемент table содержит только строку заголовка. Столбец Имя задачи содержит элемент управления флажком. Текст для элемента caption заменяется обратным вызовом onGetUserNameSuccess для функции getUserInfo в файле App.js.

    <form>
        <fieldset>
        <legend>Select assigned tasks</legend>
        <table id="assignmentsTable">
            <caption id="tableCaption">Replace caption</caption>
            <thead>
            <tr id="headerRow">
                <th>Project name</th>
                <th><input type="checkbox" id="headercheckbox" checked="checked" />Task name</th>
                <th>Actual work</th>
                <th>% complete</th>
                <th>Remaining work</th>
                <th>Due date</th>
            </tr>
            </thead>
        </table>
        </fieldset>
        <div id="inputPercentComplete" >
        Set percent complete for all selected assignments, or leave this
        <br /> field blank and set percent complete for individual assignments: 
        <input type="text" name="percentComplete" id="pctComplete" size="4"  maxlength="4" />
        </div>
        <div id="submitResult">
        <p><button id="btnSubmitUpdate" type="button" class="bottomButtons" ></button></p>
        <p id="message"></p>
        </div>
        <div id="refreshPage">
        <p><button id="btnRefresh" type="button" class="bottomButtons" >Refresh</button></p>
        </div>
        <div id="exitPage">
        <p><button id="btnExit" type="button" class="bottomButtons" >Exit</button></p>
        </div>
    </form>
    
  4. В файле App.css добавьте код CSS, определяющий положение и внешний вид элементов пользовательского интерфейса. Полный код CSS приложения QuickStatus см. в разделе Пример кода для приложения QuickStatus.

Процедура 3 добавляет функции JavaScript для чтения назначений и создания строк таблицы, а также для изменения и обновления процента завершения назначения. Фактические шаги являются более итеративными при разработке приложения, где вы поочередно создаете некоторые html-коды, добавляете и тестируете связанные стили и функции JavaScript, изменяете или добавляете дополнительный HTML-код, а затем повторяете процесс.

Создание функций JavaScript для приложения QuickStatus

Шаблон Visual Studio для приложения SharePoint включает в себя файл App.js, который содержит код инициализации по умолчанию, получающий контекст клиента SharePoint и демонстрирующий базовые операции получения и установки для страницы приложения. Пространство имен JavaScript для клиентской библиотеки SP.js SharePoint — это sp. Так как приложение для Project Server использует библиотеку PS.js, приложение применяет пространство имен PS для получения контекста клиента и доступа к JSOM для Project Server.

Функции JavaScript в приложении QuickStatus включают следующие:

  • Обработчик событий документа ready выполняется, когда создается экземпляр объектной модели документов (DOM). Обработчик события ready выполняет следующие четыре действия.

    1. Инициализирует глобальную переменную projContext с клиентским контекстом для Project Server JSOM и глобальную переменную pwaWeb.

    2. Вызывает функцию getUserInfo для инициализации глобальной переменной projUser.

    3. Вызывает функцию getAssignments, которая возвращает указанные данные назначения пользователю.

    4. Привязывает обработчики событий щелчка к флажку заголовка таблицы, а также к флажкам в каждой строке таблицы. Обработчики событий щелчка управляют атрибутом checked флажков, когда пользователь устанавливает или снимает флажки в таблице.

  • Если функция getAssignments выполнена успешно, вызывается функция onGetAssignmentsSuccess. Эта функция вставляет строку в таблицу для каждого назначения, инициализирует элементы управления HTML в каждой строке, а затем инициализирует свойства нижней кнопки.

  • Обработчик событий onClick для кнопки Обновить вызывает функцию updateAssignments . Эта функция получает значение процента завершения, применяемое к каждому выбранному назначению; Или если текстовое поле процент завершения пуст, функция получает процент завершения каждого выбранного назначения в таблице. Затем функция updateAssignments сохраняет и отправляет обновления состояния и записывает сообщение о результатах в нижнюю часть страницы.

Процедура 3. Создание функций JavaScript

  1. В Visual Studio откройте файл App.js и удалите все его содержимое.

  2. Добавьте глобальные переменные и обработчик событий ready. Доступ к объекту document осуществляется с помощью функции jQuery.

    Обработчик события щелчка для флажка заголовка таблицы задает состояние флажков строк. Если все флажки строки установлены или сняты, обработчик события щелчка задает установленное состояние флажка заголовка. Обработчики событий щелчка также задает результирующее сообщение в нижней части страницы как пустую строку.

     var projContext;
     var pwaWeb;
     var projUser;
     // This code runs when the DOM is ready and creates a ProjectContext object.
     // The ProjectContext object is required to use the JSOM for Project Server.
     $(document).ready(function () {
         projContext = PS.ProjectContext.get_current();
         pwaWeb = projContext.get_web();
         getUserInfo();
         getAssignments();
         // Bind a click event handler to the table header check box, which sets the row check boxes
         // to the checked state of the header check box, and sets the results message to an empty string.
         $('#headercheckbox').live('click', function (event) {
             $('input:checkbox:not(#headercheckbox)').attr('checked', this.checked);
             $get("message").innerText = "";
         });
         // Bind a click event handler to the row check boxes. If any row check box is cleared, clear
         // the header check box. If all of the row check boxes are selected, select the header check box.
         $('input:checkbox:not(#headercheckbox)').live('click', function (event) {
             var isChecked = true;
             $('input:checkbox:not(#headercheckbox)').each(function () {
                 if (this.checked == false) isChecked = false;
                 $get("message").innerText = "";
             });
             $("#headercheckbox").attr('checked', isChecked);
         });
     });
    
  3. Добавьте функцию getUserInfo, которая вызывает onGetUserNameSuccess при успешном выполнении запроса. Функция onGetUserNameSuccess заменяет содержимое абзацаcaption на название таблицы, которое содержит имя пользователя.

         // Get information about the current user.
         function getUserInfo() {
             projUser = pwaWeb.get_currentUser();
             projContext.load(projUser);
             projContext.executeQueryAsync(onGetUserNameSuccess,
                 // Anonymous function to execute if getUserInfo fails.
                 function (sender, args) {
                     alert('Failed to get user name. Error: ' + args.get_message());
             });
         } 
         // This function is executed if the getUserInfo call is successful.
         function onGetUserNameSuccess() {
             var prefaceInfo = 'Assignments for ' + projUser.get_title();
             $('#tableCaption').text(prefaceInfo);
         }
    
  4. Добавьте функцию getAssignments , которая вызывает onGetAssignmentsSuccess (см. шаг 5) в случае успешного выполнения запроса назначения. Параметр Включить ограничивает запрос возвращать только указанные поля.

     // Get the collection of assignments for the current user.
     function getAssignments() {
         assignments = PS.EnterpriseResource.getSelf(projContext).get_assignments();
         // Register the request that you want to run on the server. The optional "Include" parameter 
         // requests only the specified properties for each assignment in the collection.
         projContext.load(assignments,
             'Include(Project, Name, ActualWork, ActualWorkMilliseconds, PercentComplete, RemainingWork, Finish, Task)');
         // Run the request on the server.
         projContext.executeQueryAsync(onGetAssignmentsSuccess,
             // Anonymous function to execute if getAssignments fails.
             function (sender, args) {
                 alert('Failed to get assignments. Error: ' + args.get_message());
             });
     }
    
  5. Добавьте функцию onGetAssignmentsSuccess, которая добавляет строку для каждого назначения в таблицу. Переменная prevProjName используется для определения того, предназначена ли строка для другого проекта. В этом случае имя проекта отображается полужирным шрифтом, в противном случае имя проекта — это пустая строка.

    Примечание.

    JSOM не включает свойства TimeSpan , которые включает CSOM, например ActualWorkTimeSpan. Вместо них JSOM использует свойства, определяющие время в миллисекундах, например PS.StatusAssignment.actualWorkMilliseconds. Для получения свойства используется метод get_actualWorkMilliseconds, который возвращает целое число. > Метод get_actualWork возвращает строку, например "3h". В приложении QuickStatus можно использовать оба значения, но отображать их по-разному. Запрос назначений содержит оба свойства, благодаря чему можно проверить значение во время отладки. Если удалить переменную actualWork, можно также удалить свойство ActualWork из запроса назначения.

    Наконец, функция onGetAssignmentsSuccess инициализирует кнопки Update и Refresh с обработчиками события щелчка. Текстовое значение кнопки Update кнопки можно задать в HTML-коде.

         // Get the enumerator, iterate through the assignment collection, 
         // and add each assignment to the table.
         function onGetAssignmentsSuccess(sender, args) {
             if (assignments.get_count() > 0) {
                 var assignmentsEnumerator = assignments.getEnumerator();
                 var projName = "";
                 var prevProjName = "3D2A8045-4920-4B31-B3E7-9D0C5195FC70"; // Any unique name.
                 var taskNum = 0;
                 var chkTask = "";
                 var txtPctComplete = "";
                 // Constants for creating input controls in the table.
                 var INPUTCHK = '<input type="checkbox" class="chkTask" checked="checked" id="chk';
                 var LBLCHK = '<label for="chk';
                 var INPUTTXT = '<input type="text" size="4"  maxlength="4" class="txtPctComplete" id="txt';
                 while (assignmentsEnumerator.moveNext()) {
                     var statusAssignment = assignmentsEnumerator.get_current();
                     projName = statusAssignment.get_project().get_name();
                     // Get an integer, such as 3600000.
                     var actualWorkMilliseconds = statusAssignment.get_actualWorkMilliseconds(); 
                     // Get a string, such as "1h". Not used here.
                     var actualWork = statusAssignment.get_actualWork();
                     if (projName === prevProjName) {
                         projName = "";
                     }
                     prevProjName = statusAssignment.get_project().get_name();
                     // Create a row for the assignment information.
                     var row = assignmentsTable.insertRow();
                     taskNum++;
                     // Create an HTML string with a check box and task name label, for example:
                     // <input type="checkbox" class="chkTask" checked="checked" id="chk1" /> <label for="chk1">Task 1</label>
                     chkTask = INPUTCHK + taskNum + '" /> ' + LBLCHK + taskNum + '">' 
                         + statusAssignment.get_name() + '</label>';
                     txtPctComplete = INPUTTXT + taskNum + '" />';
                     // Insert cells for the assignment properties.
                     row.insertCell().innerHTML = '<strong>' + projName + '</strong>';
                     row.insertCell().innerHTML = chkTask;
                     row.insertCell().innerText = actualWorkMilliseconds / 3600000 + 'h';
                     row.insertCell().innerHTML = txtPctComplete;
                     row.insertCell().innerText = statusAssignment.get_remainingWork();
                     row.insertCell().innerText = statusAssignment.get_finish();
                     // Initialize the percent complete cell.
                     $get("txt" + taskNum).innerText = statusAssignment.get_percentComplete() + '%'
                 }
             }
             else {
                 $('p#message').attr('style', 'color: #0f3fdb');     // Blue text.
                 $get("message").innerText = projUser.get_title() + ' has no assignments'
             }
             // Initialize the button properties.
             $get("btnSubmitUpdate").onclick = function() { updateAssignments(); };
             $get("btnSubmitUpdate").innerText = 'Update';
             $get('btnRefresh').onclick = function () { window.location.reload(true); };
             $get('btnExit').onclick = function () { exitToPwa(); };
         }
    
  6. Добавьте обработчик события щелчка updateAssignments для кнопки Update. Если пользователь изменяет процент завершения задачи или добавляет значение в текстовом поле percentComplete, значение можно ввести в различных форматах, например как "60", "60%" или "60 %". Метод getNumericValue возвращает введенный текст как числовое значение.

    Примечание.

    В приложении для рабочей среды следует реализовать проверку вводимого текста и дополнительную проверку на наличие ошибок.

    Пример updateAssignments применяет базовую проверку ошибок и отображает информацию в абзаце message в нижней части страницы — зеленым цветом, если запрос на обновление выполнен успешно, и красным цветом, если при вводе или выполнении запроса на обновление возникла ошибка.

    Перед использованием метода submitAllStatusUpdates приложение должно сохранить обновления на сервере с помощью метода PS.StatusAssignmentCollection.update.

         // Update all checked assignments. If the bottom percent complete field is blank,
         // use the value in the % complete field of each selected row in the table.
         function updateAssignments() {
             // Get percent complete from the bottom text box.
             var pctCompleteMain = getNumericValue($('#pctComplete').val()).trim();
             var pctComplete = pctCompleteMain;
             var assignmentsEnumerator = assignments.getEnumerator();
             var taskNum = 0;
             var taskRow = "";
             var indexPercent = "";
             var doSubmit = true;
             while (assignmentsEnumerator.moveNext()) {
                 var pctCompleteRow = "";
                 taskRow = "chk" + ++taskNum;
                 if ($get(taskRow).checked) {
                     var statusAssignment = assignmentsEnumerator.get_current();
                     if (pctCompleteMain === "") {
                         // Get percent complete from the text box field in the table row.
                         pctCompleteRow = getNumericValue($('#txt' + taskNum).val());
                         pctComplete = pctCompleteRow;
                     }
                     // If both percent complete fields are empty, show an error.
                     if (pctCompleteMain === "" && pctCompleteRow === "") {
                         $('p#message').attr('style', 'color: #e11500');     // Red text.
                         $get("message").innerHTML =
                             '<b>Error:</b> Both <i>Percent complete</i> fields are empty, in row '
                             + taskNum
                             + ' and in the bottom textbox.<br/>One of those fields must have a valid percent.'
                             + '<p>Please refresh the page and try again.</p>';
                         doSubmit = false;
                         taskNum = 0;
                         break;
                     }
                     if (doSubmit) statusAssignment.set_percentComplete(pctComplete);
                 }
             } 
             // Save and submit the assignment updates.
             if (doSubmit) {
                 assignments.update();
                 assignments.submitAllStatusUpdates();
                 projContext.executeQueryAsync(function (source, args) {
                     $('p#message').attr('style', 'color: #0faa0d');     // Green text.
                     $get("message").innerText = 'Assignments have been updated.';
                 }, function (source, args) {
                     $('p#message').attr('style', 'color: #e11500');     // Red text.
                     $get("message").innerText = 'Error updating assignments: ' + args.get_message();
                 });
             }
         }
         // Get the numeric part for percent complete, from a string. For example, with "20 %", return "20".
         function getNumericValue(pctComplete) {
             pctComplete = pctComplete.trim();
             pctComplete = pctComplete.replace(/ /g, "");    // Remove interior spaces.
             indexPercent = pctComplete.indexOf('%', 0);
             if (indexPercent > -1) pctComplete = pctComplete.substring(0, indexPercent);
             return pctComplete;
         }
    
  7. Добавьте функцию exitToPwa, которая использует параметр строки запроса SPHostUrl для URL-адреса узла Project Web App сайта. Чтобы вернуться на страницу Задачи, добавьте "/Tasks.aspx" к URL-адресу. Например, переменная spHostUrl будет иметь значение https://ServerName/ProjectServerName/Tasks.aspx.

    Функция getQueryStringParameter разбивает URL-адрес страницы QuickStatus для извлечения и возврата указанного параметра в параметрах URL-адреса. Ниже приведен пример значения document.URL для документа QuickStatus (все в одной строке):

     https://app-ef98082fa37e3c.servername.officeapps.selfhost.corp.microsoft.com/pwa/
         QuickStatus/Pages/Default.aspx
         ?SPHostUrl=https%3A%2F%2Fsphvm%2D85178%2Fpwa
         &SPLanguage=en%2DUS
         &SPClientTag=1
         &SPProductNumber=15%2E0%2E4420%2E1022
         &SPAppWebUrl=https%3A%2F%2Fapp%2Def98082fa37e3c%2Eservername
             %2Eofficeapps%2Eselfhost%2Ecorp%2Emicrosoft%2Ecom%2Fpwa%2FQuickStatus
    

    Для предыдущего URL-адреса функция getQueryStringParameter возвращает строковое значение запроса SPHostUrl , https://ServerName/pwa.

         // Exit the QuickStatus page and go back to the Tasks page in Project Web App.
         function exitToPwa() {
             // Get the SharePoint host URL, which is the top page of PWA, and add the Tasks page.
             var spHostUrl = decodeURIComponent(getQueryStringParameter('SPHostUrl'))
                             + "/Tasks.aspx";
             // Set the top window for the QuickStatus IFrame to the Tasks page.
             window.top.location.href = spHostUrl;
         }
         // Get a specified query string parameter from the {StandardTokens} URL option string.
         function getQueryStringParameter(urlParameterKey) {
             var docUrl = document.URL;
             var params = docUrl.split('?')[1].split('&');
             for (var i = 0; i < params.length; i++) {
                 var theParam = params[i].split('=');
                 if (theParam[0] == urlParameterKey)
                     return decodeURIComponent(theParam[1]);
             }
         }
    

Если на этом этапе вы публикуете приложение QuickStatus и добавляете его в Project Web App, приложение можно запустить со страницы "Содержимое сайта", но оно не будет легко доступно для пользователей. Чтобы помочь пользователям, можно добавить кнопку приложения на ленту на странице "Задачи". В процедуре 4 показано, как добавить настраиваемое действие ленты.

Добавление настраиваемого действия ленты

Вкладки ленты, группы и элементы управления для Project Web App указываются в файле pwaribbon.xml, который устанавливается в [Program Files]\Common Files\Microsoft Shared\Web Server Extensions\15\TEMPLATE\FEATURES\PWARibbon\listtemplates каталоге на компьютере с Project Server. Чтобы упростить разработку пользовательских действий для ленты Project Web App, пакет SDK для Project 2013 содержит копию pwaribbon.xml.

Project Web App использует различные определения ленты для страницы Задачи в зависимости от того, использует ли экземпляр Project Web App режим единого входа, который позволяет пользователям вводить значения как для расписания, так и для состояния задачи. Если у вас есть административные разрешения для Project Web App, чтобы определить режим входа, выберите Параметры PWA в раскрывающемся меню параметры в правом верхнем углу страницы. На странице "Параметры PWA" выберите Параметры и значения по умолчанию для расписания и посмотрите на флажок Режим одной операции в нижней части страницы.

Если режим одной операции отключен, ленту на странице "Задачи" определяет область Моя работа в файле pwaribbon.xml:

   <!-- REGION My Work Ribbon-->
   <CustomAction
      Id="Ribbon.ContextualTabs.MyWork"
      . . .

Если режим одной операции включен, ленту на странице "Задачи" определяет область Связанный режим в файле pwaribbon.xml:

   <!-- REGION Tied Mode Ribbon-->
   <CustomAction
      Id="Ribbon.ContextualTabs.TiedMode"
      . . .

Хотя группы и элементы управления в каждой области похожи друг на друга, элемент управления для связанного режима может вызывать функции, отличные от функций того же элемента управления в другом режиме. В процедуре 4 показано, как добавить элемент управления "Кнопка" для приложения QuickStatus, если режим одной операции выключен (флажок Режим одной операции не установлен).

Примечание.

Общие сведения о добавлении настраиваемых действий на ленту или в меню в приложении SharePoint см. в статье Создание настраиваемых действий для развертывания с помощью приложений для SharePoint.

Процедура 4. Добавление настраиваемого действия ленты на страницу "Задачи"

  1. Просмотрите ленту на странице Задачи в Project Web App. Откройте вкладку ЗАДАЧИ на ленте и спланируйте, как ее изменить. Существует семь групп, таких как Отправить, Задачи и Период. Группа Отправить содержит два элемента управления: кнопка Сохранить и раскрывающееся меню Отправить состояние. Вы можете добавить элемент управления в любом месте в группе, добавить группу с новым элементом управления в любое место на вкладке ЗАДАЧИ или добавить другую вкладку с настраиваемыми группами и элементами управления. В этом примере мы добавляем третью кнопку для группы Отправить, которая вызывает URL-адрес приложения QuickStatus.

  2. В области Обозреватель решений в Visual Studio щелкните правой кнопкой мыши проект QuickStatus и добавьте новый элемент. В диалоговом окне Добавление нового элемента выберите Ленты Настраиваемое действие (см. рис. 4). Например, присвойте пользовательскому действию имя RibbonQuickStatusAction и нажмите кнопку Добавить.

    Рис. 4. Добавление настраиваемого действия на ленте

    Добавление настраиваемого действия ленты

  3. На первой странице мастера создания настраиваемого действия для ленты оставьте выбранным параметр Узел веб-сайт, выберите Нет в раскрывающемся списке для настраиваемого действия область, а затем нажмите кнопку Далее (см. рис. 5). Элементы в раскрывающихся списках относятся к SharePoint, а не к Project Server. Мы заменяем большую часть созданного XML-кода для настраиваемого действия, чтобы он применялся к Project Server.

    Рис. 5. Указание свойств для настраиваемого действия ленты

    Указание свойств для настраиваемого действия ленты

  4. На следующей странице мастера создания настраиваемого действия для ленты оставьте все значения по умолчанию для параметров и нажмите кнопку Готово (см. рис. 6). Visual Studio создает папку RibbonQuickStatusAction , которая содержит файл Elements.xml.

    Рисунок 6. Указание параметров для элемента управления "Кнопка"

    Указание параметров для элемента управления кнопкой

  5. Измените созданный по умолчанию код в файле Elements.xml для настраиваемого действия ленты. Ниже приведен XML-код по умолчанию.

     <?xml version="1.0" encoding="utf-8"?>
     <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
         <CustomAction Id="21ea3aaf-79e5-4aac-9479-8eef14b4d9df.RibbonQuickStatusAction"
                     Location="CommandUI.Ribbon"
                     Sequence="10001"
                     Title="Invoke &apos;RibbonQuickStatusAction&apos; action">
         <CommandUIExtension>
             <!-- 
             Update the UI definitions below with the controls and the command actions
             that you want to enable for the custom action.
             -->
             <CommandUIDefinitions>
             <CommandUIDefinition Location="Ribbon.ListItem.Actions.Controls._children">
                 <Button Id="Ribbon.ListItem.Actions.RibbonQuickStatusActionButton"
                         Alt="Request RibbonQuickStatusAction"
                         Sequence="100"
                         Command="Invoke_RibbonQuickStatusActionButtonRequest"
                         LabelText="Request RibbonQuickStatusAction"
                         TemplateAlias="o1"
                         Image32by32="_layouts/15/images/placeholder32x32.png"
                         Image16by16="_layouts/15/images/placeholder16x16.png" />
             </CommandUIDefinition>
             </CommandUIDefinitions>
             <CommandUIHandlers>
             <CommandUIHandler Command="Invoke_RibbonQuickStatusActionButtonRequest"
                                 CommandAction="~appWebUrl/Pages/Default.aspx"/>
             </CommandUIHandlers>
         </CommandUIExtension >
         </CustomAction>
     </Elements>
    
    1. В элементе CustomAction удалите атрибуты Sequence и Title.

    2. Чтобы добавить элемент управления в группу Submit , найдите первую группу Ribbon.ContextualTabs.MyWork.Home.Groups в коллекции в файле pwaribbon.xml, который является элементом, который начинается, <Group Id="Ribbon.ContextualTabs.MyWork.Home.Page" Command="PageGroup" Sequence="10" Title="$Resources:pwafeatures,PAGE_PDP_CM_SUBMIT". Чтобы добавить дочерний элемент управления в группу Submit , в следующем коде показан правильный атрибут Location элемента CommandUIDefinition в файле Elements.xml:

        <CommandUIDefinitions>
          <CommandUIDefinition Location="Ribbon.ContextualTabs.MyWork.Home.Page.Controls._children">
             . . .
          </CommandUIDefinition>
        </CommandUIDefinitions>
      
    3. Измените значения атрибутов дочернего элемента Button следующим образом:

           <Button Id="Ribbon.ContextualTabs.MyWork.Home.Page.QuickStatus"
                   Alt="Quick Status app"
                   Sequence="30"
                   Command="Invoke_QuickStatus"
                   LabelText="Quick Status"
                   TemplateAlias="o1"
                   Image16by16="_layouts/15/1033/images/ps16x16.png" 
                   Image16by16Left="-80"
                   Image16by16Top="-144"
                   Image32by32="_layouts/15/1033/images/ps32x32.png" 
                   Image32by32Left="-32"
                   Image32by32Top="-288" 
                   ToolTipTitle="QuickStatus"
                   ToolTipDescription="Run the QuickStatus app" />
      
      • Чтобы сделать кнопку третьим элементом управления в группе, атрибут Sequence может быть любым числом, превышающим Sequence="20" значение существующего элемента управления Send Status (который является элементом FlyoutAnchor в pwaribbon.xml). По соглашению порядковые номера групп и элементов управления — , 10, 20, 30, …что позволяет вставлять элементы в промежуточных позициях.

      • Атрибут Command указывает команду, выполняемую в элементе CommandUIHandler (см. следующий шаг 5.d). Вы можете упростить имя команды, чтобы упростить его для следующего разработчика. Например Command="Invoke_QuickStatus" , проще читать, чем Command="Invoke_RibbonQuickStatusActionButtonRequest".

      • Атрибуты изображения указывают значок 16 x 16 пикселей и значок 32 x 32 пикселя для элемента управления кнопкой. В файле Image32by32="_layouts/15/images/placeholder32x32.png" Elements.xml по умолчанию указывает оранжевую точку. Значки можно извлечь из файлов карты изображений (ps16x16.png и ps32x32.png), установленных в каталоге [Program Files]\Common Files\Microsoft Shared\Web Server Extensions\15\TEMPLATE\LAYOUTS\1033\IMAGES на компьютере с Project Server. Например, значок 32 x 32 пикселя находится во втором столбце значков слева и в десятой строке вниз от верхней части карты изображения ps32x32.png (верхняя часть значка находится после конца девятой строки; 9 строк x 32 пикселя/строка = 288 пикселей).

      • Для отображения всплывающей подсказки для элемента управления "Кнопка" добавьте атрибуты ToolTipTitle и ToolTipDescription.

    4. Измените атрибуты элемента CommandUIHandler. Например, убедитесь, что атрибут Command соответствует значению атрибута Command элемента Button. Для атрибута ~appWebUrlCommandAction — это заполнитель URL-адреса веб-страницы QuickStatus. Когда кнопка ленты вызывает приложение QuickStatus, маркер {StandardTokens} заменяется параметрами URL-адреса, а именно SPHostUrl, SPLanguage, SPClientTag, SPProductNumber и SPAppWebUrl.

          <CommandUIHandlers>
              <CommandUIHandler Command="Invoke_QuickStatus"
                                CommandAction="~appWebUrl/Pages/Default.aspx?{StandardTokens}"/>
          </CommandUIHandlers>
      
  6. В обозревателе решенийоткройте конструктор Feature1.feature и переместите элемент RibbonQuickStatusAction из области Элементы в решении в область Элементы в компоненте. При открытии конструктора Package.package элемент RibbonQuickStatusAction будет находиться в области Элементы в пакете.

При разработке приложения и добавлении кнопки ленты обычно выполняется тестирование приложения и установка точек останова в коде JavaScript для отладки. После нажатия клавиши F5 Visual Studio компилирует приложение, развертывает его на сайте, который указан в свойстве URL-адрес сайта проекта QuickStatus, и отображает страницу с вопросом о том, доверяете ли вы приложению. При переходе и выходе из приложения QuickStatus оно вернется на страницу Задачи в Project Web App.

Примечание.

На рисунке 7 показано, что кнопка Быстрого состояния на вкладке ЗАДАЧИ на ленте отключена. После многих отладочных развертываний с помощью Visual Studio настраиваемые элементы управления ленты могут быть заблокированы при продолжении отладки или развертывания опубликованного приложения на том же тестовом сервере. Чтобы включить кнопку, удалите элемент RibbonQuickStatusAction в Visual Studio, а затем создайте новое действие ленты с другим именем и идентификатором. Если это не решит проблему, попробуйте удалить приложение из тестового экземпляра Project Web App, а затем повторно создайте приложение с другим идентификатором приложения.

Рис. 7. Просмотр подсказки отключенной кнопки быстрого состояния

Просмотр подсказки отключенной кнопки

В процедуре 5 показано, как развернуть и установить приложение QuickStatus. В процедуре 6 показаны некоторые дополнительные действия для тестирования приложения после его установки.

Развертывание приложения QuickStatus

Существует несколько способов развертывания приложения в веб-приложении SharePoint, например Project Web App. Используемое развертывание будет зависеть от того, хотите ли вы опубликовать приложение в частном каталоге SharePoint или в общедоступном Магазине Office, а также от того, установлена ли SharePoint локально или является сетевым клиентом. В процедуре 5 показано, как развернуть приложение QuickStatus для локальной установки в частном каталоге приложений. Дополнительные сведения см. в разделах Установка приложений для SharePoint 2013 и Управление ими и публикация приложений для SharePoint.

Примечание.

Для добавления приложения в каталог SharePoint требуются разрешения администратора SharePoint.

Процедура 5. Развертывание приложения QuickStatus

  1. Сохраните все файлы в Visual Studio, а затем щелкните правой кнопкой мыши проект QuickStatus в обозревателе решений и выберите команду Опубликовать.

  2. Так как приложение QuickStatus размещено в SharePoint, существует очень мало вариантов публикации (см. рис. 8). В диалоговом окне Публикация приложений для Office и SharePoint нажмите кнопку Готово.

    Рис. 8. Публикация приложения QuickStatus

    Использование мастера публикации

  3. Скопируйте файл QuickStatus.app из каталога в ~\QuickStatus\bin\Debug\app.publish\1.0.0.0 удобный каталог на локальном компьютере (или на компьютер SharePoint для локальной установки).

  4. В центре администрирования SharePoint выберите Приложения на панели быстрого запуска и щелкните Управление каталогом приложений.

  5. Если каталог приложений не существует, создайте семейство веб-сайтов для каталога приложений, следуя разделу Настройка сайта каталога приложений для веб-приложениястатьи Управление каталогом приложений в SharePoint 2013.

    Если каталог приложений существует, перейдите на URL-адрес сайта на странице "Управление каталогом приложений". Например, на следующих шагах сайт каталога приложений имеет значение https://ServerName/sites/TestApps.

  6. На странице каталога приложений выберите Приложения для SharePoint на панели быстрого запуска. На странице "Приложения для SharePoint" откройте вкладку ФАЙЛЫ на ленте и нажмите кнопку Отправить документ.

  7. В диалоговом окне Добавление документа найдите файл QuickStatus.app, добавьте комментарии для версии, а затем нажмите ОК.

  8. При добавлении приложения также можно добавить локальные данные, например описание приложения, значок и другие сведения. В диалоговом окне Приложения для SharePoint — QuickStatus.app добавьте сведения, которые нужно отобразить для приложения в семействе веб-сайтов SharePoint. Например, добавьте следующие сведения:

    1. Поле краткого описания : введите приложение для тестирования быстрого состояния.

    2. Поле описание . Введите Тестовое приложение, чтобы обновить процент завершения задач в нескольких проектах.

    3. Поля URL-адреса значка . Добавьте изображение 96 x 96 пикселей для значка приложения в ресурсы сайта для каталога приложений. Например, перейдите по адресу https://ServerName/sites/TestApps, выберите Содержимое сайта в раскрывающемся меню Параметры , выберите Ресурсы сайта, а затем добавьте изображение quickStatusApp.png. Щелкните правой кнопкой мыши элемент quickStatusApp , выберите Свойства, а затем скопируйте значение Адрес (URL-адрес) в диалоговом окне Свойства . Например, скопируйте https://ServerName/sites/TestApps/SiteAssets/QuickStatusApp.png, а затем вставьте значение в поле URL-адрес значка . Введите описание значка, например (как на рис. 9), введите Значок приложения QuickStatus. Убедитесь, что URL-адрес действителен.

      Рис 9. Добавление URL-адреса значка для приложения QuickStatus

      Настройка свойств в SharePoint для приложения

    4. Поле Категория: выберите существующую категорию или укажите свое значение. Например, введите Определение состояния.

      Примечание.

      Категория с именем Определение состояния предназначена только для тестирования. Типичная категория для приложения Project Server — Управление проектами.

    5. Поле Имя издателя: введите имя издателя. В этом примере введите Project SDK.

    6. Поле Включено. Чтобы сделать приложение видимым для администраторов сайта Project Web App для установки, установите флажок Включено проверка.

    7. Дополнительные поля являются необязательными. Например, можно добавить URL-адрес поддержки и несколько образов справки для страницы сведений о приложении. На рис. 9 поля URL-адрес изображения 1 содержат URL-адрес для снимка экрана приложения и описание снимка экрана.

    8. В диалоговом окне Приложения для SharePoint — QuickStatus.app нажмите кнопку Сохранить. На рисунке 9 элемент быстрого обновления состояния в библиотеке "Приложения для SharePoint" извлечен для редактирования, поэтому на вкладке ПРАВКА ленты диалогового окна необходимо выбрать Check In для завершения процесса (см. рис. 10).

      Рис. 10. Приложение QuickStatus добавляется в библиотеку Приложений для SharePoint.

      Приложение QuickStatus добавляется в SharePoint

  9. В Project Web App в раскрывающемся меню Параметры выберите Добавить приложение. На странице Ваши приложения в разделе Быстрый запуск выберите Из организации, а затем выберите Сведения о приложении для быстрого обновления состояния . На рисунке 11 показана страница сведений со значком приложения, снимками экрана и другими сведениями, добавленными на предыдущем шаге.

    Рис. 11. Использование страницы сведений о быстром обновлении состояния в Project Web App

    Добавление приложения QuickStatus в Project Web App

  10. На странице Сведения о быстром обновлении состояния выберите ДОБАВИТЬ. Project Web App отображает диалоговое окно со списком операций, которые может выполнять приложение QuickStatus (см. рис. 12). Список операций является производным от элементов AppPermissionRequest в файле AppManifest.xml.

    Рисунок 12. Проверка доверия к приложению быстрого состояния

    Проверка доверия для приложения QuickStatus

  11. В диалоговом окне Быстрое обновление состояния вы доверяете выберите Доверять. Приложение добавляется на страницу содержимого сайта Project Web App (см. рис. 13).

    Рисунок 13. Просмотр приложения "Быстрое состояние" на странице "Содержимое сайта"

    Просмотр приложения QuickStatus в содержимом сайта

На странице "Контент сайта" щелкните значок Quick Status Update, чтобы запустить приложение.

Примечание.

Для получения дополнительных команд, которые предоставляют сведения о приложении, на странице Содержимое сайта выберите регион, содержащий имя быстрого обновления состояния и многоточие (...). Вы можете просмотреть страницу Сведения для приложения, просмотреть страницу Сведений о приложении, содержащую сведения об ошибках приложения, просмотреть страницу разрешений приложения или удалить приложение из Project Web App.

На странице Задачи в Project Web App (см. рис. 14) на ленте должна быть включена кнопка QuickStatus. Если кнопка Быстрого состояния отключена, попробуйте выполнить действия, описанные в примечании к рис. 7.

Рисунок 14. Запуск приложения QuickStatus из вкладки "ЗАДАЧИ"

Запуск приложения QuickStatus с вкладки

В процедуре 6 показано несколько тестов для проверки приложения QuickStatus.

Тестирование приложения QuickStatus

Каждая операция, которую пользователь может выполнить в приложении QuickStatus, должна быть протестирована при тестовой установке Project Server перед развертыванием приложения на рабочем сервере или в рабочем клиенте Project Online. Тестовая установка позволяет изменять и удалять назначения для пользователей, не влияя на фактические проекты. В тестирование также следует включить несколько пользователей с различными наборами разрешений, например администратора, руководителя проекта и участника группы. Тщательное тестирование поможет обнаружить изменения, которые необходимо внести в приложение, которые не были очевидны при тестировании во время разработки. В процедуре 6 показано всего несколько тестов для приложения QuickStatus, а полный исчерпывающий ряд проверок.

Процедура 6. Тестирование приложения QuickStatus

  1. Запустите приложение QuickStatus, при этом у пользователя нет назначений. Приложения должно показать сообщение синим цветом в нижней части страницы, например У имени пользователя нет назначений.

    Нажмите кнопку Update, после чего сообщение изменится на зеленое сообщение Assignments have been updated.

    Примечание.

    Поведение приложения необходимо изменить, чтобы отключить кнопку Update, если назначений не существует.

  2. Запустите приложение, в котором пользователь имеет несколько назначений в нескольких разных проектах, а некоторые назначения не завершены. Обратите внимание на внешний вид приложения и выполните следующие действия (см. рис. 15):

    1. Функция onGetAssignmentsSuccess создает строку в таблице для каждого назначения текущего пользователя. Имя проекта показано только один раз полужирным шрифтом для первого назначения в каждом проекте.

    2. Снимите флажок в заголовке столбца Имя задачи. Обработчик события щелчка заголовка таблицы снимает все другие флажки в строках задач.

    3. Выберите все задачи. Обработчик события щелчка для каждой строки определяет, выбраны ли все строки, и, если это так, выбирает заголовок столбца Имя задачи.

    4. Снова очистите все поля проверка, а затем выберите одно назначение с оставшимися рабочими очками. Например, на рисунке 15 показана верхняя задача T1, на выполнение которого осталось 20 %.

    5. В текстовом поле Задать процент завершения введите 80 и нажмите кнопку Обновить. Внизу страницы появится зеленое сообщение Assignments have been updated.

      Рисунок 15. Обновление назначения в приложении QuickStatus

      Обновление назначения в приложении QuickStatus

  3. Выберите Обновить (см. рис. 16). Все задачи выбираются снова, а верхняя задача показывает 80 % завершения.

    Рисунок 16. Обновление страницы быстрого обновления состояния

    Обновление страницы QuickStatus

  4. Очистите все поля проверка, а затем выберите другую задачу. Например, выберите Создать задачу из PWA. Оставьте поле Задать процент завершения пустым, удалите весь текст в столбце %завершено для выбранной задачи и нажмите кнопку Обновить. Так как оба текстовых поля пусты, приложение отображает красное сообщение об ошибке (см. рис. 17).

    Рисунок 17. Проверка сообщения об ошибке

    Тестирование сообщения об ошибке

  5. Обновите предыдущую задачу до 80 %, а затем нажмите кнопку Выйти. Функция exitToPwa изменяет расположение окна браузера на страницу Задачи в ведущем приложении SharePoint (то есть URL-адрес изменится на <https://ServerName/pwa/Tasks.aspx>). На рисунке 18 показано, что каждая задача T1 и Новая задача из PWA показывают 80 % завершения.

    Рисунок 18. Проверка обновления задач в Project Web App

    Проверка обновленных задач в Project Web App

  6. Прежде чем обновленное состояние отобразится в Project профессиональный 2013 г., изменения должны быть отправлены на утверждение, а затем утверждены руководителем проекта.

Тестирование показывает несколько изменений, которые необходимо внести в приложение QuickStatus для повышения удобства использования. Например:

  • Следует реализовать дополнительные проверки ошибок и значений текстовых полей. В настоящее время пользователь может ввести нечисловое или отрицательное значение процента завершения, что приводит к сообщению об ошибке. Например, для отрицательного значения отображается сообщение об ошибке Ошибка обновления назначений: PJClientCallableException: StatusingSetDataValueInvalid.

  • В сообщении об ошибке для пустых текстовых полей могут перечисляться проекты и задачи, а также номер строки.

  • Сообщение об успешном выполнении может включать в себя список обновленных задач или, если функция updateAssignments выполнена успешно, она может обновить страниц автоматически и показать обновленные задачи или процентные значения другим цветом и полужирным шрифтом.

  • Чтобы избежать получения очень большой таблицы, в таблице назначений следует добавлять только задачи, выполненные не на 100 %. Или можно добавить параметр для отображения всех задач. Эту проблему также можно устранить, используя сетку на основе jQuery вместо таблицы, где вы легко можете реализовать фильтрацию и разбиения таблицы по страницам.

  • Так как приложение QuickStatus не отправляет отчет о состоянии, значок Quick Status на вкладке ЗАДАЧИ ленты будет логичнее сделать первым значком в группе ЗАДАЧИ, а не последним значком в группе Отправить.

  • Так как функция onGetAssignmentsSuccess инициализирует текст кнопки btnSubmitUpdate, но другие текстовые значения кнопки инициализируются в HTML, страница остается в частично инициализированном состоянии при выполнении функции getAssignments. Кнопки на странице будут более согласованными, если текстовые значения инициализировать в HTML.

Самое главное, подход, используемый приложением QuickStatus, где оно изменяет процент завершения для назначений, следует изменить в рабочем приложении. Дополнительные сведения см. в разделе Дальнейшие действия.

Пример кода для приложения QuickStatus

Файл Default.aspx.

Следующий код находится в Pages\Default.aspx файле проекта QuickStatus :

    <%-- The following lines are ASP.NET directives needed when using SharePoint components --%>
    <%@ Page Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage, Microsoft.SharePoint, Version=15.0.0.0, 
    Culture=neutral, PublicKeyToken=71e9bce111e9429c" MasterPageFile="~masterurl/default.master" Language="C#" %>
    <%@ Register TagPrefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, 
    Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%@ Register TagPrefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=15.0.0.0, 
    Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, 
    Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%-- The markup and script in the following Content element will be placed in the <head> of the page.
        For production deployment, change the .debug.js JavaScript references to .js. --%>
    <asp:Content ContentPlaceHolderID="PlaceHolderAdditionalPageHead" runat="server">
    <script type="text/javascript" src="../Scripts/jquery-1.7.1.min.js"></script>
    <script type="text/javascript" src="/_layouts/15/sp.runtime.debug.js"></script>
    <script type="text/javascript" src="/_layouts/15/sp.debug.js"></script>
    <script type="text/javascript" src="/_layouts/15/ps.debug.js"></script>
    <!-- CSS styles -->
    <link rel="Stylesheet" type="text/css" href="../Content/App.css" />
    <!-- Add your JavaScript to the following file -->
    <script type="text/javascript" src="../Scripts/App.js"></script>
    </asp:Content>
    <%-- The markup and script in the following Content element will be placed in the <body> of the page --%>
    <asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server">
    <form>
        <fieldset>
        <legend>Select assigned tasks</legend>
        <table id="assignmentsTable">
            <caption id="tableCaption">Replace caption</caption>
            <thead>
            <tr id="headerRow">
                <th>Project name</th>
                <th><input type="checkbox" id="headercheckbox" checked="checked" />Task name</th>
                <th>Actual work</th>
                <th>% complete</th>
                <th>Remaining work</th>
                <th>Due date</th>
            </tr>
            </thead>
        </table>
        </fieldset>
        <div id="inputPercentComplete" >
        Set percent complete for all selected assignments, or leave this
        <br /> field blank and set percent complete for individual assignments: 
        <input type="text" name="percentComplete" id="pctComplete" size="4"  maxlength="4" />
        </div>
        <div id="submitResult">
        <p><button id="btnSubmitUpdate" type="button" class="bottomButtons" ></button></p>
        <p id="message"></p>
        </div>
        <div id="refreshPage">
        <p><button id="btnRefresh" type="button" class="bottomButtons" >Refresh</button></p>
        </div>
    <div id="exitPage">
        <p><button id="btnExit" type="button" class="bottomButtons" >Exit</button></p>
    </div>
    </form>
    </asp:Content>

Файл App.js.

Следующий код находится в Scripts\App.js файле проекта QuickStatus :

    var projContext;
    var pwaWeb;
    var projUser;
    // This code runs when the DOM is ready and creates a ProjectContext object.
    // The ProjectContext object is required to use the JSOM for Project Server.
    $(document).ready(function () {
        projContext = PS.ProjectContext.get_current();
        pwaWeb = projContext.get_web();
        getUserInfo();
        getAssignments();
        // Bind a click event handler to the table header check box, which sets the row check boxes
        // to the selected state of the header check box, and sets the results message to an empty string.
        $('#headercheckbox').live('click', function (event) {
            $('input:checkbox:not(#headercheckbox)').attr('checked', this.checked);
            $get("message").innerText = "";
        });
        // Bind a click event handler to the row check boxes. If any row check box is cleared, clear
        // the header check box. If all of the row check boxes are selected, select the header check box.
        $('input:checkbox:not(#headercheckbox)').live('click', function (event) {
            var isChecked = true;
            $('input:checkbox:not(#headercheckbox)').each(function () {
                if (this.checked == false) isChecked = false;
                $get("message").innerText = "";
            });
            $("#headercheckbox").attr('checked', isChecked);
        });
    });
    // Get information about the current user.
    function getUserInfo() {
        projUser = pwaWeb.get_currentUser();
        projContext.load(projUser);
        projContext.executeQueryAsync(onGetUserNameSuccess,
            // Anonymous function to execute if getUserInfo fails.
            function (sender, args) {
                alert('Failed to get user name. Error: ' + args.get_message());
        });
    }
    // This function is executed if the getUserInfo call is successful.
    // Replace the contents of the 'caption' paragraph with the project user name.
    function onGetUserNameSuccess() {
        var prefaceInfo = 'Assignments for ' + projUser.get_title();
        $('#tableCaption').text(prefaceInfo);
    }
    // Get the collection of assignments for the current user.
    function getAssignments() {
        assignments = PS.EnterpriseResource.getSelf(projContext).get_assignments();
        // Register the request that you want to run on the server. The optional "Include" parameter 
        // requests only the specified properties for each assignment in the collection.
        projContext.load(assignments,
            'Include(Project, Name, ActualWork, ActualWorkMilliseconds, PercentComplete, RemainingWork, Finish, Task)');
        // Run the request on the server.
        projContext.executeQueryAsync(onGetAssignmentsSuccess,
            // Anonymous function to execute if getAssignments fails.
            function (sender, args) {
                alert('Failed to get assignments. Error: ' + args.get_message());
            });
    }
    // Get the enumerator, iterate through the assignment collection, 
    // and add each assignment to the table.
    function onGetAssignmentsSuccess(sender, args) {
        if (assignments.get_count() > 0) {
            var assignmentsEnumerator = assignments.getEnumerator();
            var projName = "";
            var prevProjName = "3D2A8045-4920-4B31-B3E7-9D0C5195FC70"; // Any unique name.
            var taskNum = 0;
            var chkTask = "";
            var txtPctComplete = "";
            // Constants for creating input controls in the table.
            var INPUTCHK = '<input type="checkbox" class="chkTask" checked="checked" id="chk';
            var LBLCHK = '<label for="chk';
            var INPUTTXT = '<input type="text" size="4"  maxlength="4" class="txtPctComplete" id="txt';
            while (assignmentsEnumerator.moveNext()) {
                var statusAssignment = assignmentsEnumerator.get_current();
                projName = statusAssignment.get_project().get_name();
                // Get an integer value for the number of milliseconds of actual work, such as 3600000.
                var actualWorkMilliseconds = statusAssignment.get_actualWorkMilliseconds();
                // Get a string value for the assignment actual work, such as "1h". Not used here.
                var actualWork = statusAssignment.get_actualWork();                         
                if (projName === prevProjName) {
                    projName = "";
                }
                prevProjName = statusAssignment.get_project().get_name();
                // Create a row for the assignment information.
                var row = assignmentsTable.insertRow();
                taskNum++;
                // Create an HTML string with a check box and task name label, for example:
                //     <input type="checkbox" class="chkTask" checked="checked" id="chk1" /> 
                //     <label for="chk1">Task 1</label>
                chkTask = INPUTCHK + taskNum + '" /> ' + LBLCHK + taskNum + '">'
                    + statusAssignment.get_name() + '</label>';
                txtPctComplete = INPUTTXT + taskNum + '" />';
                // Insert cells for the assignment properties.
                row.insertCell().innerHTML = '<strong>' + projName + '</strong>';
                row.insertCell().innerHTML = chkTask;
                row.insertCell().innerText = actualWorkMilliseconds / 3600000 + 'h';
                row.insertCell().innerHTML = txtPctComplete;
                row.insertCell().innerText = statusAssignment.get_remainingWork();
                row.insertCell().innerText = statusAssignment.get_finish();
                // Initialize the percent complete cell.
                $get("txt" + taskNum).innerText = statusAssignment.get_percentComplete() + '%'
            }
        }
        else {
            $('p#message').attr('style', 'color: #0f3fdb');     // Blue text.
            $get("message").innerText = projUser.get_title() + ' has no assignments'
        }
        // Initialize the button properties.
        $get("btnSubmitUpdate").onclick = function() { updateAssignments(); };
        $get("btnSubmitUpdate").innerText = 'Update';
        $get('btnRefresh').onclick = function () { window.location.reload(true); };
        $get('btnExit').onclick = function () { exitToPwa(); };
    }
    // Update all selected assignments. If the bottom percent complete field is blank,
    // use the value in the % complete field of each selected row in the table.
    function updateAssignments() {
        // Get percent complete from the bottom text box.
        var pctCompleteMain = getNumericValue($('#pctComplete').val()).trim();
        var pctComplete = pctCompleteMain;
        var assignmentsEnumerator = assignments.getEnumerator();
        var taskNum = 0;
        var taskRow = "";
        var indexPercent = "";
        var doSubmit = true;
        while (assignmentsEnumerator.moveNext()) {
            var pctCompleteRow = "";
            taskRow = "chk" + ++taskNum;
            if ($get(taskRow).checked) {
                var statusAssignment = assignmentsEnumerator.get_current();
                if (pctCompleteMain === "") {
                    // Get percent complete from the text box field in the table row.
                    pctCompleteRow = getNumericValue($('#txt' + taskNum).val());
                    pctComplete = pctCompleteRow;
                }
                // If both percent complete fields are empty, show an error.
                if (pctCompleteMain === "" && pctCompleteRow === "") {
                    $('p#message').attr('style', 'color: #e11500');     // Red text.
                    $get("message").innerHTML =
                        '<b>Error:</b> Both <i>Percent complete</i> fields are empty, in row '
                        + taskNum
                        + ' and in the bottom textbox.<br/>One of those fields must have a valid percent.'
                        + '<p>Please refresh the page and try again.</p>';
                    doSubmit = false;
                    taskNum = 0;
                    break;
                }
                if (doSubmit) statusAssignment.set_percentComplete(pctComplete);
            }
        } 
        // Save and submit the assignment updates.
        if (doSubmit) {
            assignments.update();
            assignments.submitAllStatusUpdates();
            projContext.executeQueryAsync(function (source, args) {
                $('p#message').attr('style', 'color: #0faa0d');     // Green text.
                $get("message").innerText = 'Assignments have been updated.';
            }, function (source, args) {
                $('p#message').attr('style', 'color: #e11500');     // Red text.
                $get("message").innerText = 'Error updating assignments: ' + args.get_message();
            });
        }
    }
    // Get the numeric part for percent complete, from a string. 
    // For example, with "20 %", return "20".
    function getNumericValue(pctComplete) {
        pctComplete = pctComplete.trim();
        pctComplete = pctComplete.replace(/ /g, "");    // Remove interior spaces.
        indexPercent = pctComplete.indexOf('%', 0);
        if (indexPercent > -1) pctComplete = pctComplete.substring(0, indexPercent);
        return pctComplete;
    }
    // Exit the QuickStatus page and go back to the Tasks page in Project Web App.
    function exitToPwa() {
        // Get the SharePoint host URL, which is the top page of PWA, and add the Tasks page.
        var spHostUrl = decodeURIComponent(getQueryStringParameter('SPHostUrl'))
                        + "/Tasks.aspx";
        // Set the top window for the QuickStatus IFrame to the Tasks page.
        window.top.location.href = spHostUrl;
    }
    // Get a specified query string parameter from the {StandardTokens} URL option string.
    function getQueryStringParameter(urlParameterKey) {
        var docUrl = document.URL;
        var params = docUrl.split('?')[1].split('&');
        for (var i = 0; i < params.length; i++) {
            var theParam = params[i].split('=');
            if (theParam[0] == urlParameterKey)
                return decodeURIComponent(theParam[1]);
        }
    }

Файл App.css

Следующий код CSS находится в Content\App.css файле проекта QuickStatus :

    /* Custom styles for the QuickStatus app. */
    /*============= Table elements ========================================*/
    table {
        width: 90%;
    }
    caption {
        font-size: 16px;
        padding-bottom: 5px;
        font-weight: bold;
        color: gray;
    }
    table th {
        background-color: gray;
        color: white;
    }
    table td, th {
        width: auto;
        text-align: left;
        padding: 2px;
        border: solid 1px whitesmoke;
        color: gray;
    }
    /*=== Class for check boxes added to rows 
    */
    .chkTask {
        width: 12px;
        height: 12px;
        color: gray;
    }
    /*========== DIV id for the Percent Complete text box ================*/
    #inputPercentComplete {
        position: fixed;
        top: auto;
        height: auto;
        padding-top: 20px;
        margin-left: 30px;
    }
    /*========== DIV id for the Submit Result button ====================*/
    #submitResult {
        position: fixed;
        top: auto;
        height: auto;
        padding-top: 60px;
    }
    /*========== DIV id for the Refresh Page button ====================*/
    #refreshPage {
        position: fixed;
        top: auto;
        height: auto;
        padding-top: 60px;
        margin-left: 120px;
    }
    /*========== DIV id for the Exit Page button ====================*/
    #exitPage {
        position: fixed;
        top: auto;
        height: auto;
        padding-top: 60px;
        margin-left: 240px;
    }
    /*========== Class for the buttons at the bottom of the page =======*/
    .bottomButtons {
        color: gray;
        font-weight: bold; 
        font-size: 12px; 
        border-color: darkgreen;
        border-width: thin;
    }

Файл Elements.xml для ленты.

Следующее xml-определение для кнопки добавления на вкладке ЗАДАЧИ на ленте находится в RibbonQuickStatusAction\Elements.xml файле проекта QuickStatus :

    <?xml version="1.0" encoding="utf-8"?>
    <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
    <CustomAction Id="21ea3aaf-79e5-4aac-9479-8eef14b4d9df.RibbonQuickStatusAction"
                    Location="CommandUI.Ribbon">
        <CommandUIExtension>
        <!-- 
        Add a button that invokes the QuickStatus app. The Quick Status button is displayed as  
        the third control in the Page group (the group title is "Submit").
        -->
        <CommandUIDefinitions>
            <CommandUIDefinition Location="Ribbon.ContextualTabs.MyWork.Home.Page.Controls._children">
            <Button Id="Ribbon.ContextualTabs.MyWork.Home.Page.QuickStatus"
                    Alt="Quick Status app"
                    Sequence="30"
                    Command="Invokae_QuickStatus"
                    LabelText="Quick Status"
                    TemplateAlias="o1"
                    Image16by16="_layouts/15/1033/images/ps16x16.png" 
                    Image16by16Left="-80"
                    Image16by16Top="-144"
                    Image32by32="_layouts/15/1033/images/ps32x32.png" 
                    Image32by32Left="-32"
                    Image32by32Top="-288" 
                    ToolTipTitle="Quick Status"
                    ToolTipDescription="Run the QuickStatus app" />
            </CommandUIDefinition>
        </CommandUIDefinitions>
        <CommandUIHandlers>
            <CommandUIHandler Command="Invoke_QuickStatus"
                            CommandAction="~appWebUrl/Pages/Default.aspx?{StandardTokens}"/>
        </CommandUIHandlers>
        </CommandUIExtension >
    </CustomAction>
    </Elements>

Файл AppManifest.xml

Ниже приведен XML-код для манифеста приложения проекта QuickStatus, содержащий две области запроса разрешений, которые необходимы для обновления состояния назначения пользователя в нескольких проектах:

    <?xml version="1.0" encoding="utf-8" ?>
    <!--Created:cb85b80c-f585-40ff-8bfc-12ff4d0e34a9-->
    <App xmlns="http://schemas.microsoft.com/sharepoint/2012/app/manifest"
        Name="QuickStatus"
        ProductID="{bbc497e7-1221-4d7b-a0ae-141a99546008}"
        Version="1.0.0.0"
        SharePointMinVersion="15.0.0.0"
    >
    <Properties>
        <Title>Quick Status Update</Title>
        <StartPage>~appWebUrl/Pages/Default.aspx?{StandardTokens}</StartPage>
    </Properties>
    <AppPrincipal>
        <Internal />
    </AppPrincipal>
    <AppPermissionRequests>
        <AppPermissionRequest Scope="https://sharepoint/projectserver/statusing" Right="SubmitStatus" />
        <AppPermissionRequest Scope="https://sharepoint/projectserver/projects" Right="Read" />
    </AppPermissionRequests>
    </App>

Файл AppIcon.png.

Полное решение Visual Studio для приложения QuickStatus содержит настраиваемые файл AppIcon.png. Решение будет включено в пакет SDK для Project 2013.

Дальнейшие действия

Приложение QuickStatus — это относительно простой пример создания приложений, которые можно установить в Project Server 2013 и Project Online. В разделе Тестирование приложения QuickStatus перечислены несколько улучшений, которые могут сделать приложение более удобным. Приложение QuickStatus использует функции JavaScript для обновления состояния назначения для Project Web App. Однако изменение процента завершения назначения — нерекомендуемый метод для управления проектами. Другой подход состоит в обновлении фактической начальной даты и оставшейся длительности назначенных задач. Обсуждение этих вопросов см. в разделе Обновление лучше в бюллетене MPUG.

См. также