Source: constructors/ClientItem.js

/**
 * This file is part of Sesatheque.
 *   Copyright 2014-2015, Association Sésamath
 *
 * Sesatheque is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation.
 *
 * Sesatheque is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Sesatheque (LICENCE.txt).
 * @see http://www.gnu.org/licenses/agpl.txt
 *
 *
 * Ce fichier fait partie de l'application Sésathèque, créée par l'association Sésamath.
 *
 * Sésathèque est un logiciel libre ; vous pouvez le redistribuer ou le modifier suivant
 * les termes de la GNU Affero General Public License version 3 telle que publiée par la
 * Free Software Foundation.
 * Sésathèque est distribué dans l'espoir qu'il sera utile, mais SANS AUCUNE GARANTIE,
 * sans même la garantie tacite de QUALITÉ MARCHANDE ou d'ADÉQUATION à UN BUT PARTICULIER.
 * Consultez la GNU Affero General Public License pour plus de détails.
 * Vous devez avoir reçu une copie de la GNU General Public License en même temps que Sésathèque
 * (cf LICENCE.txt et http://vvlibri.org/fr/Analyse/gnu-affero-general-public-license-v3-analyse
 * pour une explication en français)
 */
'use strict'

import { getBaseUrl, getRidComponents } from '../sesatheques'
import Ref from './Ref'
import { constantes } from '../config'
import tools from 'sesajstools'
const { hasProp } = tools

/**
 * Définition d'un ClientItem. C'est une Ressource avec des attributs en moins
 * (idem Ref où aliasOf est remplacé par rid)
 * et d'autres volatiles en plus ($droits et les $xxxUrl).
 * Il est à priori construit coté client, ce n'est pas une entité
 * @param {Object} [values={}] L'objet qui sert à initialiser un nouvel objet Ref, accepte une Ressource
 * @throws {Error} Si on passe des enfants sur un type non arbre
 * @constructor
 */
export default function ClientItem (values) {
  if (typeof values !== 'object') values = {}
  // on commence par initialiser avec une ref
  Object.assign(this, new Ref(values))

  // les erreurs sont un type particulier minimaliste auquel on ajoute rien
  if (this.type === 'error') return { type: 'error', titre: this.titre }

  // idem Ref où aliasOf est remplacé par rid
  // le constructeur Ref se charge d'init aliasRid ou pas

  /**
   * Rid de la ressource qu'on représente
   */
  this.rid = this.aliasOf
  delete this.aliasOf
  // pour la doc faut ajouter tout ça
  /**
   * Rid de l'alias éventuel que l'on représente
   * @property aliasRid
   * @type {string}
   */
  /**
   * Titre
   * @property titre
   * @type {string}
   */
  /**
   * Résumé (pour tous)
   * @property resume
   * @type {string}
   */
  /**
   * Description (pour tous)
   * @property description
   * @type {string}
   */
  /**
   * Commentaires (pour le formateur)
   * @property commentaires
   * @type {string}
   */
  /**
   * Le type qui permet de savoir à quel type de contenu s'attendre, ou quel picto afficher
   * @property type
   * @type {string}
   */
  /**
   * Un ou des id de catégorie(s) éventuel (pour un picto)
   * @property categories
   * @type {number[]}
   */
  /**
   * True si public (sinon il faut être authentifié pour lire la ressource)
   * @property public
   * @type {boolean}
   */
  /**
   * Éventuelle clé de lecture, pour que des élèves puissent lire
   * la ressource non publique si leur prof la leur affecte
   * @property cle
   * @type {string}
   */
  /**
   * true si la ressource privée est partagée avec un ou des groupes
   * @property partage
   * @type {boolean}
   * @default undefined
   */
  /**
   * Un incrément ajouté à l'url pour contourner le cache du navigateur en cas de modif
   * @property inc
   * @type number
   */

  // on passe aux routes
  // si on est issu d'un alias, on a rid (la ressource) et aliasRid (l'alias),
  // dans ce cas c'est l'original qui nous intéresse,
  // sauf pour le delete où on veut effacer l'alias
  // attention, on peut être un item sans rid (qui n'a pas de ressource équivalente,
  // un simple dossier dans un arbre)
  if (this.rid) {
    // => on a des urls à mettre
    const [originalBaseId, oid] = getRidComponents(this.rid)
    const originalBaseUrl = getBaseUrl(originalBaseId)
    const publicSuffix = this.inc ? `?inc=${this.inc}` : ''
    const { describe, display, edit } = constantes.routes

    // $displayUrl et $describeUrl
    if (this.public) {
      /**
       * Url absolue pour afficher la ressource
       * @type {string}
       */
      this.$displayUrl = `${originalBaseUrl}public/${display}/${oid}${publicSuffix}`
      /**
       * Url absolue de description de la ressource
       * @type {string}
       */
      this.$describeUrl = `${originalBaseUrl}ressource/${describe}/${oid}${publicSuffix}`
    } else if (values.cle) {
      this.$displayUrl = `${originalBaseUrl}public/${display}/cle/${values.cle}${publicSuffix}`
      // pas de clé pour describe, un élève se prendra une 403 (mais il devrait pas avoir le lien)
      // (idem pour un prof qui n'aurait pas les droits)
      this.$describeUrl = `${originalBaseUrl}ressource/${describe}/${oid}`
    } else {
      // privée sans clé de lecture, un utilisateur élève se prendrait une 404,
      // mais ça ne devrait pas arriver car la clé est générée au beforeStore
      // On laisse pour le cas où la ressource viendrait de passer privée
      // et n'aurait pas encore été sauvegardée.
      this.$displayUrl = `${originalBaseUrl}ressource/${display}/${oid}`
      this.$describeUrl = `${originalBaseUrl}ressource/${describe}/${oid}`
    }
    // $dataUrl
    if (this.public) {
      /**
       * Url absolue du json de la ressource
       * @type {string}
       */
      this.$dataUrl = `${originalBaseUrl}api/public/${oid}${publicSuffix}`
    } else {
      this.$dataUrl = `${originalBaseUrl}api/ressource/${oid}`
    }

    if (values.$droits) {
      // $editUrl
      if (values.$droits.includes('W')) {
        let editBaseUrl
        let editId
        if (values.aliasRid) {
          // c'est une ressource alias, avec aliasOf ET aliasRid, effacement et édition concerne l'alias
          // (il sera cloné par la sésathèque à la 1re édition pour devenir une ressource à part entière)
          const [aliasBaseId, aliasId] = getRidComponents(values.aliasRid)
          editBaseUrl = getBaseUrl(aliasBaseId)
          editId = aliasId
        } else {
          editBaseUrl = originalBaseUrl
          editId = oid
        }
        /**
         * Url absolue d'édition (propriété présente quand on sait que l'utilisateur courant a les droits)
         * @type {string}
         */
        this.$editUrl = `${editBaseUrl}ressource/${edit}/${editId}`

        // $deleteUrl n'existe jamais sans $editUrl, on récupère donc la base du dessus
        if (values.$droits.includes('D')) {
          /**
           * Url absolue de suppression via l'api (si l'utilisateur courant a les droits de suppression)
           * propriété absente si on en sait rien
           * @type {string}
           */
          this.$deleteUrlApi = `${editBaseUrl}api/ressource/${editId}`
        }
      }
    }
  } else if (this.type !== 'arbre') {
    // aliasOf est obligatoire, sauf pour error (éliminé au départ) et arbre
    // (dans ce cas cet item est un simple dossier et pas une ressource)
    throw Error('Ressource invalide')
  }

  // reste les éventuels enfants que l'on normalise aussi comme ClientItem[]
  if (Array.isArray(this.enfants)) {
    /**
     * Liste des enfants (tous normalisés)
     * @type {ClientItem[]}
     */
    if (this.enfants.length) this.enfants = this.enfants.map(e => new ClientItem(e))
  } else if (hasProp(this, 'enfants')) {
    console.error(`propriété enfants invalide pour ${this.rid}`, this.enfants)
    delete this.enfants
  }

  // Pour les paramètres, on laisse le constructeur de Ref décider
  /**
   * Les paramètres éventuels (à priori pour le type sequenceModele),
   * forcément un plain Object s'il existe
   * @property parametres
   * @type {Object}
   */
}

/**
 * Cast en string d'une ref (son titre avec aliasOf)
 * @returns {string}
 */
ClientItem.prototype.toString = function () {
  if (this.aliasRid) return `${this.titre} (${this.rid} => ${this.aliasRid})`
  return `${this.titre} (${this.rid})`
}