First create API access

未命名_2016-12-23_21-24-00.png

Generate a server-to-server key

  • openssl ecparam -name prime256v1 -genkey -noout -out eckey.pem
  • openssl ec -in eckey.pem -pubout
  • paste output to public key and save

    未命名_2016-12-23_21-38-16.png

  • npm install node-fetch
  • curl https://cdn.apple-cloudkit.com/ck/2/cloudkit.js > cloudkit.js

For web client

  • use cloutkit.js and knockout.js (for data-binding)
    <!doctype html>
    <html lang="en">
      <head>
          <meta charset="UTF-8"/>
          <meta name="viewport" content="width=device-width, initial-scale=1" />
          <title>Pollster from Web</title>
          <!-- FONT –––––––––––––––––––––––––––––––––––––––––––––––––– -->
          <link href="https://fonts.googleapis.com/css?family=Raleway:400,300,600" rel="stylesheet" type="text/css">
    
          <!-- CSS –––––––––––––––––––––––––––––––––––––––––––––––––– -->
          <link rel="stylesheet" href="css/normalize.css">
          <link rel="stylesheet" href="css/skeleton.css">
    
      </head>
    
      <body>
            <!-- Primary Page Layout –––––––––––––––––––––––––––––––––––––––––––––––––– -->
      <div class="container">
        <div class="row">
          <div class="u-full-width">
            <h2>CloudKit Web Service</h2>
          </div>
        </div>
        <div class="row">
          <div class="six columns">
              <h5 data-bind="text: displayUserName"></h5>
          </div>
          <div class="four columns">
            <div id="apple-sign-in-button"></div>
            <div id="apple-sign-out-button"></div>
          </div>
          <div class="two columns">
            <div><button data-bind="click: performQuery">Manual Refresh</button></div>
          </div>
        </div>
    
        <table>
            <thead>
                <tr>
                    <th>Question </th>
                    <th>Answers</th>
                </tr>
            </thead>
            <tbody data-bind="foreach: items">
                <tr>
                    <td data-bind="text: fields.question.value"></td>
                    <td data-bind="text: fields.answers.value"></td>
                </tr>
            </tbody>
        </table>
        <!-- all scripts -->
        <script src="https://cdn.apple-cloudkit.com/ck/2/cloudkit.js"></script>
        <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>
        <script>
         window.addEventListener('cloudkitloaded', function() {
             console.log("listening for cloudkitloaded");
             ko.applyBindings(AppViewModel());
         });
        </script>
        <script type="text/javascript" src="viewmodel.js"></script>
      </body>
    </html>
    
  • for viewmodel (using knockout term)
    • first config cloudkit
      function AppViewModel() {
          var self = this;
          console.log("listening for cloudkit loaded");
      
          // cloudkit configuration
          CloudKit.configure({
              containers: [{
                  // Change this to a container identifier you own.
                  containerIdentifier: 'iCloud.tw.com.onionstudio.jerry.Pollster',
                  apiTokenAuth: {
                      // And generate a web token through CloudKit Dashboard.
                      apiToken: '<insert your api key>';
                      persist: true, // Sets a cookie.
                  },
                  environment: 'development'
              }]
          });
          console.log("cloudkitloaded");
      
          //...
      
      }
      
    • setup authorization
      var container = CloudKit.getDefaultContainer();
      var database = container.publicCloudDatabase;
      
      // setup authorizaiton
      self.displayUserName = ko.observable('');
      
      var gotoAuthenticatedState = function(userIdentity) {
          var name = userIdentity.nameComponents;
          if(name) {
              self.displayUserName(name.givenName + ' ' + name.familyName);
          } else {
              self.displayUserName('User record name: ' + userIdentity.userRecordName);
          }
      
          container
              .whenUserSignsOut()
              .then(gotoUnauthenticatedState);
      };
      var gotoUnauthenticatedState = function(error) {
      
          if(error && error.ckErrorCode === 'AUTH_PERSIST_ERROR') {
              showDialogForPersistError();
          }
      
          displayUserName('Unauthenticated User');
          container
              .whenUserSignsIn()
              .then(gotoAuthenticatedState)
              .catch(gotoUnauthenticatedState);
      };
      // Check a user is signed in and render the appropriate button.
      container.setUpAuth().then(function(userIdentity) {
          console.log("SetUpAuth");
          // Either a sign-in or a sign-out button was added to the DOM.
          // userIdentity is the signed-in user or null.
          if(userIdentity) {
              gotoAuthenticatedState(userIdentity);
          } else {
              gotoUnauthenticatedState();
          }
      });
      console.log("setup authorizaiton");
      
    • perform query
      // fetch records and put them into items array
      self.items = ko.observableArray();
      
      var databaseScope = 'PUBLIC';
      var zoneName = '_defaultZone';
      var ownerRecordName = '';
      var recordType = 'QandA';
      var desiredKeys = ['question', 'answers'];
      var sortByField = 'question';
      var ascending = true;
      var filters = '';
      self.performQuery = function()
      {
          console.log("perform query ...");
          var db = container.getDatabaseWithDatabaseScope(
              CloudKit.DatabaseScope[databaseScope]
          );
      
          // Set the query parameters.
          var query = {
              recordType: recordType
          };
      
          if(sortByField) {
              var sortDescriptor = {
                  fieldName: sortByField,
                  ascending: ascending
              };
              query.sortBy = [sortDescriptor];
          }
      
          // Convert the filters to the appropriate format.
      
          /*
           query.filterBy = filters.map(function(filter) {
           filter.fieldValue = { value: filter.fieldValue };
           return filter;
           });
           */
      
      
          // Set the options.
          var options = {
              // Restrict our returned fields to this array of keys.
              desiredKeys: desiredKeys,
              // Fetch 5 results at a time.
              resultsLimit: 5
          };
      
          if(zoneName) {
              options.zoneID = { zoneName: zoneName };
              if(ownerRecordName) {
                  options.zoneID.ownerRecordName = ownerRecordName;
              }
          }
      
          // If we have a continuation marker, use it to fetch the next 5 results.
      
          /*
           var continuationMarker = getContinuationMarker();
           if(continuationMarker) {
           options.continuationMarker = continuationMarker;
           }
           */
      
          // Execute the query.
          return db.performQuery(query,options)
              .then(function (response){
                  if(response.hasErrors) {
                      // Handle them in your app.
                      throw response.errors[0];
      
                  } else {
                      var records = response.records;
                      console.log(records);
                      self.items(records);
      
                      return records;
                  }
              });
      }
      
    • add records, add javascript saveRecord and add an area to input
      self.newQuestion = ko.observable('');
      self.newAnswers = ko.observable([]);
      self.saveButtonEnabled = ko.observable(true);
      self.newItemVisible = ko.observable(false);
      
      self.saveNewItem = function() {
          if (self.newQuestion().length > 0 && self.newAnswers().length > 0) {
              self.saveButtonEnabled(false);
              self.newAnswers(self.newAnswers().split(','));
      
              var record = { recordType: "QandA",
                             fields: { question: { value: self.newQuestion() },
                                       answers: { value: self.newAnswers() }}
                           };
      
              database.saveRecords(record).then(function(response) {
                  if (response.hasErrors) {
                      console.error(response.errors[0]);
                      self.saveButtonEnabled(true);
                      return;
                  }
                  var createdRecord = response.records[0];
                  self.items.push(createdRecord);
                  self.newQuestion("");
                  self.newAnswers("");
                  self.saveButtonEnabled(true);
              });
          } else {
              alert('QandA must with question and answers');
          }
      };
      
      <!-- user to add records -->
      <div data-bind="visible: newItemVisible">
          <div class="row">
              <div class="u-full-width">
                  <h4>Add New Q and A</h4>
              </div>
          </div>
          <form data-bind="submit: saveNewItem">
              <div class="row">
                  <div class="three columns">
                      <label>Question</label>
                      <input class="u-full-width" placeholder="question" data-bind="value: newQuestion">
                  </div>
                  <div class="nine columns">
                      <label>Answers</label>
                      <input class="u-full-width" placeholder="answer1,answer2,answer3..." data-bind="value: newAnswers">
                      <input class="button-primary" type="submit" data-bind="enable: saveButtonEnabled" value="Save QandA">
                  </div>
              </div>
          </form>
      </div>
      
    • To remove record, first add role

      未命名_2016-12-25_18-02-06.png

      • add function in js
        self.removeRecord = function(record) {
        
            database.deleteRecords(record).then(function(response) {
                if (response.hasErrors) {
                    throw response.errors[0];
                    return;
                }
                else {
                    var deletedRecord = response.records[0];
                    for (var i = self.items.length; i--;) {
                        if (self.items[i] == deletedRecord) {
                            self.items.splice(i, 1);
                        }
                    }
                    return deletedRecord;
                }
            });
        
            // refresh
        
        };
        
      • add button in index.html
        <tbody data-bind="foreach: items">
            <tr>
                <td data-bind="text: fields.question.value" ></td>
                <td data-bind="text: fields.answers.value" ></td>
                <td><button data-bind="click: removeRecord">Remove</button></td>
            </tr>
        </tbody>
        
    • add subscription, so when Cloud server changes on create, delete, update will fire
      var gotoAuthenticatedState = function(userInfo) {
          self.newItemVisible(true);
      
          if(userInfo.isDiscoverable) {
              self.displayUserName(userInfo.firstName + ' ' + userInfo.lastName);
          } else {
              self.displayUserName('User record name: ' + userInfo.userRecordName);
          }
      
      
          var querySubscription = {
              subscriptionType: 'query',
              subscriptionID: userInfo.userRecordName,
              firesOn: ['create', 'update', 'delete'],
              query: { recordType: 'QandA', sortBy: [{ fieldName: 'question'}] }
          };
      
          database.fetchSubscriptions([querySubscription.subscriptionID]).then(function(response) {
          if(response.hasErrors) {  // subscription doesn't exist, so save it
              database.saveSubscriptions(querySubscription).then(function(response) {
                  if (response.hasErrors) {
                      console.error(response.errors[0]);
                      throw response.errors[0];
                  } else {
                      console.log("successfully saved subscription")
                  }
              });
          }
          });
      
          container.registerForNotifications();
          container.addNotificationListener(function(notification) {
              console.log(notification);
              self.performQuery();
          });
      
          container
              .whenUserSignsOut()
              .then(gotoUnauthenticatedState);
      
      };
      
Notice: compact(): Undefined variable: limits in /var/www/html/wp-includes/class-wp-comment-query.php on line 853 Notice: compact(): Undefined variable: groupby in /var/www/html/wp-includes/class-wp-comment-query.php on line 853

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *