ViewerEditLockFields.cs
//
// This code is part of Document Solutions for PDF demos.
// Copyright (c) MESCIUS inc. All rights reserved.
//
using System.IO;
using GrapeCity.Documents.Pdf;
using System.Drawing;
using GrapeCity.Documents.Pdf.Annotations;
using GrapeCity.Documents.Pdf.Actions;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Pdf.AcroForms;

namespace DsPdfWeb.Demos
{
    // This example demonstrates how to create a custom sidebar with a specific UI that controls the availability of fields.
    // This example uses the "locked" annotation property.
    // When the locked property is set to true, the user cannot edit or delete annotations using the user interface.

    // This and other samples in this section demonstrate the features of GcPdfViewer
    // (a JavaScript PDF viewer control included with DsPdf), mainly the ability
    // to change PDF files (add or edit annotations and AcroForm fields, rotate pages etc.)
    // when the JS viewer on the client is supported by DsPdf running on the server.
    //
    // To enable the editing features of the viewer, its supportApi property must be set
    // to a URL on the server that implements all or some of the edit supporting APIs
    // that are known to/expected by the viewer. This DsPdf demo site provides those APIs,
    // which makes it possible to demonstrate the editing when you open the PDF viewer
    // in this sample. When you download this sample, in addition to the .NET Core
    // console app project that generates the sample PDF, an ASP.NET Core project is
    // also included in the download zip (located in the GcPdfViewerWeb sub-folder of the
    // downloaded zip), which also provides the necessary APIs. In particular, it includes
    // a project that implements the APIs and provides them via a special controller.
    // It is actually the same controller that is used by this DsPdf demo site, and which
    // can be used in any ASP.NET Core site to enable the viewer editing features.
    //
    // Look at the following files in the sample download zip for more info:
    // - GcPdfViewerWeb\SupportApiDemo: the sample ASP.NET Core web site.
    // - GcPdfViewerWeb\SupportApiDemo.sln: solution to build/run the sample web site.
    // - GcPdfViewerWeb\SupportApi: support API implementation (can be used in any site).
    // - GcPdfViewerWeb\SupportApi\Controllers\GcPdfViewerController.cs: support API controller.
    // 
    // Please note that this and other samples in this section are only available in C# at this time.
    //
    public class ViewerEditLockFields
    {
        public void CreatePDF(Stream stream)
        {
            var doc = new GcPdfDocument();
            var page = doc.NewPage();

            var rc = Common.Util.AddNote(
                "This example demonstrates how to create a custom sidebar with a specific UI that controls the availability of fields." +
                "This example uses the locked annotation property." +
                "When the locked property is set to true, the user cannot edit or delete annotations using the user interface.",
                page);

            var g = page.Graphics;
            var tf = new TextFormat() { Font = StandardFonts.Times, FontSize = 14 };
            var ip = new PointF(72, rc.Bottom + 36);
            float fldOffset = 72 * 2 + 46;
            float fldHeight = tf.FontSize * 1.2f;
            float dY = 32;

            // Text field:
            g.DrawString("First name:", tf, ip);
            var fldFirstName = new TextField() { Name = "FirstName", Value = "John" };
            fldFirstName.Widget.Page = page;
            fldFirstName.Widget.Rect = new RectangleF(ip.X + fldOffset, ip.Y, 72 * 3, fldHeight);
            fldFirstName.Widget.DefaultAppearance.Font = tf.Font;
            fldFirstName.Widget.DefaultAppearance.FontSize = tf.FontSize;
            doc.AcroForm.Fields.Add(fldFirstName);
            ip.Y += dY;

            // Text field:
            g.DrawString("Last name:", tf, ip);
            var fldLastName = new TextField() { Name = "LastName", Value = "Smith" };
            fldLastName.Widget.Page = page;
            fldLastName.Widget.Rect = new RectangleF(ip.X + fldOffset, ip.Y, 72 * 3, fldHeight);
            fldLastName.Widget.DefaultAppearance.Font = tf.Font;
            fldLastName.Widget.DefaultAppearance.FontSize = tf.FontSize;
            doc.AcroForm.Fields.Add(fldLastName);
            ip.Y += dY;

            // Checkbox:
            g.DrawString("Subscribe to Mailing List:", tf, ip);
            var fldCheckbox = new CheckBoxField() { Name = "Subscribe", Checked = true };
            fldCheckbox.Widget.Page = page;
            fldCheckbox.Widget.Rect = new RectangleF(ip.X + fldOffset, ip.Y, fldHeight, fldHeight);
            doc.AcroForm.Fields.Add(fldCheckbox);
            ip.Y += dY;

            // Multiline TextBox:
            g.DrawString("Additional information:", tf, ip);
            var fldAdditionalInfo = new TextField() { Name = "AdditionalInfo", Multiline = true };
            fldAdditionalInfo.Widget.Page = page;
            fldAdditionalInfo.Widget.Rect = new RectangleF(ip.X + fldOffset, ip.Y, 72 * 3, fldHeight * 2);
            fldAdditionalInfo.Widget.DefaultAppearance.Font = tf.Font;
            fldAdditionalInfo.Widget.DefaultAppearance.FontSize = tf.FontSize;
            doc.AcroForm.Fields.Add(fldAdditionalInfo);
            ip.Y += dY * 2;

            // Reset form button:
            var btnReset = new PushButtonField();
            btnReset.Widget.Rect = new RectangleF(ip.X + fldOffset, ip.Y, 72 * 3, fldHeight);
            btnReset.Widget.ButtonAppearance.Caption = "Reset";
            btnReset.Widget.Highlighting = HighlightingMode.Invert;
            btnReset.Widget.Page = page;
            btnReset.Widget.Activate = new ActionResetForm();
            doc.AcroForm.Fields.Add(btnReset);
            ip.Y += dY;

            // Done:
            doc.Save(stream);
        }

        // Used by SupportApiDemo to initialize GcPdfViewer.
        public static GcPdfViewerSupportApiDemo.Models.PdfViewerOptions PdfViewerOptions
        {
            get => new GcPdfViewerSupportApiDemo.Models.PdfViewerOptions(
                GcPdfViewerSupportApiDemo.Models.PdfViewerOptions.Options.AnnotationEditorPanel,
                viewerTools: new string[] { "save", "$navigation", "$split", "text-selection", "pan", "$zoom", "$fullscreen", "download", "print", "rotate", "view-mode", "hide-annotations", "doc-properties", "about" },
                annotationEditorTools: new string[] { "edit-select", "$split", "edit-link", "edit-stamp", "$split", "edit-undo", "edit-redo", "save" });
        }

        public const string JS_CODE = @"
var viewer;
var React;
var ReactDOM;
var initialAccessTable = null;

function applyAccess(selectedUserName, access) {
  console.log('Apply access ' + selectedUserName + ': ' + access + ', viewer.currentUserName=' +  viewer.currentUserName);

  if (selectedUserName === viewer.currentUserName) {
    viewer.findAnnotation(20, // 20 - AnnotationTypeCode.WIDGET
    {
      findField: 'annotationType',
      pageNumberConstraint: 1,
      findAll: true
    }).then(function (annotationsDataArr) {
      if (access === 'LockAll') {
        viewer.updateAnnotations(0, annotationsDataArr.map(function (data) {
          data.annotation.locked = true;
          return data.annotation;
        }));
      } else {
        viewer.updateAnnotations(0, annotationsDataArr.map(function (data) {
          data.annotation.locked = false;
          return data.annotation;
        }));
      }
    });
  }
}

function getInitialAccessTable(users) {
	if(!initialAccessTable) {
      initialAccessTable = {};
      for(var i = 0; i < users.length; i++) {
        initialAccessTable[users[i]] = 'UnlockAll';
      }
    }
    return initialAccessTable;
}

function FieldsLockerPanel(props)  {
	var availableUsers = props.availableUsers;
    var stateArr1 = React.useState(availableUsers[0] || ''),
        selectedUserName = stateArr1[0],
        setSelectedUserName = stateArr1[1];
    var stateArr2 = React.useState(getInitialAccessTable(availableUsers));
    var accessTable = stateArr2[0],
        setAccessTable = stateArr2[1];
    var userOpts = [];

    for (var i = 0; i < availableUsers.length; i++) {
      var curUserName = availableUsers[i];
      userOpts.push( /*#__PURE__*/React.createElement('option', {
        selected: !!(selectedUserName === curUserName),
        value: curUserName
      }, curUserName));
    }

    var currentAccess = accessTable[selectedUserName];
    return /*#__PURE__*/React.createElement('div', {
      style: {
        margin: '30px 30px 30px 30px'
      },
      class: 'fields-locker-panel-outer'
    }, /*#__PURE__*/React.createElement('p', {
      class: 'gc-heading__text'
    }, 'Current user:'), /*#__PURE__*/React.createElement('p', null, /*#__PURE__*/React.createElement('label', {
      class: 'current-user-label'
    }, /*#__PURE__*/React.createElement('b', null, props.userName))), /*#__PURE__*/React.createElement('p', {
      class: 'gc-heading__text'
    }, 'Select user:'), /*#__PURE__*/React.createElement('p', null, /*#__PURE__*/React.createElement('select', {
      style: {
        width: '100%'
      },
      class: 'gc-combo select-user-combo',
      onChange: function (e) {
        setSelectedUserName(e.target.value);
      }
    }, userOpts), /*#__PURE__*/React.createElement('label', {
      style: {
        margin: '10px 10px 10px 10px'
      }
    }, 'Access: ', currentAccess)), /*#__PURE__*/React.createElement('p', {
      class: 'gc-heading__text'
    }, 'Choose access type:'), /*#__PURE__*/React.createElement('p', null, /*#__PURE__*/React.createElement('select', {
      style: {
        width: '100%'
      },
      value: currentAccess,
      class: 'gc-combo access-combo',
      onChange: function (e) {
        var newAccessTable = JSON.parse(JSON.stringify(accessTable));
        newAccessTable[selectedUserName] = e.target.value;
        initialAccessTable = newAccessTable;
        setAccessTable(newAccessTable);
        applyAccess(selectedUserName, e.target.value);
      }
    }, /*#__PURE__*/React.createElement('option', {
      selected: currentAccess === 'UnlockAll',
      value: 'UnlockAll'
    }, 'Unlock all fields'), /*#__PURE__*/React.createElement('option', {
      selected: currentAccess === 'LockAll',
      value: 'LockAll'
    }, 'Lock all fields'))), /*#__PURE__*/React.createElement('p', null, /*#__PURE__*/React.createElement('button', {
      style: {
        width: '100%',
        textAlign: 'center'
      },
      class: 'gc-btn',
      onClick: function (e) {
        viewer.layoutMode = 2;
      }
    }, /*#__PURE__*/React.createElement('span', {
      class: 'gc-btn__text'
    }, 'Open Form Editor'))));
	
}

function createFieldsLockerPanel(userName)  { 
  // Create react element:
  return React.createElement(FieldsLockerPanel, { userName: userName, availableUsers: ['Anonymous', 'James', 'Jame', 'Lisa', 'John'] });
}
	
function createSvgIconElement()
{
    return React.createElement('svg', { xmlns: 'http://www.w3.org/2000/svg', version: '1.1', width: '24', height: '24', viewBox: '0 0 24 24', fill: '#ffffff' }, React.createElement('path', { d: 'M10 13.32q-0.352-0.273-3.184-2.48t-4.316-3.34l7.5-5.82 7.5 5.82q-1.484 1.133-4.297 3.32t-3.203 2.5zM10 15.469l6.133-4.805 1.367 1.055-7.5 5.82-7.5-5.82 1.367-1.055z' }));
}

function createPdfViewer(selector, baseOptions) {
    var options = baseOptions || {};
    if(!options.supportApi) {
        options.supportApi = {
            apiUrl: (window.__baseUrl || '') + 'support-api/gc-pdf-viewer',
            token: window.afToken || '',
            webSocketUrl: false, suppressInfoMessages: true, suppressErrorMessages: true
        };
    }
    viewer = new GcPdfViewer(selector, options);
	React = viewer.getType('React');
	ReactDOM = viewer.getType('ReactDOM');	
	viewer.addFormEditorPanel();
	
    accessPanelHandle = viewer.createPanel(createFieldsLockerPanel(viewer.currentUserName || 'World'), null, 'AccessLevel',  
        { icon: { type: 'svg', content: createSvgIconElement() },
          label: 'Access level', description: 'Access level administration',
          visible: false, enabled: false } );
    // Add 'AccessLevel' panel to panels layout:
    viewer.layoutPanels(['AccessLevel', 'FormEditor']);

    viewer.onAfterOpen.register(()=>{
      // Enable 'AccessLevel' panel on document open:
      viewer.updatePanel(accessPanelHandle, { visible: true, enabled: true });
      // Open and pin panel:
      viewer.leftSidebar.menu.panels.open(accessPanelHandle.id);
	  viewer.leftSidebar.menu.panels.pin(accessPanelHandle.id);
    });
    return viewer;
}
";
    }
}