michielbdejong/solid-panes

View on GitHub
src/internal/internalPane.ts

Summary

Maintainability
C
1 day
Test Coverage
/*   Internal Pane
 **
 **  This outline pane contains the properties which are
 ** internal to the user's interaction with the web, and are not normally displayed
 */
/* global alert confirm */

import { icons, ns, widgets } from 'solid-ui'
import { IndexedFormula, literal, NamedNode, st, sym } from 'rdflib'
import { PaneDefinition } from 'pane-registry'

const pane: PaneDefinition = {
  icon: icons.originalIconBase + 'tango/22-emblem-system.png',

  name: 'internal',

  audience: [ns.solid('Developer')],

  label: function () {
    return 'under the hood' // There is often a URI even of no statements
  },

  render: function (subject, context) {
    const dom = context.dom
    const store = context.session.store
    const canonizedSubject = store.canon(subject)
    const types = store.findTypeURIs(canonizedSubject)

    function filter (pred: NamedNode) {
      if (types['http://www.w3.org/2007/ont/link#ProtocolEvent']) return true // display everything for them
      const view = context.session.paneRegistry.byName('internal')
      return (
        view &&
        view.predicates &&
        !!(typeof view.predicates[pred.uri] !== 'undefined')
      )
    }

    var div = dom.createElement('div')
    div.setAttribute('class', 'internalPane')
    div.setAttribute(
      'style',
      'background-color: #ddddff; padding: 0.5em; border-radius: 1em;'
    )

    function deleteRecursive (kb: IndexedFormula, folder: NamedNode) {
      const fetcher = (kb as any).fetcher
      if (!fetcher) {
        console.error('No fetcher available')
        return
      }
      return new Promise(function (resolve, reject) {
        fetcher.load(folder).then(function () {
          const promises = kb.each(folder, ns.ldp('contains')).map(file => {
            if (kb.holds(file, ns.rdf('type'), ns.ldp('BasicContainer'))) {
              return deleteRecursive(kb, file as NamedNode)
            } else {
              console.log('deleteRecursive leaf file: ' + file)
              return fetcher.webOperation('DELETE', (file as NamedNode).uri)
            }
          })
          Promise.all(promises).then(
            () => {
              console.log('deleteRecursive empty folder: ' + folder)
              fetcher
                .webOperation('DELETE', folder.uri)
                .then(() => {
                  console.log('Deleted Ok: ' + folder)
                  resolve()
                })
                .catch((err: string) => {
                  var str = 'Unable to delete ' + folder + ': ' + err
                  console.log(str)
                  reject(new Error(str))
                })
              resolve()
            },
            err => {
              alert(err)
              reject(err)
            }
          )
        })
      })
    }

    const isDocument = subject.uri && !subject.uri.includes('#')
    if (isDocument) {
      const controls = div.appendChild(dom.createElement('table'))
      controls.style.width = '100%'
      controls.style.margin = '1em'
      const controlRow = controls.appendChild(dom.createElement('tr'))

      const deleteCell = controlRow.appendChild(dom.createElement('td'))
      const isFolder =
        (subject.uri && subject.uri.endsWith('/')) ||
        store.holds(subject, ns.rdf('type'), ns.ldp('Container'))
      const noun = isFolder ? 'folder' : 'file'
      if (!isProtectedUri(subject)) {
        console.log(subject)
        var deleteButton = widgets.deleteButtonWithCheck(
          dom,
          deleteCell,
          noun,
          function () {
            if (
              !confirm(
                `Are you sure you want to delete ${subject}? This cannot be undone.`
              )
            ) {
              return
            }
            // @@ TODO Remove casing of store.fetcher
            var promise = isFolder
              ? deleteRecursive(store, subject)
              : (store as any).fetcher.webOperation('DELETE', subject.uri) // @@ TODO remove casting
            promise
              .then(() => {
                var str = 'Deleted: ' + subject
                console.log(str)
              })
              .catch((err: any) => {
                var str = 'Unable to delete ' + subject + ': ' + err
                console.log(str)
                alert(str)
              })
          }
        )
        deleteButton.style = 'height: 2em;'
        deleteButton.class = '' // Remove hover hide
        deleteCell.appendChild(deleteButton)
      }

      const refreshCell = controlRow.appendChild(dom.createElement('td'))
      const refreshButton = widgets.button(
        dom,
        icons.iconBase + 'noun_479395.svg',
        'refresh'
      )
      refreshCell.appendChild(refreshButton)
      refreshButton.addEventListener('click', () => {
        // @@ TODO Remove casting of store.fetcher
        ;(store as any).fetcher.refresh(subject, function (
          // @@ TODO Remove casting
          ok: boolean,
          errm: string
        ) {
          let str
          if (ok) {
            str = 'Refreshed OK: ' + subject
          } else {
            str = 'Error refreshing: ' + subject + ': ' + errm
          }
          console.log(str)
          alert(str)
        })
      })
    }

    var plist = store.statementsMatching(subject)
    var docURI = ''
    if (subject.uri) {
      plist.push(
        st(
          subject,
          sym('http://www.w3.org/2007/ont/link#uri'),
          subject.uri,
          (store as any).fetcher.appNode // @@ TODO Remove casting
        )
      )
      if (subject.uri.indexOf('#') >= 0) {
        docURI = subject.uri.split('#')[0]
        plist.push(
          st(
            subject,
            sym('http://www.w3.org/2007/ont/link#documentURI'),
            subject.uri.split('#')[0],
            (store as any).fetcher.appNode // @@ TODO Remove casting
          )
        )
        plist.push(
          st(
            subject,
            sym('http://www.w3.org/2007/ont/link#document'),
            sym(subject.uri.split('#')[0]),
            (store as any).fetcher.appNode // @@ TODO Remove casting
          )
        )
      } else {
        docURI = subject.uri
      }
    }
    if (docURI) {
      // @@ TODO Remove casting of store.updater.editable
      var ed = (store.updater as any).editable(docURI)
      if (ed) {
        // @@ TODO Remove casting of literal when rdflib exports proper types
        plist.push(
          st(
            subject,
            sym('http://www.w3.org/ns/rww#editable'),
            (literal as any)(ed),
            (store as any).fetcher.appNode // @@ TODO remove casting
          )
        )
      }
    }
    // @@ TODO get a proper type
    const outliner: any = context.getOutliner(dom)
    outliner.appendPropertyTRs(div, plist, false, filter)
    plist = store.statementsMatching(undefined, undefined, subject)
    outliner.appendPropertyTRs(div, plist, true, filter)
    return div
  },

  predicates: {
    // Predicates used for inner workings. Under the hood
    'http://www.w3.org/2007/ont/link#request': 1,
    'http://www.w3.org/2007/ont/link#requestedBy': 1,
    'http://www.w3.org/2007/ont/link#source': 1,
    'http://www.w3.org/2007/ont/link#session': 2, // 2=  test neg but display
    'http://www.w3.org/2007/ont/link#uri': 1,
    'http://www.w3.org/2007/ont/link#documentURI': 1,
    'http://www.w3.org/2007/ont/link#document': 1,
    'http://www.w3.org/2007/ont/link#all': 1, // From userinput.js
    'http://www.w3.org/2007/ont/link#Document': 1,
    'http://www.w3.org/ns/rww#editable': 1,
    'http://www.w3.org/2000/01/rdf-schema#seeAlso': 1,
    'http://www.w3.org/2002/07/owl#': 1
  },

  classes: {
    // Things which are inherently already undercover
    'http://www.w3.org/2007/ont/link#ProtocolEvent': 1
  }
}

function isProtectedUri (subject: NamedNode): boolean {
  // TODO: Could make the code below smarter by removing some of the redundancy by creating a recursive function, but did not bother now
  const siteUri = subject.site().uri
  return (
    subject.uri === siteUri ||
    subject.uri === siteUri + 'profile/' ||
    subject.uri === siteUri + 'profile/card' ||
    subject.uri === siteUri + 'settings/' ||
    subject.uri === siteUri + 'settings/prefs.ttl' ||
    subject.uri === siteUri + 'settings/privateTypeIndex.ttl' ||
    subject.uri === siteUri + 'settings/publicTypeIndex.ttl' ||
    subject.uri === siteUri + 'settings/serverSide.ttl'
  )
}

export default pane

// ends