<commit_before>var app = angular.module('mergely', []);

app.directive('mergelyEditor', function() {
  return {
    restrict: 'E',
    replace: true,
    template: '

',
    scope: {
      files: '=',
      mergeFiles: '=',
      complete: '='
    },
    link: function($scope, element) {
      $scope.lset = null;
      $scope.rset = null;

      // Make list of filenames
      var filenames = Object.keys($scope.files);
      for (var f in $scope.mergeFiles) {
        filenames.push(f);
      }

      // This function is called once per file
      var file_index = 0;
      var nextFile = function() {
        var lcontent = $scopes.files;
        file_index++;
      }

      // We need to wait until lset and rset have been set
      var set_sets = 0;
      var set_set = function() {
        set_sets++;
        if (set_sets == 2) {
          // lset and rset set
          nextFile();
        }
      };

      $('#mergely-editor').mergely({
        cmsettings: { readOnly: false, lineNumbers: true },
        lhs: function(setValue) {
          $scope.lset = setValue;
          set_set();
        },
        rhs: function(setValue) {
          $scope.rset = setValue;
          set_set();
        }
      });
    }
  }
});
<commit_msg>Takes files into account<commit_after>var app = angular.module('mergely', []);

app.directive('mergelyEditor', function() {
  return {
    restrict: 'E',
    replace: true,
    template: '

',
    scope: {
      files: '=',
      mergeFiles: '=',
      complete: '='
    },
    link: function($scope, element) {
      var file = undefined;

      var setFiles = function(files, set) {
        if (Object.keys(files).length) {
          if (file === undefined) {
            file = Object.keys(files)[0];
          }
        }

        var content = '';
        if (file in files) {
          content = '' + files[file];
        }

        console.log('setting ', file, content);
        $('#mergely-editor').mergely(set, content);

        if ($scope.files && $scope.mergeFiles) {
          for (var k in $scope.files) {
            if ($scope.files[k] !== $scope.mergeFiles) {
              console.log(k, 'versions differ');
            } else {
              console.log(k, 'versions same');
            }
          }
        }
      };

      $scope.$watch('files', function(files) {
        if (files) {
          setFiles(files, 'lhs');
        }
      });

      $scope.$watch('mergeFiles', function(files) {
        if (files) {
          setFiles(files, 'rhs');
        }
      });

      $('#mergely-editor').mergely({
        cmsettings: { readOnly: false, lineNumbers: true },
      });
    }
  }
});
<|endoftext|>


<commit_before>// Meyda Javascript DSP library

var Meyda = function(audioContext,source,bufferSize){
	//add some utilities to array prototype
	Float32Array.prototype.meanValue = function() {
		var sum = 0;
		for(var i = 0; i < this.length; i++){
		    sum += parseInt(this[i], 10);
		}

		return sum/this.length;
	};

	var self = this;

	self.featureExtractors = {
		"rms": function(bufferSize, m, spectrum, signal){
			var rms = 0;
			for(var i = 0 ; i < signal.length ; i++){
				rms += Math.pow(signal[i],2);
			}
			rms = Math.sqrt(rms);
			return rms;
		},
		"energy": function(bufferSize, m, spectrum, signal) {
			var energy = 0;
			for(var i = 0 ; i < signal.length ; i++){
				energy += Math.pow(Math.abs(signal[i]),2);
			}
			return energy;
		},
		"spectrum": function(bufferSize, m, spectrum) {
			return spectrum;
		},
		"spectralSlope": function(bufferSize, m, spectrum) {
			//linear regression
			var x = 0.0, y = 0.0, xy = 0.0, x2 = 0.0;
			for (var i = 0; i < spectrum.length; i++) {
				y += spectrum[i];
				xy += spectrum[i] * i;
				x2 += i*i;
			};

			x = spectrum.length/2;
			y /= spectrum.length;
			xy /= spectrum.length;
			x2 /= spectrum.length;

			return (x*y - xy)/(x*x - x2);
		},
		"spectralCentroid": function(bufferSize, m, spectrum){
			var magspec = m.featureExtractors.amplitudeSpectrum(bufferSize, m, spectrum);
			var numerator = 0;
			var denominator = 0;
			for(var i = 0; i < bufferSize-1; i++){
				numerator += magspec[i]*i*audioContext.sampleRate/bufferSize
				denominator += i*audioContext.sampleRate/bufferSize
			}
			return numerator/denominator;
		},
		"spectralRolloff": function(bufferSize, m, spectrum){
			var magspec = m.featureExtractors.amplitudeSpectrum(bufferSize, m, spectrum);
			var ec = 0;
			for(var i = 0; i < magspec.length; i++){
				ec += magspec[i];
			}
			var threshold = 0.99 * ec;
			var n = magspec.length - 1;
			while(ec > threshold && n >= 0){
				ec -= magspec[n];
            	n--;
			}
			return ec;
		},
		"spectralFlatness": function(bufferSize, m, spectrum){
			var powspec = m.featureExtractors.powerSpectrum(bufferSize, m, spectrum);
			// console.log(powspec);
			var numerator = 0;
			var denominator = 0;
			for(var i = 0; i < powspec.length-1;i++){
				numerator += Math.log(powspec[i]);
				denominator += powspec[i];
			}
			return Math.exp((1/powspec.length)*numerator)/((1/powspec.length)*denominator);
		},
		"amplitudeSpectrum": function(bufferSize, m, spectrum){
			var ampRatioSpectrum = new Float32Array(bufferSize);
			for (var i = 0; i < spectrum.length; i++) {
				ampRatioSpectrum[i] =  Math.pow(10,spectrum[i]/20);

			}
			return ampRatioSpectrum;
		},
		"zcr": function(bufferSize, m, spectrum, signal){
			var zcr = 0;
			for(var i = 0; i < signal.length; i++){
				if((signal[i] >= 0 && signal[i+1] < 0) || (signal[i] < 0 && signal[i+1] >= 0)){
					zcr++;
				}
			}
			return zcr;
		},
		"powerSpectrum": function(bufferSize, m, spectrum){
			var powerRatioSpectrum = new Float32Array(bufferSize);
			for (var i = 0; i < spectrum.length; i++) {
				powerRatioSpectrum[i] =  Math.pow(10,spectrum[i]/10);

			}
			return powerRatioSpectrum;
		}
		"loudness": function(bufferSize, m, spectrum){

			var barkScale = Float32Array(bufferSize);
			var NUM_BARK_BANDS = 24;
			var spec = Float32Array(NUM_BARK_BANDS);
			var tot = 0;
			var normalisedSpectrum = m.featureExtractors["normalisedSpectrum"](bufferSize, m, spectrum);

			for(var i = 0; i < barkScale.length; i++){
				barkScale[i] = i*m.audioContext.sampleRate/(bufferSize);
				barkScale[i] = 13*Math.atan(barkScale[i]/1315.8) + 3.5* Math.atan(Math.pow(barkScale[i]/7518,2));
			}

			// console.log("bark: ", barkScale);
			var bbLimits = [0];
			var currentBandEnd = barkScale[bufferSize-1]/NUM_BARK_BANDS;
			var currentBand = 1;
			for(var i = 0; i<bufferSize; i++){
				while(barkScale[i] > currentBandEnd){
					bbLimits[currentBand] = i;
					currentBand++;
					currentBandEnd = (currentBand*barkScale[bufferSize-1])/NUM_BARK_BANDS;
				}
			}
			// console.log("bbLimits", bbLimits);
			bbLimits[NUM_BARK_BANDS] = bufferSize-1;

			for (var i = 1; i <= NUM_BARK_BANDS; i++){
				var sum = 0;
				for (var j = bbLimits[i-1] ; j < bbLimits[i] ; j++) {

					sum += normalisedSpectrum[j];
				}
				spec[i] = Math.pow(sum,0.23);
			}

			for (var i = 0; i < spec.length; i++){
				tot += spec[i];
			}

			return {
				specific: spec,
				total: tot
			};
		},
		"perceptualSpread": function(bufferSize, m, spectrum) {
			var loudness = m.featureExtractors["loudness"](bufferSize, m, spectrum);

			var max = 0;
			for (var i=0; i<loudness.specific.length; i++) {
				if (loudness.specific[i] > max) {
					max = loudness.specific[i];
				}
			}

			var spread = Math.pow((loudness.total - max)/loudness.total, 2);

			return spread;
		},
		"perceptualSharpness": function(bufferSize,m,spectrum) {
			var loudness = m.featureExtractors["loudness"](bufferSize, m, spectrum);
			var spec = loudness.specific;
			var output = 0;

			for (var i = 0; i < spec.length; i++) {
				if (i < 15) {
					output += (i+1) * spec[i+1];
				}
				else {
					output += 0.066 * Math.exp(0.171 * (i+1));
				}
			};
			output *= 0.11/loudness.total;

			return output;
		}
	}
	//create nodes
	self.analyser = audioContext.createAnalyser();
	self.analyser.fftSize = bufferSize;
	self.audioContext = audioContext;

	self.get = function(feature) {

		var spectrum = new Float32Array(bufferSize/2);
		self.analyser.getFloatFrequencyData(spectrum);

		var signal = new Float32Array(bufferSize);
		self.analyser.getFloatTimeDomainData(signal);

		if(typeof feature === "object"){
			var results = new Array();
			for (var x = 0; x < feature.length; x++){
				results.push(self.featureExtractors[feature[x]](bufferSize, self, spectrum, signal));
			}
			return results;
		}
		else if (typeof feature === "string"){
			return self.featureExtractors[feature](bufferSize, self, spectrum, signal);
		}
		else{
			throw "Invalid Feature Format";
		}
	}
	source.connect(self.analyser);
	return self;
}
<commit_msg>fixed small error in master (missing comma)<commit_after>// Meyda Javascript DSP library

var Meyda = function(audioContext,source,bufferSize){
	//add some utilities to array prototype
	Float32Array.prototype.meanValue = function() {
		var sum = 0;
		for(var i = 0; i < this.length; i++){
		    sum += parseInt(this[i], 10);
		}

		return sum/this.length;
	};

	var self = this;

	self.featureExtractors = {
		"rms": function(bufferSize, m, spectrum, signal){
			var rms = 0;
			for(var i = 0 ; i < signal.length ; i++){
				rms += Math.pow(signal[i],2);
			}
			rms = Math.sqrt(rms);
			return rms;
		},
		"energy": function(bufferSize, m, spectrum, signal) {
			var energy = 0;
			for(var i = 0 ; i < signal.length ; i++){
				energy += Math.pow(Math.abs(signal[i]),2);
			}
			return energy;
		},
		"spectrum": function(bufferSize, m, spectrum) {
			return spectrum;
		},
		"spectralSlope": function(bufferSize, m, spectrum) {
			//linear regression
			var x = 0.0, y = 0.0, xy = 0.0, x2 = 0.0;
			for (var i = 0; i < spectrum.length; i++) {
				y += spectrum[i];
				xy += spectrum[i] * i;
				x2 += i*i;
			};

			x = spectrum.length/2;
			y /= spectrum.length;
			xy /= spectrum.length;
			x2 /= spectrum.length;

			return (x*y - xy)/(x*x - x2);
		},
		"spectralCentroid": function(bufferSize, m, spectrum){
			var magspec = m.featureExtractors.amplitudeSpectrum(bufferSize, m, spectrum);
			var numerator = 0;
			var denominator = 0;
			for(var i = 0; i < bufferSize-1; i++){
				numerator += magspec[i]*i*audioContext.sampleRate/bufferSize
				denominator += i*audioContext.sampleRate/bufferSize
			}
			return numerator/denominator;
		},
		"spectralRolloff": function(bufferSize, m, spectrum){
			var magspec = m.featureExtractors.amplitudeSpectrum(bufferSize, m, spectrum);
			var ec = 0;
			for(var i = 0; i < magspec.length; i++){
				ec += magspec[i];
			}
			var threshold = 0.99 * ec;
			var n = magspec.length - 1;
			while(ec > threshold && n >= 0){
				ec -= magspec[n];
            	n--;
			}
			return ec;
		},
		"spectralFlatness": function(bufferSize, m, spectrum){
			var powspec = m.featureExtractors.powerSpectrum(bufferSize, m, spectrum);
			// console.log(powspec);
			var numerator = 0;
			var denominator = 0;
			for(var i = 0; i < powspec.length-1;i++){
				numerator += Math.log(powspec[i]);
				denominator += powspec[i];
			}
			return Math.exp((1/powspec.length)*numerator)/((1/powspec.length)*denominator);
		},
		"amplitudeSpectrum": function(bufferSize, m, spectrum){
			var ampRatioSpectrum = new Float32Array(bufferSize);
			for (var i = 0; i < spectrum.length; i++) {
				ampRatioSpectrum[i] =  Math.pow(10,spectrum[i]/20);

			}
			return ampRatioSpectrum;
		},
		"zcr": function(bufferSize, m, spectrum, signal){
			var zcr = 0;
			for(var i = 0; i < signal.length; i++){
				if((signal[i] >= 0 && signal[i+1] < 0) || (signal[i] < 0 && signal[i+1] >= 0)){
					zcr++;
				}
			}
			return zcr;
		},
		"powerSpectrum": function(bufferSize, m, spectrum){
			var powerRatioSpectrum = new Float32Array(bufferSize);
			for (var i = 0; i < spectrum.length; i++) {
				powerRatioSpectrum[i] =  Math.pow(10,spectrum[i]/10);

			}
			return powerRatioSpectrum;
		},
		"loudness": function(bufferSize, m, spectrum){

			var barkScale = Float32Array(bufferSize);
			var NUM_BARK_BANDS = 24;
			var spec = Float32Array(NUM_BARK_BANDS);
			var tot = 0;
			var normalisedSpectrum = m.featureExtractors["normalisedSpectrum"](bufferSize, m, spectrum);

			for(var i = 0; i < barkScale.length; i++){
				barkScale[i] = i*m.audioContext.sampleRate/(bufferSize);
				barkScale[i] = 13*Math.atan(barkScale[i]/1315.8) + 3.5* Math.atan(Math.pow(barkScale[i]/7518,2));
			}

			// console.log("bark: ", barkScale);
			var bbLimits = [0];
			var currentBandEnd = barkScale[bufferSize-1]/NUM_BARK_BANDS;
			var currentBand = 1;
			for(var i = 0; i<bufferSize; i++){
				while(barkScale[i] > currentBandEnd){
					bbLimits[currentBand] = i;
					currentBand++;
					currentBandEnd = (currentBand*barkScale[bufferSize-1])/NUM_BARK_BANDS;
				}
			}
			// console.log("bbLimits", bbLimits);
			bbLimits[NUM_BARK_BANDS] = bufferSize-1;

			for (var i = 1; i <= NUM_BARK_BANDS; i++){
				var sum = 0;
				for (var j = bbLimits[i-1] ; j < bbLimits[i] ; j++) {

					sum += normalisedSpectrum[j];
				}
				spec[i] = Math.pow(sum,0.23);
			}

			for (var i = 0; i < spec.length; i++){
				tot += spec[i];
			}

			return {
				specific: spec,
				total: tot
			};
		},
		"perceptualSpread": function(bufferSize, m, spectrum) {
			var loudness = m.featureExtractors["loudness"](bufferSize, m, spectrum);

			var max = 0;
			for (var i=0; i<loudness.specific.length; i++) {
				if (loudness.specific[i] > max) {
					max = loudness.specific[i];
				}
			}

			var spread = Math.pow((loudness.total - max)/loudness.total, 2);

			return spread;
		},
		"perceptualSharpness": function(bufferSize,m,spectrum) {
			var loudness = m.featureExtractors["loudness"](bufferSize, m, spectrum);
			var spec = loudness.specific;
			var output = 0;

			for (var i = 0; i < spec.length; i++) {
				if (i < 15) {
					output += (i+1) * spec[i+1];
				}
				else {
					output += 0.066 * Math.exp(0.171 * (i+1));
				}
			};
			output *= 0.11/loudness.total;

			return output;
		}
	}
	//create nodes
	self.analyser = audioContext.createAnalyser();
	self.analyser.fftSize = bufferSize;
	self.audioContext = audioContext;

	self.get = function(feature) {

		var spectrum = new Float32Array(bufferSize/2);
		self.analyser.getFloatFrequencyData(spectrum);

		var signal = new Float32Array(bufferSize);
		self.analyser.getFloatTimeDomainData(signal);

		if(typeof feature === "object"){
			var results = new Array();
			for (var x = 0; x < feature.length; x++){
				results.push(self.featureExtractors[feature[x]](bufferSize, self, spectrum, signal));
			}
			return results;
		}
		else if (typeof feature === "string"){
			return self.featureExtractors[feature](bufferSize, self, spectrum, signal);
		}
		else{
			throw "Invalid Feature Format";
		}
	}
	source.connect(self.analyser);
	return self;
}
<|endoftext|>


<commit_before>import asyncIteratorToStream from 'async-iterator-to-stream'
import createLogger from '@xen-orchestra/log'
import { alteredAuditRecord, missingAuditRecord } from 'xo-common/api-errors'
import { createGzip } from 'zlib'
import { fromCallback } from 'promise-toolbox'
import { pipeline } from 'readable-stream'
import {
  AlteredRecordError,
  AuditCore,
  MissingRecordError,
  NULL_ID,
  Storage,
} from '@xen-orchestra/audit-core'

const log = createLogger('xo:xo-server-audit')

const DEFAULT_BLOCKED_LIST = {
  'acl.get': true,
  'acl.getCurrentPermissions': true,
  'audit.checkIntegrity': true,
  'audit.generateFingerprint': true,
  'audit.getRecords': true,
  'backupNg.getAllJobs': true,
  'backupNg.getAllLogs': true,
  'cloud.getResourceCatalog': true,
  'cloudConfig.getAll': true,
  'group.getAll': true,
  'host.stats': true,
  'ipPool.getAll': true,
  'job.getAll': true,
  'log.get': true,
  'metadataBackup.getAllJobs': true,
  'plugin.get': true,
  'pool.listMissingPatches': true,
  'proxy.getAll': true,
  'remote.getAll': true,
  'remote.getAllInfo': true,
  'resourceSet.getAll': true,
  'role.getAll': true,
  'schedule.getAll': true,
  'server.getAll': true,
  'session.getUser': true,
  'session.signIn': true,
  'sr.getUnhealthyVdiChainsLength': true,
  'sr.stats': true,
  'system.getMethodsInfo': true,
  'system.getServerTimezone': true,
  'system.getServerVersion': true,
  'user.getAll': true,
  'vm.stats': true,
  'xo.getAllObjects': true,
  'xoa.getApplianceInfo': true,
  'xoa.supportTunnel.getState': true,
  'xosan.checkSrCurrentState': true,
  'xosan.getVolumeInfo': true,
}

const LAST_ID = 'lastId'
class Db extends Storage {
  constructor(db) {
    super()
    this._db = db
  }

  async put(record) {
    await this._db.put(record.id, record)
  }

  async del(id) {
    await this._db.del(id)
  }

  get(id) {
    return this._db.get(id).catch(error => {
      if (!error.notFound) {
        throw error
      }
    })
  }

  async setLastId(id) {
    await this._db.put(LAST_ID, id)
  }

  getLastId() {
    return this.get(LAST_ID)
  }
}

const NAMESPACE = 'audit'
class AuditXoPlugin {
  constructor({ staticConfig, xo }) {
    this._blockedList = {
      ...DEFAULT_BLOCKED_LIST,
      ...staticConfig.blockedList,
    }
    this._cleaners = []
    this._xo = xo

    this._auditCore = undefined
    this._storage = undefined
  }

  async load() {
    const storage = (this._storage = new Db(await this._xo.getStore(NAMESPACE)))
    this._auditCore = new AuditCore(storage)

    const cleaners = this._cleaners
    cleaners.push(() => {
      this._auditCore = undefined
      this._storage = undefined
    })

    this._addListener('xo:postCall', this._handleEvent.bind(this, 'apiCall'))
    this._addListener('xo:audit', this._handleEvent.bind(this))

    const exportRecords = this._exportRecords.bind(this)
    exportRecords.permission = 'admin'

    const getRecords = this._getRecords.bind(this)
    getRecords.description = 'Get records from a passed record ID'
    getRecords.permission = 'admin'
    getRecords.params = {
      id: { type: 'string', optional: true },
      ndjson: { type: 'boolean', optional: true },
    }

    const checkIntegrity = this._checkIntegrity.bind(this)
    checkIntegrity.description =
      'Check records integrity between oldest and newest'
    checkIntegrity.permission = 'admin'
    checkIntegrity.params = {
      newest: { type: 'string', optional: true },
      oldest: { type: 'string', optional: true },
    }

    const generateFingerprint = this._generateFingerprint.bind(this)
    generateFingerprint.description =
      'Generate a fingerprint of the chain oldest-newest'
    generateFingerprint.permission = 'admin'
    generateFingerprint.params = {
      newest: { type: 'string', optional: true },
      oldest: { type: 'string', optional: true },
    }

    cleaners.push(
      this._xo.addApiMethods({
        audit: {
          checkIntegrity,
          exportRecords,
          generateFingerprint,
          getRecords,
        },
      })
    )
  }

  unload() {
    this._cleaners.forEach(cleaner => cleaner())
    this._cleaners.length = 0
  }

  _addListener(event, listener_) {
    const listener = async (...args) => {
      try {
        await listener_(...args)
      } catch (error) {
        log.error(error)
      }
    }
    const xo = this._xo
    xo.on(event, listener)
    this._cleaners.push(() => xo.removeListener(event, listener))
  }

  _handleEvent(event, { userId, userIp, userName, ...data }) {
    if (event !== 'apiCall' || !this._blockedList[data.method]) {
      return this._auditCore.add(
        {
          userId,
          userIp,
          userName,
        },
        event,
        data
      )
    }
  }

  async _getRecords({ id, ndjson = false }) {
    if (ndjson) {
      return this._xo
        .registerHttpRequest((req, res) => {
          res.set('Content-Type', 'application/json')
          return fromCallback(pipeline, this._getRecordsStream(id), res)
        })
        .then($getFrom => ({
          $getFrom,
        }))
    }

    const records = []
    for await (const record of this._auditCore.getFrom(id)) {
      records.push(record)
    }
    return records
  }

  _exportRecords() {
    return this._xo
      .registerHttpRequest(
        (req, res) => {
          res.set({
            'content-disposition': 'attachment',
            'content-type': 'application/json',
          })
          return fromCallback(
            pipeline,
            this._getRecordsStream(),
            createGzip(),
            res
          )
        },
        undefined,
        {
          suffix: `/audit-records-${new Date()
            .toISOString()
            .replace(/:/g, '_')}.gz`,
        }
      )
      .then($getFrom => ({
        $getFrom,
      }))
  }

  async _checkIntegrity(props) {
    const { oldest = NULL_ID, newest = await this._storage.getLastId() } = props
    return this._auditCore.checkIntegrity(oldest, newest).catch(error => {
      if (error instanceof MissingRecordError) {
        throw missingAuditRecord(error)
      }
      if (error instanceof AlteredRecordError) {
        throw alteredAuditRecord(error)
      }
      throw error
    })
  }

  async _generateFingerprint(props) {
    const { oldest = NULL_ID, newest = await this._storage.getLastId() } = props
    try {
      return {
        fingerprint: `${oldest}|${newest}`,
        nValid: await this._checkIntegrity({ oldest, newest }),
      }
    } catch (error) {
      if (missingAuditRecord.is(error) || alteredAuditRecord.is(error)) {
        return {
          fingerprint: `${error.data.id}|${newest}`,
          nValid: error.data.nValid,
          error,
        }
      }
      throw error
    }
  }
}

AuditXoPlugin.prototype._getRecordsStream = asyncIteratorToStream(
  async function* (id) {
    for await (const record of this._auditCore.getFrom(id)) {
      yield JSON.stringify(record)
      yield '\n'
    }
  }
)

export default opts => new AuditXoPlugin(opts)
<commit_msg>feat(xo-server-audit): ignore common methods without side effects (#5109)<commit_after>import asyncIteratorToStream from 'async-iterator-to-stream'
import createLogger from '@xen-orchestra/log'
import { alteredAuditRecord, missingAuditRecord } from 'xo-common/api-errors'
import { createGzip } from 'zlib'
import { fromCallback } from 'promise-toolbox'
import { pipeline } from 'readable-stream'
import {
  AlteredRecordError,
  AuditCore,
  MissingRecordError,
  NULL_ID,
  Storage,
} from '@xen-orchestra/audit-core'

const log = createLogger('xo:xo-server-audit')

const DEFAULT_BLOCKED_LIST = {
  'acl.get': true,
  'acl.getCurrentPermissions': true,
  'audit.checkIntegrity': true,
  'audit.generateFingerprint': true,
  'audit.getRecords': true,
  'backup.list': true,
  'backupNg.getAllJobs': true,
  'backupNg.getAllLogs': true,
  'backupNg.listVmBackups': true,
  'cloud.getResourceCatalog': true,
  'cloudConfig.getAll': true,
  'group.getAll': true,
  'host.isHostServerTimeConsistent': true,
  'host.isHyperThreadingEnabled': true,
  'host.stats': true,
  'ipPool.getAll': true,
  'job.getAll': true,
  'log.get': true,
  'metadataBackup.getAllJobs': true,
  'network.getBondModes': true,
  'pif.getIpv4ConfigurationModes': true,
  'plugin.get': true,
  'pool.listMissingPatches': true,
  'proxy.getAll': true,
  'remote.getAll': true,
  'remote.getAllInfo': true,
  'remote.list': true,
  'resourceSet.getAll': true,
  'role.getAll': true,
  'schedule.getAll': true,
  'server.getAll': true,
  'session.getUser': true,
  'session.signIn': true,
  'sr.getUnhealthyVdiChainsLength': true,
  'sr.stats': true,
  'system.getMethodsInfo': true,
  'system.getServerTimezone': true,
  'system.getServerVersion': true,
  'user.getAll': true,
  'vm.getHaValues': true,
  'vm.stats': true,
  'xo.getAllObjects': true,
  'xoa.getApplianceInfo': true,
  'xoa.licenses.get': true,
  'xoa.licenses.getAll': true,
  'xoa.licenses.getSelf': true,
  'xoa.supportTunnel.getState': true,
  'xosan.checkSrCurrentState': true,
  'xosan.computeXosanPossibleOptions': true,
  'xosan.getVolumeInfo': true,
}

const LAST_ID = 'lastId'
class Db extends Storage {
  constructor(db) {
    super()
    this._db = db
  }

  async put(record) {
    await this._db.put(record.id, record)
  }

  async del(id) {
    await this._db.del(id)
  }

  get(id) {
    return this._db.get(id).catch(error => {
      if (!error.notFound) {
        throw error
      }
    })
  }

  async setLastId(id) {
    await this._db.put(LAST_ID, id)
  }

  getLastId() {
    return this.get(LAST_ID)
  }
}

const NAMESPACE = 'audit'
class AuditXoPlugin {
  constructor({ staticConfig, xo }) {
    this._blockedList = {
      ...DEFAULT_BLOCKED_LIST,
      ...staticConfig.blockedList,
    }
    this._cleaners = []
    this._xo = xo

    this._auditCore = undefined
    this._storage = undefined
  }

  async load() {
    const storage = (this._storage = new Db(await this._xo.getStore(NAMESPACE)))
    this._auditCore = new AuditCore(storage)

    const cleaners = this._cleaners
    cleaners.push(() => {
      this._auditCore = undefined
      this._storage = undefined
    })

    this._addListener('xo:postCall', this._handleEvent.bind(this, 'apiCall'))
    this._addListener('xo:audit', this._handleEvent.bind(this))

    const exportRecords = this._exportRecords.bind(this)
    exportRecords.permission = 'admin'

    const getRecords = this._getRecords.bind(this)
    getRecords.description = 'Get records from a passed record ID'
    getRecords.permission = 'admin'
    getRecords.params = {
      id: { type: 'string', optional: true },
      ndjson: { type: 'boolean', optional: true },
    }

    const checkIntegrity = this._checkIntegrity.bind(this)
    checkIntegrity.description =
      'Check records integrity between oldest and newest'
    checkIntegrity.permission = 'admin'
    checkIntegrity.params = {
      newest: { type: 'string', optional: true },
      oldest: { type: 'string', optional: true },
    }

    const generateFingerprint = this._generateFingerprint.bind(this)
    generateFingerprint.description =
      'Generate a fingerprint of the chain oldest-newest'
    generateFingerprint.permission = 'admin'
    generateFingerprint.params = {
      newest: { type: 'string', optional: true },
      oldest: { type: 'string', optional: true },
    }

    cleaners.push(
      this._xo.addApiMethods({
        audit: {
          checkIntegrity,
          exportRecords,
          generateFingerprint,
          getRecords,
        },
      })
    )
  }

  unload() {
    this._cleaners.forEach(cleaner => cleaner())
    this._cleaners.length = 0
  }

  _addListener(event, listener_) {
    const listener = async (...args) => {
      try {
        await listener_(...args)
      } catch (error) {
        log.error(error)
      }
    }
    const xo = this._xo
    xo.on(event, listener)
    this._cleaners.push(() => xo.removeListener(event, listener))
  }

  _handleEvent(event, { userId, userIp, userName, ...data }) {
    if (event !== 'apiCall' || !this._blockedList[data.method]) {
      return this._auditCore.add(
        {
          userId,
          userIp,
          userName,
        },
        event,
        data
      )
    }
  }

  async _getRecords({ id, ndjson = false }) {
    if (ndjson) {
      return this._xo
        .registerHttpRequest((req, res) => {
          res.set('Content-Type', 'application/json')
          return fromCallback(pipeline, this._getRecordsStream(id), res)
        })
        .then($getFrom => ({
          $getFrom,
        }))
    }

    const records = []
    for await (const record of this._auditCore.getFrom(id)) {
      records.push(record)
    }
    return records
  }

  _exportRecords() {
    return this._xo
      .registerHttpRequest(
        (req, res) => {
          res.set({
            'content-disposition': 'attachment',
            'content-type': 'application/json',
          })
          return fromCallback(
            pipeline,
            this._getRecordsStream(),
            createGzip(),
            res
          )
        },
        undefined,
        {
          suffix: `/audit-records-${new Date()
            .toISOString()
            .replace(/:/g, '_')}.gz`,
        }
      )
      .then($getFrom => ({
        $getFrom,
      }))
  }

  async _checkIntegrity(props) {
    const { oldest = NULL_ID, newest = await this._storage.getLastId() } = props
    return this._auditCore.checkIntegrity(oldest, newest).catch(error => {
      if (error instanceof MissingRecordError) {
        throw missingAuditRecord(error)
      }
      if (error instanceof AlteredRecordError) {
        throw alteredAuditRecord(error)
      }
      throw error
    })
  }

  async _generateFingerprint(props) {
    const { oldest = NULL_ID, newest = await this._storage.getLastId() } = props
    try {
      return {
        fingerprint: `${oldest}|${newest}`,
        nValid: await this._checkIntegrity({ oldest, newest }),
      }
    } catch (error) {
      if (missingAuditRecord.is(error) || alteredAuditRecord.is(error)) {
        return {
          fingerprint: `${error.data.id}|${newest}`,
          nValid: error.data.nValid,
          error,
        }
      }
      throw error
    }
  }
}

AuditXoPlugin.prototype._getRecordsStream = asyncIteratorToStream(
  async function* (id) {
    for await (const record of this._auditCore.getFrom(id)) {
      yield JSON.stringify(record)
      yield '\n'
    }
  }
)

export default opts => new AuditXoPlugin(opts)
<|endoftext|>


<commit_before>// ---------------------------------------
// Build a mapacache.xml file from
// a WMS capabilies xml file
// (Synchronous request)
// --------------------------------------------
// Parameters
//  - capabilitiesFile : xml capabilities
//  - urlRequestCapa : WMS getCapabilities request
//  - projectName : name of the project
// --------------------------------------------
buildMapcacheXmlFromContent = (function(capabilitiesXmlContent, urlRequestCapa, projectName) {
    var parser = new ol.parser.ogc.WMSCapabilities(), capabilitiesXmlContent;
    capabilitiesXmlContent = parser.read(capabilitiesXmlContent);

    var layers = capabilitiesXmlContent.capability.layers;

    var project = projectName;
    var mapcache = '';
    var tileset = '';
    var source = '';
    var concat = '&';
    var idx = urlRequestCapa.indexOf('?');
    if (idx == -1) {
        concat = '?';
    }

    mapcache = '<?xml version="1.0" encoding="UTF-8"?>\n';
    mapcache += '<mapcache>\n';
    mapcache += '  <metadata>\n';
    mapcache += '    <title>Mapcache service for url: ' + urlRequestCapa + '</title>\n';
    mapcache += '    <abstract>Render map services for url: ' + urlRequestCapa + '</abstract>\n';
    mapcache += '  </metadata>\n\n';

    // Cache: Disk
    mapcache += '  <cache name="disk" type="disk">\n';
    mapcache += '    <base>/tmp/' + project + '</base>\n';
    mapcache += '    <symlink_blank/>\n';
    mapcache += '  </cache>\n\n';

    // Cache: template
    mapcache += '  <cache name="tmpl" type="disk">\n';
    mapcache += '    <base>/tmp' + project + '</base>\n';
    mapcache += '    <template>/tmp/' + project + '/template/{tileset}#{grid}#{dim}/{z}/{x}/{y}.{ext}</template>\n';
    mapcache += '  </cache>\n\n';

    // Cache: sqlite
    mapcache += '  <cache name="sqlite" type="sqlite3">\n';
    mapcache += '    <dbfile>/tmp/' + project + '/sqlitetiles.db</dbfile>\n';
    mapcache += '    <pragma name="key">value</pragma>\n';
    mapcache += '  </cache>\n\n';

    // Cache: MbTiles
    mapcache += '  <cache name="mbtiles" type="mbtiles">\n';
    mapcache += '    <dbfile>/tmp/' + project + '/' + project + '.mbtiles</dbfile>\n';
    mapcache += '  </cache>\n\n';

    // Format: png
    mapcache += '  <format name="PNGQ_FAST" type ="PNG">\n';
    mapcache += '    <compression>fast</compression>\n';
    mapcache += '    <colors>256</colors>\n';
    mapcache += '  </format>\n\n';

    // Format: jpeg
    mapcache += '  <format name="JPEG_75" type ="JPEG">\n';
    mapcache += '    <quality>75</quality>\n';
    mapcache += '    <photometric>RGB</photometric>\n';
    mapcache += '  </format>\n\n';

    // Format: png
    mapcache += '  <format name="PNG_BEST" type ="PNG">\n';
    mapcache += '    <compression>best</compression>\n';
    mapcache += '  </format>\n\n';

    // Format: mixed
    mapcache += '  <format name="mixed" type="MIXED">\n';
    mapcache += '    <transparent>PNG_BEST</transparent>\n';
    mapcache += '    <opaque>JPEG</opaque>\n';
    mapcache += '  </format>\n\n';

    for (var l = 0; l < layers.length; ++l) {
        layer = layers[l];

        console.log('Layer:' + layer.name);
        console.log('\tAvailable elevations: ' + layer.dimensions.elevation.values);
        console.log('\tAvailable times: ' + layer.dimensions.time.values);

        var arrayTab = layer.styles;
        for (var s = 0; s < arrayTab.length; ++s) {
            style = arrayTab[s];

            tileset = layer.name + '_' + style.name;
            source = 'SOURCE_' + layer.name + '_' + style.name;

            mapcache += '  <!-- Source for layer: ' + layer.name + ', style: ' + style.name + ' -->\n';
            mapcache += '  <source name="' + source + '" type="wms">\n';
            mapcache += '    <http>\n';
            mapcache += '      <url>' + urlRequestCapa + '</url>\n';
            mapcache += '    </http>\n';
            mapcache += '    <getmap>\n';
            mapcache += '      <params>\n';
            mapcache += '        <FORMAT>image/png</FORMAT>\n';
            mapcache += '        <LAYERS>' + layer.name + '</LAYERS>\n';
            mapcache += '        <STYLES>' + style.name + '</STYLES>\n';
            mapcache += '      </params>\n';
            mapcache += '    </getmap>\n';
            mapcache += '  </source>\n\n';

            mapcache += '  <!-- Tileset for layer: ' + layer.name + ', style: ' + style.name + ' -->\n';
            mapcache += '  <tileset name="' + tileset + '">\n';
            mapcache += '    <source>' + source + '</source>\n';
            mapcache += '    <dimensions>\n';
            mapcache += '      <dimension type="regex" name="elevation" default="-1">.*</dimension>\n';
            mapcache += '      <dimension type="regex" name="time" default="2000-01-00T00:00:00Z">.*</dimension>\n';
            mapcache += '    </dimensions>\n';
            mapcache += '    <cache>disk</cache>\n';
            mapcache += '    <format>PNG</format>\n';

            // TODO : create grid for each srs
            //var projs = layer.srs;
            //for (var p = 0; p < projs.length; ++p) {
            //    proj = projs[p];
            //    mapcache += '    <grid>' + proj + '</grid>\n';
            //}

            mapcache += '    <grid>WGS84</grid>\n';
            mapcache += '    <grid>GoogleMapsCompatible</grid>\n';
            mapcache += '    <metatile>5 5</metatile>\n';
            mapcache += '    <metabuffer>10</metabuffer>\n';
            mapcache += '  </tileset>\n\n';

        }
    }

    // Services: wms
    mapcache += '  <service type="wms" enabled="true">\n';
    for (var l = 0; l < layers.length; ++l) {
        layer = layers[l];
        var arrayTab = layer.styles;
        for (var s = 0; s < arrayTab.length; ++s) {
            style = arrayTab[s];
            tileset = layer.name + '_' + style.name;
            source = 'SOURCE_' + layer.name + '_' + style.name;
            mapcache += '    <forwarding_rule name="RULE' + tileset + '"&gt\n';
            mapcache += '      <param name="SERVICE" type="values">WMS</param&gt\n';
            mapcache += '      <param name="LAYERS" type="values">' + layer.name + '</param&gt\n';
            mapcache += '      <param name="STYLES" type="values">' + style.name + '</param&gt\n';
            mapcache += '      <http&gt\n';
            mapcache += '        <url>' + urlRequestCapa + concat + 'LAYERS=' + tileset + '</url&gt\n';
            mapcache += '      </http&gt\n';
            mapcache += '    </forwarding_rule&gt\n';

        }
    }
    mapcache += '    <full_wms>assemble</full_wms&gt\n';
    mapcache += '    <resample_mode>bilinear</resample_mode&gt\n';
    mapcache += '    <format>JPEG_75</format&gt\n';
    mapcache += '    <maxsize>4096</maxsize&gt\n';
    mapcache += '  </service&gt\n\n';

    // Services: wmts, tms, gmaps, ve, demo
    mapcache += '  <service type="wmts" enabled="true"/>\n';
    mapcache += '  <service type="tms" enabled="true"/>\n';
    mapcache += '  <service type="kml" enabled="true"/>\n';
    mapcache += '  <service type="gmaps" enabled="true"/>\n';
    mapcache += '  <service type="ve" enabled="true"/>\n';
    mapcache += '  <service type="demo" enabled="true"/>\n\n';

    // General parameters
    mapcache += '  <errors>report</errors>\n';
    mapcache += '  <lock_dir>/tmp/' + project + '</lock_dir>\n';
    mapcache += '  <threaded_fetching>true</threaded_fetching>\n';
    mapcache += '  <log_level>debug</log_level>\n';
    mapcache += '  <auto_reload>true&lt/auto_reload> \n';

    mapcache += '</mapcache>\n';

    return mapcache;
});

// ---------------------------------------
// Build a mapacache.xml file from
// a WMS capabilies xml file
// (Synchronous request)
// --------------------------------------------
// Parameters
//  - capabilitiesFile : xml capabilities
//  - urlRequestCapa : WMS getCapabilities request
//  - projectName : name of the project
// --------------------------------------------
buildMapcacheXml = (function(capabilitiesFile, urlRequestCapa, projectName) {
    var mapcache = 'null';
    var xhr = new XMLHttpRequest();
    xhr.open('GET', capabilitiesFile, false);
    xhr.setRequestHeader('Content-Type', 'text/xml');
    xhr.send(null);

    if (xhr.status == 200) {
        mapcache = buildMapcacheFromXml(result, urlRequestCapa, projectName);
    }
    return mapcache;
});

<commit_msg>Update mapcache.js<commit_after>// ---------------------------------------
// Build a mapacache.xml file from
// a WMS capabilies xml file
// (Synchronous request)
// --------------------------------------------
// Parameters
//  - capabilitiesFile : xml capabilities
//  - urlRequestCapa : WMS getCapabilities request
//  - projectName : name of the project
// --------------------------------------------
buildMapcacheXmlFromContent = (function(capabilitiesXmlContent, urlRequestCapa, projectName) {
    var parser = new ol.parser.ogc.WMSCapabilities(), capabilitiesXmlContent;
    capabilitiesXmlContent = parser.read(capabilitiesXmlContent);

    var layers = capabilitiesXmlContent.capability.layers;

    var project = projectName;
    var mapcache = '';
    var tileset = '';
    var source = '';
    var concat = '&';
    var idx = urlRequestCapa.indexOf('?');
    if (idx == -1) {
        concat = '?';
    }

    mapcache = '<?xml version="1.0" encoding="UTF-8"?>\n';
    mapcache += '<mapcache>\n';
    mapcache += '  <metadata>\n';
    mapcache += '    <title>Mapcache service for url: ' + urlRequestCapa + '</title>\n';
    mapcache += '    <abstract>Render map services for url: ' + urlRequestCapa + '</abstract>\n';
    mapcache += '  </metadata>\n\n';

    // Cache: Disk
    mapcache += '  <cache name="disk" type="disk">\n';
    mapcache += '    <base>/tmp/' + project + '</base>\n';
    mapcache += '    <symlink_blank/>\n';
    mapcache += '  </cache>\n\n';

    // Cache: template
    mapcache += '  <cache name="tmpl" type="disk">\n';
    mapcache += '    <base>/tmp' + project + '</base>\n';
    mapcache += '    <template>/tmp/' + project + '/template/{tileset}#{grid}#{dim}/{z}/{x}/{y}.{ext}</template>\n';
    mapcache += '  </cache>\n\n';

    // Cache: sqlite
    mapcache += '  <cache name="sqlite" type="sqlite3">\n';
    mapcache += '    <dbfile>/tmp/' + project + '/sqlitetiles.db</dbfile>\n';
    mapcache += '    <pragma name="key">value</pragma>\n';
    mapcache += '  </cache>\n\n';

    // Cache: MbTiles
    mapcache += '  <cache name="mbtiles" type="mbtiles">\n';
    mapcache += '    <dbfile>/tmp/' + project + '/' + project + '.mbtiles</dbfile>\n';
    mapcache += '  </cache>\n\n';

    // Format: png
    mapcache += '  <format name="PNGQ_FAST" type ="PNG">\n';
    mapcache += '    <compression>fast</compression>\n';
    mapcache += '    <colors>256</colors>\n';
    mapcache += '  </format>\n\n';

    // Format: jpeg
    mapcache += '  <format name="JPEG_75" type ="JPEG">\n';
    mapcache += '    <quality>75</quality>\n';
    mapcache += '    <photometric>RGB</photometric>\n';
    mapcache += '  </format>\n\n';

    // Format: png
    mapcache += '  <format name="PNG_BEST" type ="PNG">\n';
    mapcache += '    <compression>best</compression>\n';
    mapcache += '  </format>\n\n';

    // Format: mixed
    mapcache += '  <format name="mixed" type="MIXED">\n';
    mapcache += '    <transparent>PNG_BEST</transparent>\n';
    mapcache += '    <opaque>JPEG</opaque>\n';
    mapcache += '  </format>\n\n';

    for (var l = 0; l < layers.length; ++l) {
        layer = layers[l];

        console.log('Layer:' + layer.name);
        console.log('\tAvailable elevations: ' + layer.dimensions.elevation.values);
        console.log('\tAvailable times: ' + layer.dimensions.time.values);

        var arrayTab = layer.styles;
        for (var s = 0; s < arrayTab.length; ++s) {
            style = arrayTab[s];

            tileset = layer.name + '_' + style.name;
            source = 'SOURCE_' + layer.name + '_' + style.name;

            mapcache += '  <!-- Source for layer: ' + layer.name + ', style: ' + style.name + ' -->\n';
            mapcache += '  <source name="' + source + '" type="wms">\n';
            mapcache += '    <http>\n';
            mapcache += '      <url>' + urlRequestCapa + '</url>\n';
            mapcache += '    </http>\n';
            mapcache += '    <getmap>\n';
            mapcache += '      <params>\n';
            mapcache += '        <FORMAT>image/png</FORMAT>\n';
            mapcache += '        <LAYERS>' + layer.name + '</LAYERS>\n';
            mapcache += '        <STYLES>' + style.name + '</STYLES>\n';
            mapcache += '      </params>\n';
            mapcache += '    </getmap>\n';
            mapcache += '  </source>\n\n';

            mapcache += '  <!-- Tileset for layer: ' + layer.name + ', style: ' + style.name + ' -->\n';
            mapcache += '  <tileset name="' + tileset + '">\n';
            mapcache += '    <source>' + source + '</source>\n';
            mapcache += '    <dimensions>\n';
            mapcache += '      <dimension type="regex" name="elevation" default="-1">.*</dimension>\n';
            mapcache += '      <dimension type="regex" name="time" default="2000-01-00T00:00:00Z">.*</dimension>\n';
            mapcache += '    </dimensions>\n';
            mapcache += '    <cache>disk</cache>\n';
            mapcache += '    <format>PNG</format>\n';

            // TODO : create grid for each srs
            //var projs = layer.srs;
            //for (var p = 0; p < projs.length; ++p) {
            //    proj = projs[p];
            //    mapcache += '    <grid>' + proj + '</grid>\n';
            //}

            mapcache += '    <grid>WGS84</grid>\n';
            mapcache += '    <grid>GoogleMapsCompatible</grid>\n';
            mapcache += '    <metatile>5 5</metatile>\n';
            mapcache += '    <metabuffer>10</metabuffer>\n';
            mapcache += '  </tileset>\n\n';

        }
    }

    // Services: wms
    mapcache += '  <service type="wms" enabled="true">\n';
    for (var l = 0; l < layers.length; ++l) {
        layer = layers[l];
        var arrayTab = layer.styles;
        for (var s = 0; s < arrayTab.length; ++s) {
            style = arrayTab[s];
            tileset = layer.name + '_' + style.name;
            source = 'SOURCE_' + layer.name + '_' + style.name;
            mapcache += '    <forwarding_rule name="RULE' + tileset + '"&gt\n';
            mapcache += '      <param name="SERVICE" type="values">WMS</param&gt\n';
            mapcache += '      <param name="LAYERS" type="values">' + layer.name + '</param&gt\n';
            mapcache += '      <param name="STYLES" type="values">' + style.name + '</param&gt\n';
            mapcache += '      <http&gt\n';
            mapcache += '        <url>' + urlRequestCapa + concat + 'LAYERS=' + tileset + '</url&gt\n';
            mapcache += '      </http&gt\n';
            mapcache += '    </forwarding_rule&gt\n';

        }
    }
    mapcache += '    <full_wms>assemble</full_wms&gt\n';
    mapcache += '    <resample_mode>bilinear</resample_mode&gt\n';
    mapcache += '    <format>JPEG_75</format&gt\n';
    mapcache += '    <maxsize>4096</maxsize&gt\n';
    mapcache += '  </service&gt\n\n';

    // Services: wmts, tms, gmaps, ve, demo
    mapcache += '  <service type="wmts" enabled="true"/>\n';
    mapcache += '  <service type="tms" enabled="true"/>\n';
    mapcache += '  <service type="kml" enabled="true"/>\n';
    mapcache += '  <service type="gmaps" enabled="true"/>\n';
    mapcache += '  <service type="ve" enabled="true"/>\n';
    mapcache += '  <service type="demo" enabled="true"/>\n\n';

    // General parameters
    mapcache += '  <errors>report</errors>\n';
    mapcache += '  <lock_dir>/tmp/' + project + '</lock_dir>\n';
    mapcache += '  <threaded_fetching>true</threaded_fetching>\n';
    mapcache += '  <log_level>debug</log_level>\n';
    mapcache += '  <auto_reload>true</auto_reload> \n';

    mapcache += '</mapcache>\n';

    return mapcache;
});

// ---------------------------------------
// Build a mapacache.xml file from
// a WMS capabilies xml file
// (Synchronous request)
// --------------------------------------------
// Parameters
//  - capabilitiesFile : xml capabilities
//  - urlRequestCapa : WMS getCapabilities request
//  - projectName : name of the project
// --------------------------------------------
buildMapcacheXml = (function(capabilitiesFile, urlRequestCapa, projectName) {
    var mapcache = 'null';
    var xhr = new XMLHttpRequest();
    xhr.open('GET', capabilitiesFile, false);
    xhr.setRequestHeader('Content-Type', 'text/xml');
    xhr.send(null);

    if (xhr.status == 200) {
        mapcache = buildMapcacheFromXml(result, urlRequestCapa, projectName);
    }
    return mapcache;
});

<|endoftext|>


<commit_before>// We have to remove node_modules/react to avoid having multiple copies loaded.
// eslint-disable-next-line import/no-unresolved
import React, { PropTypes } from 'react';
import Switch from 'react-router-dom/Switch';
import Route from 'react-router-dom/Route';
import Link from 'react-router-dom/Link';

import Paneset from '@folio/stripes-components/lib/Paneset';
import Pane from '@folio/stripes-components/lib/Pane';
import NavList from '@folio/stripes-components/lib/NavList';
import NavListSection from '@folio/stripes-components/lib/NavListSection';

// Should this list be loaded dynamically from configuration?
import PermissionSets from './PermissionSets';
import PatronGroupsSettings from './PatronGroupsSettings';

const pages = [
  {
    route: 'perms',
    label: 'Permission sets',
    component: PermissionSets,
    perm: 'perms.permissions.get',
  },
  {
    route: 'groups',
    label: 'Patron groups',
    component: PatronGroupsSettings,
    // No perm needed yet
  },
];

const UsersSettings = (props) => {
  const navLinks = pages.map((p) => {
    if (p.perm && !props.stripes.hasPerm(p.perm)) return null;
    return (
      <Link
        key={p.route}
        to={`${props.match.path}/${p.route}`}
      >{p.label}</Link>
    );
  }).filter(l => l);

  const routes = pages.map((p) => {
    const Current = props.stripes.connect(p.component);
    return (<Route
      key={p.route}
      path={`${props.match.path}/${p.route}`}
      render={props2 => <Current {...props2} stripes={props.stripes} />}
    />);
  });

  return (
    <Paneset nested defaultWidth="80%">
      <Pane defaultWidth="25%" paneTitle="Users">
        <NavList>
          <NavListSection activeLink="">
            {navLinks}
          </NavListSection>
        </NavList>
      </Pane>

      <Switch>
        {routes}
        <Route component={() =>
Choose category
} />
      </Switch>
    </Paneset>
  );
};

UsersSettings.propTypes = {
  stripes: PropTypes.shape({
    connect: PropTypes.func.isRequired,
    hasPerm: PropTypes.func.isRequired,
  }).isRequired,
};

export default UsersSettings;
<commit_msg>Rename component internally to just Settings<commit_after>// We have to remove node_modules/react to avoid having multiple copies loaded.
// eslint-disable-next-line import/no-unresolved
import React, { PropTypes } from 'react';
import Switch from 'react-router-dom/Switch';
import Route from 'react-router-dom/Route';
import Link from 'react-router-dom/Link';

import Paneset from '@folio/stripes-components/lib/Paneset';
import Pane from '@folio/stripes-components/lib/Pane';
import NavList from '@folio/stripes-components/lib/NavList';
import NavListSection from '@folio/stripes-components/lib/NavListSection';

// Should this list be loaded dynamically from configuration?
import PermissionSets from './PermissionSets';
import PatronGroupsSettings from './PatronGroupsSettings';

const pages = [
  {
    route: 'perms',
    label: 'Permission sets',
    component: PermissionSets,
    perm: 'perms.permissions.get',
  },
  {
    route: 'groups',
    label: 'Patron groups',
    component: PatronGroupsSettings,
    // No perm needed yet
  },
];

const Settings = (props) => {
  const navLinks = pages.map((p) => {
    if (p.perm && !props.stripes.hasPerm(p.perm)) return null;
    return (
      <Link
        key={p.route}
        to={`${props.match.path}/${p.route}`}
      >{p.label}</Link>
    );
  }).filter(l => l);

  const routes = pages.map((p) => {
    const Current = props.stripes.connect(p.component);
    return (<Route
      key={p.route}
      path={`${props.match.path}/${p.route}`}
      render={props2 => <Current {...props2} stripes={props.stripes} />}
    />);
  });

  return (
    <Paneset nested defaultWidth="80%">
      <Pane defaultWidth="25%" paneTitle="Users">
        <NavList>
          <NavListSection activeLink="">
            {navLinks}
          </NavListSection>
        </NavList>
      </Pane>

      <Switch>
        {routes}
        <Route component={() =>
Choose category
} />
      </Switch>
    </Paneset>
  );
};

Settings.propTypes = {
  stripes: PropTypes.shape({
    connect: PropTypes.func.isRequired,
    hasPerm: PropTypes.func.isRequired,
  }).isRequired,
};

export default Settings;
<|endoftext|>


<commit_before>/*    Copyright 2016 Firewalla LLC
 *
 *    This program 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.
 *
 *    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require("../net2/logger.js")(__filename);
const Promise = require('bluebird');

const rclient = require('../util/redis_manager.js').getRedisClient()

const async = require('asyncawait/async')
const await = require('asyncawait/await')

const DNSMASQ = require('../extension/dnsmasq/dnsmasq.js');
const dnsmasq = new DNSMASQ();

const Block = require('./Block.js');

const DNSTool = require('../net2/DNSTool.js')
const dnsTool = new DNSTool()

const dns = require('dns');
const resolve4Async = Promise.promisify(dns.resolve4)
const resolve6Async = Promise.promisify(dns.resolve6)

const SysManager = require("../net2/SysManager.js")
const sysManager = new SysManager()

const sem = require('../sensor/SensorEventManager.js').getInstance()

let globalLock = false

function delay(t) {
  return new Promise(function(resolve) {
    setTimeout(resolve, t)
  });
}

class DomainBlock {

  constructor() {

  }

  // a mapping from domain to ip is tracked in redis, so that we can apply block at ip level, which is more secure
  blockDomain(domain, options) {
    options = options || {}
    return async(() => {
      log.info(`Block ${domain} is holding the lock`)

      if(!options.no_dnsmasq_entry) {
        await (dnsmasq.addPolicyFilterEntry(domain, options).catch((err) => undefined))
      }

      if(!options.no_dnsmasq_reload) {
        sem.emitEvent({
          type: 'ReloadDNSRule',
          message: 'DNSMASQ filter rule is updated',
          toProcess: 'FireMain',
          suppressEventLogging: true
        })
      }

      await (this.syncDomainIPMapping(domain, options))
      if(!options.ignoreApplyBlock) {
        await (this.applyBlock(domain, options))
      }

      // setTimeout(() => {
      //   this.incrementalUpdateIPMapping(domain, options)
      // }, 60 * 1000) // reinforce in 60 seconds
    })()
  }

  unblockDomain(domain, options) {
    return async(() => {
      if(!options.ignoreUnapplyBlock) {
        await (this.unapplyBlock(domain, options))
      }

      if(!this.externalMapping) {
        await (this.removeDomainIPMapping(domain, options))
      }

      if(!options.no_dnsmasq_entry) {
        await (dnsmasq.removePolicyFilterEntry(domain).catch((err) => undefined))
      }

      if(!options.no_dnsmasq_reload) {
        sem.emitEvent({
          type: 'ReloadDNSRule',
          message: 'DNSMASQ filter rule is updated',
          toProcess: 'FireMain',
          suppressEventLogging: true,
        })
      }
    })()
  }

  getDomainIPMappingKey(domain, options) {
    options = options || {}

    if(this.externalMapping) {
      return this.externalMapping
    }

    if(options.exactMatch) {
      return `ipmapping:exactdomain:${domain}`
    } else {
      return `ipmapping:domain:${domain}`
    }
  }

  removeDomainIPMapping(domain, options) {
    const key = this.getDomainIPMappingKey(domain, options)
    return rclient.delAsync(key)
  }

  removeAllDomainIPMapping() {
    const patternDomainKey = `ipmapping:domain:*`
    const domainKeys = await (rclient.keysAsync(patternDomainKey))
    if(domainKeys) {
      await (rclient.delAsync(domainKeys))
    }

    const patternExactDomainKey = `ipmapping:exactdomain:*`
    const exactDomainKeys = await (rclient.keysAsync(patternExactDomainKey))
    if(exactDomainKeys) {
      await (rclient.delAsync(exactDomainKeys))
    }
  }

  getMappedIPAddresses(domain, options) {
    const key = this.getDomainIPMappingKey(domain, options)
    return rclient.smembersAsync(key)
  }

  applyBlock(domain, options) {
    const blockSet = options.blockSet || "blocked_domain_set"
    return async(() => {
      const addresses = await (this.getMappedIPAddresses(domain, options))
      if(addresses) {
        addresses.forEach((addr) => {
          await (Block.block(addr, blockSet).catch((err) => undefined))
        })
      }
    })()
  }

  unapplyBlock(domain, options) {
    const blockSet = options.blockSet || "blocked_domain_set"

    return async(() => {
      const addresses = await (this.getMappedIPAddresses(domain, options))
      if(addresses) {
        addresses.forEach((addr) => {
          await (Block.unblock(addr, blockSet).catch((err) => undefined))
        })
      }
    })()
  }

  resolve4WithTimeout(domain, timeout) {
    let callbackCalled = false

    return new Promise((resolve, reject) => {
      resolve4Async(domain).then((addresses) => {
        if(!callbackCalled) {
          callbackCalled = true
          resolve(addresses)
        }
      }).catch((err) => {
        if(!callbackCalled) {
          callbackCalled = true
          resolve([]) // return empty array in case any error
        }
      })
      setTimeout(() => {
        if(!callbackCalled) {
          callbackCalled = true
          resolve([]) // return empty array in case timeout
        }
      }, timeout)
    })
  }

  resolve6WithTimeout(domain, timeout) {
    let callbackCalled = false

    return new Promise((resolve, reject) => {
      resolve6Async(domain).then((addresses) => {
        if(!callbackCalled) {
          callbackCalled = true
          resolve(addresses)
        }
      }).catch((err) => {
        if(!callbackCalled) {
          callbackCalled = true
          resolve([]) // return empty array in case any error
        }
      })
      setTimeout(() => {
        if(!callbackCalled) {
          log.warn("Timeout when query domain", domain, {})
          callbackCalled = true
          resolve([]) // return empty array in case timeout
        }
      }, timeout)
    })
  }

  resolveDomain(domain) {
    return async(() => {
      const v4Addresses = await (this.resolve4WithTimeout(domain, 3 * 1000).catch((err) => [])) // 3 seconds for timeout
      await (dnsTool.addReverseDns(domain, v4Addresses))

      const gateway6 = sysManager.myGateway6()
      if(gateway6) { // only query if ipv6 is supported
        const v6Addresses = await (this.resolve6WithTimeout(domain, 3 * 1000).catch((err) => []))
        await (dnsTool.addReverseDns(domain, v6Addresses))
        return v4Addresses.concat(v6Addresses)
      } else {
        return v4Addresses
      }

    })()
  }

  syncDomainIPMapping(domain, options) {
    options = options || {}

    const key = this.getDomainIPMappingKey(domain, options)

    return async(() => {
      await (this.resolveDomain(domain))

      let list = []

      // load other addresses from rdns, critical to apply instant blocking
      const addresses = await (dnsTool.getAddressesByDNS(domain).catch((err) => []))
      list.push.apply(list, addresses)

      if(!options.exactMatch) {
        const patternAddresses = await (dnsTool.getAddressesByDNSPattern(domain).catch((err) => []))
        list.push.apply(list, patternAddresses)
      }

      return rclient.saddAsync(key, list)

    })()
  }

  // incremental update mapping to reinforce ip blocking
  // this function should be executed in a serial way with other policy enforcements to avoid race conditions
  incrementalUpdateIPMapping(domain, options) {
    options = options || {}

    log.info("Incrementally updating blocking list for", domain)

    const key = this.getDomainIPMappingKey(domain, options)

    return async(() => {
      const existing = await(rclient.existsAsync(key))

      if(!existing) {
        return
      }

      await (this.resolveDomain(domain))

      let set = {}

      // load other addresses from rdns, critical to apply instant blocking
      const addresses = await (dnsTool.getAddressesByDNS(domain).catch((err) => []))
      addresses.forEach((addr) => {
        set[addr] = 1
      })

      if(!options.exactMatch) {
        const patternAddresses = await (dnsTool.getAddressesByDNSPattern(domain).catch((err) => []))
        patternAddresses.forEach((addr) => {
          set[addr] = 1
        })
      }

      const existingAddresses = await (this.getMappedIPAddresses(domain, options))

      let existingSet = {}
      existingAddresses.forEach((addr) => {
        existingSet[addr] = 1
      })

      // only add new changed ip addresses, there is no need to remove any old ip addrs
      for(let addr in set) {
        if(!existingSet[addr]) {
          await (rclient.saddAsync(key,addr))
          await (Block.block(addr, "blocked_domain_set").catch((err) => undefined))
        }
      }

    })()
  }

  getAllIPMappings() {
    return async(() => {
      let list = await (rclient.keysAsync("ipmapping:*"))
      return list
    })()
  }

  removeAllIPMappings() {
    return async(() => {
      const list = await (this.getAllIPMappings())
      return rclient.delAsync(list)
    })()
  }
}

module.exports = () => new DomainBlock()<commit_msg>bug fix<commit_after>/*    Copyright 2016 Firewalla LLC
 *
 *    This program 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.
 *
 *    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require("../net2/logger.js")(__filename);
const Promise = require('bluebird');

const rclient = require('../util/redis_manager.js').getRedisClient()

const async = require('asyncawait/async')
const await = require('asyncawait/await')

const DNSMASQ = require('../extension/dnsmasq/dnsmasq.js');
const dnsmasq = new DNSMASQ();

const Block = require('./Block.js');

const DNSTool = require('../net2/DNSTool.js')
const dnsTool = new DNSTool()

const dns = require('dns');
const resolve4Async = Promise.promisify(dns.resolve4)
const resolve6Async = Promise.promisify(dns.resolve6)

const SysManager = require("../net2/SysManager.js")
const sysManager = new SysManager()

const sem = require('../sensor/SensorEventManager.js').getInstance()

let globalLock = false

function delay(t) {
  return new Promise(function(resolve) {
    setTimeout(resolve, t)
  });
}

class DomainBlock {

  constructor() {

  }

  // a mapping from domain to ip is tracked in redis, so that we can apply block at ip level, which is more secure
  blockDomain(domain, options) {
    options = options || {}
    return async(() => {
      log.info(`Block ${domain} is holding the lock`)

      if(!options.no_dnsmasq_entry) {
        await (dnsmasq.addPolicyFilterEntry(domain, options).catch((err) => undefined))
      }

      if(!options.no_dnsmasq_reload) {
        sem.emitEvent({
          type: 'ReloadDNSRule',
          message: 'DNSMASQ filter rule is updated',
          toProcess: 'FireMain',
          suppressEventLogging: true
        })
      }

      await (this.syncDomainIPMapping(domain, options))
      if(!options.ignoreApplyBlock) {
        await (this.applyBlock(domain, options))
      }

      // setTimeout(() => {
      //   this.incrementalUpdateIPMapping(domain, options)
      // }, 60 * 1000) // reinforce in 60 seconds
    })()
  }

  unblockDomain(domain, options) {
    return async(() => {
      if(!options.ignoreUnapplyBlock) {
        await (this.unapplyBlock(domain, options))
      }

      if(!this.externalMapping) {
        await (this.removeDomainIPMapping(domain, options))
      }

      if(!options.no_dnsmasq_entry) {
        await (dnsmasq.removePolicyFilterEntry(domain).catch((err) => undefined))
      }

      if(!options.no_dnsmasq_reload) {
        sem.emitEvent({
          type: 'ReloadDNSRule',
          message: 'DNSMASQ filter rule is updated',
          toProcess: 'FireMain',
          suppressEventLogging: true,
        })
      }
    })()
  }

  getDomainIPMappingKey(domain, options) {
    options = options || {}

    if(this.externalMapping) {
      return this.externalMapping
    }

    if(options.exactMatch) {
      return `ipmapping:exactdomain:${domain}`
    } else {
      return `ipmapping:domain:${domain}`
    }
  }

  removeDomainIPMapping(domain, options) {
    const key = this.getDomainIPMappingKey(domain, options)
    return rclient.delAsync(key)
  }

  removeAllDomainIPMapping() {
    const patternDomainKey = `ipmapping:domain:*`
    const domainKeys = await (rclient.keysAsync(patternDomainKey))
    if(domainKeys) {
      domainKeys.forEach((key) => {
        await (rclient.delAsync(key))
      })
    }

    const patternExactDomainKey = `ipmapping:exactdomain:*`
    const exactDomainKeys = await (rclient.keysAsync(patternExactDomainKey))
    if(exactDomainKeys) {
      exactDomainKeys.forEach((key) => {
        await (rclient.delAsync(key))
      })
    }
  }

  getMappedIPAddresses(domain, options) {
    const key = this.getDomainIPMappingKey(domain, options)
    return rclient.smembersAsync(key)
  }

  applyBlock(domain, options) {
    const blockSet = options.blockSet || "blocked_domain_set"
    return async(() => {
      const addresses = await (this.getMappedIPAddresses(domain, options))
      if(addresses) {
        addresses.forEach((addr) => {
          await (Block.block(addr, blockSet).catch((err) => undefined))
        })
      }
    })()
  }

  unapplyBlock(domain, options) {
    const blockSet = options.blockSet || "blocked_domain_set"

    return async(() => {
      const addresses = await (this.getMappedIPAddresses(domain, options))
      if(addresses) {
        addresses.forEach((addr) => {
          await (Block.unblock(addr, blockSet).catch((err) => undefined))
        })
      }
    })()
  }

  resolve4WithTimeout(domain, timeout) {
    let callbackCalled = false

    return new Promise((resolve, reject) => {
      resolve4Async(domain).then((addresses) => {
        if(!callbackCalled) {
          callbackCalled = true
          resolve(addresses)
        }
      }).catch((err) => {
        if(!callbackCalled) {
          callbackCalled = true
          resolve([]) // return empty array in case any error
        }
      })
      setTimeout(() => {
        if(!callbackCalled) {
          callbackCalled = true
          resolve([]) // return empty array in case timeout
        }
      }, timeout)
    })
  }

  resolve6WithTimeout(domain, timeout) {
    let callbackCalled = false

    return new Promise((resolve, reject) => {
      resolve6Async(domain).then((addresses) => {
        if(!callbackCalled) {
          callbackCalled = true
          resolve(addresses)
        }
      }).catch((err) => {
        if(!callbackCalled) {
          callbackCalled = true
          resolve([]) // return empty array in case any error
        }
      })
      setTimeout(() => {
        if(!callbackCalled) {
          log.warn("Timeout when query domain", domain, {})
          callbackCalled = true
          resolve([]) // return empty array in case timeout
        }
      }, timeout)
    })
  }

  resolveDomain(domain) {
    return async(() => {
      const v4Addresses = await (this.resolve4WithTimeout(domain, 3 * 1000).catch((err) => [])) // 3 seconds for timeout
      await (dnsTool.addReverseDns(domain, v4Addresses))

      const gateway6 = sysManager.myGateway6()
      if(gateway6) { // only query if ipv6 is supported
        const v6Addresses = await (this.resolve6WithTimeout(domain, 3 * 1000).catch((err) => []))
        await (dnsTool.addReverseDns(domain, v6Addresses))
        return v4Addresses.concat(v6Addresses)
      } else {
        return v4Addresses
      }

    })()
  }

  syncDomainIPMapping(domain, options) {
    options = options || {}

    const key = this.getDomainIPMappingKey(domain, options)

    return async(() => {
      await (this.resolveDomain(domain))

      let list = []

      // load other addresses from rdns, critical to apply instant blocking
      const addresses = await (dnsTool.getAddressesByDNS(domain).catch((err) => []))
      list.push.apply(list, addresses)

      if(!options.exactMatch) {
        const patternAddresses = await (dnsTool.getAddressesByDNSPattern(domain).catch((err) => []))
        list.push.apply(list, patternAddresses)
      }

      return rclient.saddAsync(key, list)

    })()
  }

  // incremental update mapping to reinforce ip blocking
  // this function should be executed in a serial way with other policy enforcements to avoid race conditions
  incrementalUpdateIPMapping(domain, options) {
    options = options || {}

    log.info("Incrementally updating blocking list for", domain)

    const key = this.getDomainIPMappingKey(domain, options)

    return async(() => {
      const existing = await(rclient.existsAsync(key))

      if(!existing) {
        return
      }

      await (this.resolveDomain(domain))

      let set = {}

      // load other addresses from rdns, critical to apply instant blocking
      const addresses = await (dnsTool.getAddressesByDNS(domain).catch((err) => []))
      addresses.forEach((addr) => {
        set[addr] = 1
      })

      if(!options.exactMatch) {
        const patternAddresses = await (dnsTool.getAddressesByDNSPattern(domain).catch((err) => []))
        patternAddresses.forEach((addr) => {
          set[addr] = 1
        })
      }

      const existingAddresses = await (this.getMappedIPAddresses(domain, options))

      let existingSet = {}
      existingAddresses.forEach((addr) => {
        existingSet[addr] = 1
      })

      // only add new changed ip addresses, there is no need to remove any old ip addrs
      for(let addr in set) {
        if(!existingSet[addr]) {
          await (rclient.saddAsync(key,addr))
          await (Block.block(addr, "blocked_domain_set").catch((err) => undefined))
        }
      }

    })()
  }

  getAllIPMappings() {
    return async(() => {
      let list = await (rclient.keysAsync("ipmapping:*"))
      return list
    })()
  }

  removeAllIPMappings() {
    return async(() => {
      const list = await (this.getAllIPMappings())
      return rclient.delAsync(list)
    })()
  }
}

module.exports = () => new DomainBlock()<|endoftext|>


<commit_before>var request = require('request-promise');
var API_BASE_URL = "https://api.tnyu.org/v1.0/";

module.exports = {
  showEvents: function(req, res, next, urlFor) {
    request({
      url: API_BASE_URL + "events/public-check-in-eligible",
      json: true,
      rejectUnauthorized: false
    }, function(err, response, body) {
      res.render('showEvents', {events: body.events});
    });
  },

  showConfiguredEvent: function(req, res, next, urlFor) {
    request({
      url: API_BASE_URL + "events/" + req.params.event_id,
      json: true,
      rejectUnauthorized: false
    }, function(err, response, body) {
      res.render('showEvents', {events: body.events, fromConfig: true});
    });
  },

  checkin: function(req, res, next, urlFor) {
    res.render('checkin');
  },

  thanks: function(req, res, next, urlFor) {
    res.render('thanks');
  },

  configure: function(req, res, next, urlFor) {
    request({
      // include non-public events so board members can
      // create check in pages, e.g., for our eboard meetings.
      url: "https://api.tnyu.org/v1.0/events/next-10",
      json: true,
      rejectUnauthorized: false
    }, function(err, response, body) {
      var events = body.events instanceof Array ? body.events : [body.events];

      events.forEach(function(event) {
        event.checkInUrl = urlFor("showConfiguredEvent", {absolute: true, params: {event_id: event.id}});
      })

      res.render('configure', {events: events});
    });
  }
}<commit_msg>Actually, only use this system for public events<commit_after>var request = require('request-promise');
var API_BASE_URL = "https://api.tnyu.org/v1.0/";

module.exports = {
  showEvents: function(req, res, next, urlFor) {
    request({
      url: API_BASE_URL + "events/public-check-in-eligible",
      json: true,
      rejectUnauthorized: false
    }, function(err, response, body) {
      res.render('showEvents', {events: body.events});
    });
  },

  showConfiguredEvent: function(req, res, next, urlFor) {
    request({
      url: API_BASE_URL + "events/" + req.params.event_id,
      json: true,
      rejectUnauthorized: false
    }, function(err, response, body) {
      res.render('showEvents', {events: body.events, fromConfig: true});
    });
  },

  checkin: function(req, res, next, urlFor) {
    res.render('checkin');
  },

  thanks: function(req, res, next, urlFor) {
    res.render('thanks');
  },

  configure: function(req, res, next, urlFor) {
    request({
      url: "https://api.tnyu.org/v1.0/events/next-10-public",
      json: true,
      rejectUnauthorized: false
    }, function(err, response, body) {
      var events = body.events instanceof Array ? body.events : [body.events];

      events.forEach(function(event) {
        event.checkInUrl = urlFor("showConfiguredEvent", {absolute: true, params: {event_id: event.id}});
      })

      res.render('configure', {events: events});
    });
  }
}<|endoftext|>


<commit_before>/* jspsych-text.js
 * Josh de Leeuw
 *
 * This plugin displays text (including HTML formatted strings) during the experiment.
 * Use it to show instructions, provide performance feedback, etc...
 *
 * No data is currently collected with this plugin. Do not use it for situations in which
 * data collection is important.
 *
 * Parameters:
 * 		type: "text"
 *		text: an array of strings. Each element in the array will be displayed on a separate screen.
 *		cont_key: the keycode of the key the user should press to advance to the next screen. Default is '13' which is ENTER. May specify mouse click
 *                  by listing the key as 'mouse'
 *		timing_post_trial: an array with a single element representing the time in milliseconds to delay on a blank screen after the continue key is pressed. Default is no delay.
 *		variables: see variables section below.
 *      data: optional data object
 *
 * Optional Variables: If you want to display dynamic information that is updated at the moment the text is rendered on the screen,
 * such calculating an accuracy score to tell a subject how many trials they got right, you can use the optional variables parameter.
 * The variables are specified as an array of arrays. The outer level array indexes for which block of text you are showing, and the
 * outer array should have the same length as the text array. Each element of the outer level array is also an array, with one element
 * for each variable that you are specifying. Variables are specified as functions which return the string that you want to display.
 * To indicate where a variable should be placed in the text, use the special string "%v" in the text. This will be replaced by the
 * return value of the function that is in the variables array. Each "%v" string will be replaced in the order that they appear in the
 * text, and only one replacement will be made per function call. Therefore, you should have the same number of "%v" strings as you have
 * elements in the inner arrays of the "variables" array.
 *
 *				Example:
 * 					var goodbye_func = function(){return "goodbye";}
 *					var hello_func = function(){return "hello";}
 *					"text": ["%v, %v.", "I don't know why you say %v, I say %v."]
 *					"variables":[[hello_func, hello_func],[goodbye_func, hello_func]]
 *
 *				When the above parameters are loaded into the text plugin, the first screen would show
 * 				"hello, hello." and the second screen would show "I don't know why you say goodbye, I say hello."
 *
 */

 (function($) {
    jsPsych.text = (function() {

        var plugin = {};

        plugin.create = function(params) {
            var trials = new Array(params.text.length);
            for (var i = 0; i < trials.length; i++) {
                trials[i] = {};
                trials[i].type = "text"; // must match plugin name
                trials[i].text = params.text[i]; // text of all trials
                trials[i].cont_key = params.cont_key || '13'; // keycode to press to advance screen, default is ENTER.
                trials[i].timing_post_trial = params.timing_post_trial || 0; // how long to delay between screens, default is no delay.
                trials[i].variables = (typeof params.variables === 'undefined') ? undefined : params.variables[i];
                trials[i].data = (typeof params.data === 'undefined') ? {} : params.data[i];
            }
            return trials;
        };

        plugin.trial = function(display_element, block, trial, part) {
            // the text for the trial is in trial.text, but we need to replace any variables that are in the text.
            var replaced_text = trial.text;

            // check to see if there are any variables defined.
            if (typeof trial.variables != 'undefined') {
                for (var i = 0; i < trial.variables.length; i++) {
                    // loop through the array of variables and call each variable function
                    // to get the actual text that should be substituted in.
                    var variable_text = trial.variables[i]();
                    // replace the "%v" with the return value of the function.
                    replaced_text = replaced_text.replace("%v", variable_text);
                }
            }
            // set the HTML of the display target to replaced_text.
            display_element.html(replaced_text);

            var startTime = (new Date()).getTime();

            // define a function that will advance to the next trial when the user presses
            // the continue key.
            var key_listener = function(e) {
                if (e.which == trial.cont_key) {
                    save_data();
                    $(document).unbind('keyup', key_listener); // remove the response function, so that it doesn't get triggered again.
                    display_element.html(''); // clear the display
                    setTimeout(function() {
                        block.next();
                    }, trial.timing_post_trial); // call block.next() to advance the experiment after a delay.
                }
            };

            var mouse_listener = function(e) {
                save_data();
                display_element.unbind('click', mouse_listener); // remove the response function, so that it doesn't get triggered again.
                display_element.html(''); // clear the display
                setTimeout(function() {
                    block.next();
                }, trial.timing_post_trial); // call block.next() to advance the experiment after a delay.
            };

            // check if key is 'mouse'
            if (trial.cont_key == 'mouse') {
                display_element.click(mouse_listener);
            }
            else {
                // attach the response function to the html document.
                $(document).keyup(key_listener);
            }

            var save_data = function() {
                var rt = (new Date()).getTime() - startTime;
                block.writeData($.extend({}, {
                    "trial_type": "text",
                    "trial_index": block.trial_idx,
                    "rt": rt
                }, trial.data));
            };
        };

        return plugin;
    })();
})(jQuery);<commit_msg>Adding a keydown requirement so that plugin will not advance if key is already pressed when user arrives at plugin page<commit_after>/* jspsych-text.js
 * Josh de Leeuw
 *
 * This plugin displays text (including HTML formatted strings) during the experiment.
 * Use it to show instructions, provide performance feedback, etc...
 *
 * No data is currently collected with this plugin. Do not use it for situations in which
 * data collection is important.
 *
 * Parameters:
 * 		type: "text"
 *		text: an array of strings. Each element in the array will be displayed on a separate screen.
 *		cont_key: the keycode of the key the user should press to advance to the next screen. Default is '13' which is ENTER. May specify mouse click
 *                  by listing the key as 'mouse'
 *		timing_post_trial: an array with a single element representing the time in milliseconds to delay on a blank screen after the continue key is pressed. Default is no delay.
 *		variables: see variables section below.
 *      data: optional data object
 *
 * Optional Variables: If you want to display dynamic information that is updated at the moment the text is rendered on the screen,
 * such calculating an accuracy score to tell a subject how many trials they got right, you can use the optional variables parameter.
 * The variables are specified as an array of arrays. The outer level array indexes for which block of text you are showing, and the
 * outer array should have the same length as the text array. Each element of the outer level array is also an array, with one element
 * for each variable that you are specifying. Variables are specified as functions which return the string that you want to display.
 * To indicate where a variable should be placed in the text, use the special string "%v" in the text. This will be replaced by the
 * return value of the function that is in the variables array. Each "%v" string will be replaced in the order that they appear in the
 * text, and only one replacement will be made per function call. Therefore, you should have the same number of "%v" strings as you have
 * elements in the inner arrays of the "variables" array.
 *
 *				Example:
 * 					var goodbye_func = function(){return "goodbye";}
 *					var hello_func = function(){return "hello";}
 *					"text": ["%v, %v.", "I don't know why you say %v, I say %v."]
 *					"variables":[[hello_func, hello_func],[goodbye_func, hello_func]]
 *
 *				When the above parameters are loaded into the text plugin, the first screen would show
 * 				"hello, hello." and the second screen would show "I don't know why you say goodbye, I say hello."
 *
 */

 (function($) {
    jsPsych.text = (function() {

        var plugin = {};

        plugin.create = function(params) {
            var trials = new Array(params.text.length);
            for (var i = 0; i < trials.length; i++) {
                trials[i] = {};
                trials[i].type = "text"; // must match plugin name
                trials[i].text = params.text[i]; // text of all trials
                trials[i].cont_key = params.cont_key || '13'; // keycode to press to advance screen, default is ENTER.
                trials[i].timing_post_trial = params.timing_post_trial || 0; // how long to delay between screens, default is no delay.
                trials[i].variables = (typeof params.variables === 'undefined') ? undefined : params.variables[i];
                trials[i].data = (typeof params.data === 'undefined') ? {} : params.data[i];
            }
            return trials;
        };

        plugin.trial = function(display_element, block, trial, part) {
            // the text for the trial is in trial.text, but we need to replace any variables that are in the text.
            var replaced_text = trial.text;

            // check to see if there are any variables defined.
            if (typeof trial.variables !== 'undefined') {
                for (var i = 0; i < trial.variables.length; i++) {
                    // loop through the array of variables and call each variable function
                    // to get the actual text that should be substituted in.
                    var variable_text = trial.variables[i]();
                    // replace the "%v" with the return value of the function.
                    replaced_text = replaced_text.replace("%v", variable_text);
                }
            }
            // set the HTML of the display target to replaced_text.
            display_element.html(replaced_text);

            var startTime = (new Date()).getTime();

            // it's possible that if the user is holding down the cont_key when
            // they arrive on the page that they will advance as soon as the
            // key is released. this prevents that from happening by requiring a
            // full cycle on the page with a down and up event.
            var cont_key_down = false;

            // define a function that will advance to the next trial when the user presses
            // the continue key.
            var key_listener = function(e) {
                if (e.which == trial.cont_key && cont_key_down) {
                    save_data();
                    $(document).unbind('keyup', key_listener); // remove the response function, so that it doesn't get triggered again.
                    $(document).unbind('keydown', key_down_listener);
                    display_element.html(''); // clear the display
                    setTimeout(function() {
                        block.next();
                    }, trial.timing_post_trial); // call block.next() to advance the experiment after a delay.
                }
            };

            var key_down_listener = function(e){
                if(e.which == trial.cont_key)
                {
                    cont_key_down = true;
                }
            };

            var mouse_listener = function(e) {
                save_data();
                display_element.unbind('click', mouse_listener); // remove the response function, so that it doesn't get triggered again.
                display_element.html(''); // clear the display
                setTimeout(function() {
                    block.next();
                }, trial.timing_post_trial); // call block.next() to advance the experiment after a delay.
            };

            // check if key is 'mouse'
            if (trial.cont_key == 'mouse') {
                display_element.click(mouse_listener);
            }
            else {
                // attach the response function to the html document.
                $(document).keydown(key_down_listener);
                $(document).keyup(key_listener);
            }

            var save_data = function() {
                var rt = (new Date()).getTime() - startTime;
                block.writeData($.extend({}, {
                    "trial_type": "text",
                    "trial_index": block.trial_idx,
                    "rt": rt
                }, trial.data));
            };
        };

        return plugin;
    })();
})(jQuery);<|endoftext|>


<commit_before>/* eslint-disable no-undefined */
/* eslint-disable no-use-before-define */
import _ from 'lodash';
import {MANY_TO_ONE, MANY_TO_MANY} from 'node-bits';
import pluralize from 'pluralize';
import {foreignKeyRelationshipName} from '../foreign_key_name';
import {READ} from '../../constants';

// boolean functions to define if the relationship makes it in
const searchTeamForRelationship = (model, relationship) => {
  if (relationship.model === model.name && relationship.as) {
    return relationship.as.replace('Id', '');
  }

  if (relationship.references === model.name) {
    return relationship.model;
  }

  return relationship.references;
};

const notNull = array => _.filter(array, c => !_.isNil(c));

const keysFromNode = node => {
  if (_.isArray(node)) {
    return _.flatMap(notNull(node.map(c => keysFromNode(c))));
  }

  if (_.isString(node)) {
    return null;
  }

  if (_.isObject(node)) {
    const own = _.keys(node);
    const child = notNull(own.map(c => keysFromNode(node[c])));

    return _.flatMap([...own, ...child]);
  }

  return null;
};

const relationshipApplies = (model, relationship, piece, split) => {
  const searchTerm = searchTeamForRelationship(model, relationship);
  return _.some(piece, item => {
    const parts = split(item);
    if (parts.includes(searchTerm)) {
      return true;
    }

    if (parts.includes(pluralize(searchTerm))) {
      return true;
    }

    if (parts.includes(pluralize.singular(searchTerm))) {
      return true;
    }

    return false;
  });
};

const compileCheckList = params => {
  const checklist = [];
  if (params.args.select) {
    checklist.push(buildShouldInclude(params.args.select, {overrideDefault: true}));
  }

  if (params.args.expand) {
    checklist.push(buildShouldInclude(params.args.expand, {overrideDefault: true}));
  }

  if (params.args.orderby) {
    checklist.push(buildShouldInclude(params.args.orderby.map(o => o.field)));
  }

  if (params.args.where) {
    checklist.push(buildShouldInclude(keysFromNode(params.args.where)));
  }

  return checklist;
};

const shouldIncludeBySchemaDefinition = (model, relationship, params) => {
  let config = params.mode === READ ? relationship.includeInSelect : relationship.includeInWrite;
  if (config === undefined) {
    return false;
  }

  // include both by default
  if (config === true) {
    config = {model: true, reverse: true};
  }

  // include per config
  if (relationship.model === model.name) {
    return config.model;
  }

  if (relationship.references === model.name) {
    return config.reverse;
  }

  return false;
};

const buildShouldInclude = (models, overrideDefault) => {
  return (model, relationship) => {
    // nothing specified, so don't imply either way
    if (models === undefined) {
      return undefined;
    }

    const includesRelationship = relationshipApplies(model, relationship, models, item => item.split('.'));
    return overrideDefault ? (includesRelationship || undefined) : includesRelationship;
  }
}

const shouldIncludeRelationship = (model, relationship, params) => {
  // see if this applies at all to the passed in model (if not it could be brought in elsewhere)
  if (relationship.model !== model.name && relationship.references !== model.name) {
    return false;
  }

  // If some part of our query requires the relationship, it trumps the schema definition
  const checks = compileCheckList(params);

  const results = checks.map(check => check(model, relationship));
  const includeByQuery = _.some(results, result => result === true);
  const excludeByQuery = _.some(results, result => result === false);

  if (includeByQuery) {
    return true;
  }

  if (excludeByQuery) {
    return false;
  }

  // follow constraints
  if (params.constraints && !params.constraints.schema) {
    return false;
  }

  // not defined by the query, follow the schema definition
  return shouldIncludeBySchemaDefinition(model, relationship, params);
};

// functions to create the include object per sequelizejs syntax
const findRelatedModel = (model, relationship, params) => {
  if (relationship.model === model.name) {
    return params.models[relationship.references];
  }

  return params.models[relationship.model];
};

const defineSeparate = (relationship, params) => {
  const config = params.mode === READ ? relationship.includeInSelect : relationship.includeInWrite;
  if (!config) {
    return false;
  }

  if (!_.isObject(config)) {
    return false;
  }

  return config.separate && (relationship.type === MANY_TO_MANY || relationship.type === MANY_TO_ONE);
};

const defineAttributes = (related, relationship, params) => {
  if (!params.args.select) {
    return undefined;
  }

  const attributes = [];
  _.forEach(params.args.select, item => {
    const pieces = item.split('.');

    if (pieces.length < 2) {
      return;
    }

    const term = relationship.as ? relationship.as.replace('Id', '') : related.name;

    if (pieces[pieces.length - 2] === pluralize(term) || pieces[pieces.length-2] === pluralize.singular(term)) {
      attributes.push(pieces[pieces.length - 1]);
    }
  });

  return attributes.length === 0 || attributes.includes('*') ? undefined : attributes;
};

const defineInclude = (model, relationship, params, path) => {
  // find the model
  const related = findRelatedModel(model, relationship, params);

  // stop the recursion from going back up the tree
  if (path.includes(related.name)) {
    return null;
  }

  // build include object
  return {
    as: foreignKeyRelationshipName(relationship),
    model: related,
    include: build(related, params, [...path, model.name]), // eslint-disable-line
    separate: defineSeparate(relationship, params),
    attributes: defineAttributes(related, relationship, params),
  };
};

// entry point for recursion so we can go down the rabbit hole
const build = (model, params, path = []) => {

  const appliedRelationships = _.filter(params.relationships,
    relationship => shouldIncludeRelationship(model, relationship, params));

  const includes = appliedRelationships.map(
    relationship => defineInclude(model, relationship, params, path)
  );

  return _.filter(includes, x => !_.isNil(x));
};

// public interface
export const buildInclude = (args, mode, model, models, relationships, constraints) =>
  build(model, {mode, models, relationships, args, constraints});
<commit_msg>Fixed backwards logic in override defaults expands<commit_after>/* eslint-disable no-undefined */
/* eslint-disable no-use-before-define */
import _ from 'lodash';
import {MANY_TO_ONE, MANY_TO_MANY} from 'node-bits';
import pluralize from 'pluralize';
import {foreignKeyRelationshipName} from '../foreign_key_name';
import {READ} from '../../constants';

// boolean functions to define if the relationship makes it in
const searchTeamForRelationship = (model, relationship) => {
  if (relationship.model === model.name && relationship.as) {
    return relationship.as.replace('Id', '');
  }

  if (relationship.references === model.name) {
    return relationship.model;
  }

  return relationship.references;
};

const notNull = array => _.filter(array, c => !_.isNil(c));

const keysFromNode = node => {
  if (_.isArray(node)) {
    return _.flatMap(notNull(node.map(c => keysFromNode(c))));
  }

  if (_.isString(node)) {
    return null;
  }

  if (_.isObject(node)) {
    const own = _.keys(node);
    const child = notNull(own.map(c => keysFromNode(node[c])));

    return _.flatMap([...own, ...child]);
  }

  return null;
};

const relationshipApplies = (model, relationship, piece, split) => {
  const searchTerm = searchTeamForRelationship(model, relationship);
  return _.some(piece, item => {
    const parts = split(item);
    if (parts.includes(searchTerm)) {
      return true;
    }

    if (parts.includes(pluralize(searchTerm))) {
      return true;
    }

    if (parts.includes(pluralize.singular(searchTerm))) {
      return true;
    }

    return false;
  });
};

const compileCheckList = params => {
  const checklist = [];
  if (params.args.select) {
    checklist.push(buildShouldInclude(params.args.select, {overrideDefault: true}));
  }

  if (params.args.expand) {
    checklist.push(buildShouldInclude(params.args.expand, {overrideDefault: true}));
  }

  if (params.args.orderby) {
    checklist.push(buildShouldInclude(params.args.orderby.map(o => o.field)));
  }

  if (params.args.where) {
    checklist.push(buildShouldInclude(keysFromNode(params.args.where)));
  }

  return checklist;
};

const shouldIncludeBySchemaDefinition = (model, relationship, params) => {
  let config = params.mode === READ ? relationship.includeInSelect : relationship.includeInWrite;
  if (config === undefined) {
    return false;
  }

  // include both by default
  if (config === true) {
    config = {model: true, reverse: true};
  }

  // include per config
  if (relationship.model === model.name) {
    return config.model;
  }

  if (relationship.references === model.name) {
    return config.reverse;
  }

  return false;
};

const buildShouldInclude = (models, overrideDefault) => {
  return (model, relationship) => {
    // nothing specified, so don't imply either way
    if (models === undefined) {
      return undefined;
    }

    const includesRelationship = relationshipApplies(model, relationship, models, item => item.split('.'));
    return overrideDefault ? includesRelationship : (includesRelationship || undefined);
  }
}

const shouldIncludeRelationship = (model, relationship, params) => {
  // see if this applies at all to the passed in model (if not it could be brought in elsewhere)
  if (relationship.model !== model.name && relationship.references !== model.name) {
    return false;
  }

  // If some part of our query requires the relationship, it trumps the schema definition
  const checks = compileCheckList(params);

  const results = checks.map(check => check(model, relationship));
  const includeByQuery = _.some(results, result => result === true);
  const excludeByQuery = _.some(results, result => result === false);

  if (includeByQuery) {
    return true;
  }

  if (excludeByQuery) {
    return false;
  }

  // follow constraints
  if (params.constraints && !params.constraints.schema) {
    return false;
  }

  // not defined by the query, follow the schema definition
  return shouldIncludeBySchemaDefinition(model, relationship, params);
};

// functions to create the include object per sequelizejs syntax
const findRelatedModel = (model, relationship, params) => {
  if (relationship.model === model.name) {
    return params.models[relationship.references];
  }

  return params.models[relationship.model];
};

const defineSeparate = (relationship, params) => {
  const config = params.mode === READ ? relationship.includeInSelect : relationship.includeInWrite;
  if (!config) {
    return false;
  }

  if (!_.isObject(config)) {
    return false;
  }

  return config.separate && (relationship.type === MANY_TO_MANY || relationship.type === MANY_TO_ONE);
};

const defineAttributes = (related, relationship, params) => {
  if (!params.args.select) {
    return undefined;
  }

  const attributes = [];
  _.forEach(params.args.select, item => {
    const pieces = item.split('.');

    if (pieces.length < 2) {
      return;
    }

    const term = relationship.as ? relationship.as.replace('Id', '') : related.name;

    if (pieces[pieces.length - 2] === pluralize(term) || pieces[pieces.length-2] === pluralize.singular(term)) {
      attributes.push(pieces[pieces.length - 1]);
    }
  });

  return attributes.length === 0 || attributes.includes('*') ? undefined : attributes;
};

const defineInclude = (model, relationship, params, path) => {
  // find the model
  const related = findRelatedModel(model, relationship, params);

  // stop the recursion from going back up the tree
  if (path.includes(related.name)) {
    return null;
  }

  // build include object
  return {
    as: foreignKeyRelationshipName(relationship),
    model: related,
    include: build(related, params, [...path, model.name]), // eslint-disable-line
    separate: defineSeparate(relationship, params),
    attributes: defineAttributes(related, relationship, params),
  };
};

// entry point for recursion so we can go down the rabbit hole
const build = (model, params, path = []) => {

  const appliedRelationships = _.filter(params.relationships,
    relationship => shouldIncludeRelationship(model, relationship, params));

  const includes = appliedRelationships.map(
    relationship => defineInclude(model, relationship, params, path)
  );

  return _.filter(includes, x => !_.isNil(x));
};

// public interface
export const buildInclude = (args, mode, model, models, relationships, constraints) =>
  build(model, {mode, models, relationships, args, constraints});
<|endoftext|>