<commit_before>// @flow

import clone from 'lodash/cloneDeep'
import moment from 'moment'
import validator from 'validator'

import {getTableById} from './gtfs'

import type {Entity, GtfsSpecField, ScheduleException} from '../../types'
import type {EditorTables} from '../../types/reducers'

export type EditorValidationIssue = {
  field: string,
  invalid: boolean,
  reason: string
}

export function doesNotExist (value: any): boolean {
  return value === '' || value === null || typeof value === 'undefined'
}

/**
 * Validate if a value is ok.
 *
 * Returns false if the value is ok.
 * Returns an EditorValidationIssue object if the value is not ok.
 */
// eslint-disable-next-line complexity
export function validate (
  field: GtfsSpecField,
  value: any,
  entities: ?Array<Entity>,
  entity: ?Entity,
  tableData: EditorTables
): EditorValidationIssue | false {
  const {inputType, required, name} = field
  const valueDoesNotExist = doesNotExist(value)
  const isRequiredButEmpty = required && valueDoesNotExist
  const isOptionalAndEmpty = !required && valueDoesNotExist
  const agencies = getTableById(tableData, 'agency')
  let locationType: ?number = null

  // entity.locationtype is a string.  Convert to number for conditinals later on.
  if (entity && entity.location_type !== null) {
    locationType = parseInt(entity.location_type, 10)
  }

  // setting as a variable here because of eslint bug
  type CheckPositiveOutput = {
    num?: number,
    result: false | EditorValidationIssue
  }

  /**
   * Construct an EditorValidationIssue for the field name and reason (defaults to
   * empty field message).
   */
  function validationIssue (reason: string, field = name) {
    return {field, invalid: true, reason}
  }

  /**
   * Construct an EditorValidationIssue for this field, used if it is required
   * and has an empty value.
   */
  function emptyFieldValidationIssue (field = name) {
    return validationIssue('Required field must not be empty', field)
  }

  /**
   * Checks whether value is a positive number
   */
  function checkPositiveNumber (): CheckPositiveOutput {
    if (isRequiredButEmpty) {
      return {
        result: emptyFieldValidationIssue()
      }
    } else if (isOptionalAndEmpty) {
      return {
        result: false
      }
    }
    const num = parseFloat(value)

    // make sure value is parseable to a number
    if (isNaN(num)) {
      return {
        result: validationIssue('Field must be a valid number')
      }
    }

    // make sure value is positive
    if (num < 0) {
      return {
        result: validationIssue('Field must be a positive number')
      }
    }

    return {
      num,
      result: false
    }
  }

  switch (inputType) {
    case 'GTFS_ID':
      // Indices contains list of all indexes of occurrences of the ID value.
      const indices = []
      const idList = entities ? entities.map(e => {
        // $FlowFixMe too many unions for flow to handle
        return name in e ? e[name] : null
      }) : []
      let idx = idList.indexOf(value)
      while (idx !== -1) {
        indices.push(idx)
        idx = idList.indexOf(value, idx + 1)
      }
      const isNotUnique = !!(
        entities &&
        entity &&
        value &&
        (indices.length > 1 ||
          (indices.length > 0 && entities[indices[0]].id !== entity.id))
      )
      if (
        name === 'agency_id' &&
        idList.length > 1 &&
        valueDoesNotExist
      ) {
        return validationIssue('Identifier is required if more than one agency exists')
      }
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      } else if (isNotUnique) {
        return validationIssue('Identifier must be unique')
      } else {
        return false
      }
    case 'TEXT':
      if (name === 'stop_name' && locationType !== null && (typeof locationType === 'number' && locationType <= 2)) {
        return validationIssue('Stop name is required for stop, station, and entrance location types.')
      }
      if (name === 'route_short_name' && !value && entity && entity.route_long_name) {
        return false
      } else if (
        name === 'route_long_name' &&
        !value &&
        entity &&
        entity.route_short_name
      ) {
        return false
      } else {
        if (isRequiredButEmpty) {
          return emptyFieldValidationIssue()
        } else {
          return false
        }
      }
    case 'GTFS_TRIP':
    case 'GTFS_SHAPE':
    case 'GTFS_BLOCK':
    case 'GTFS_FARE':
    case 'GTFS_SERVICE':
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      } else {
        return false
      }
    case 'URL':
      const isNotUrl = value && !validator.isURL(value)
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      } else if (isNotUrl) {
        return validationIssue('Field must contain valid URL.')
      } else {
        return false
      }
    case 'EMAIL':
      const isNotEmail = value && !validator.isEmail(value)
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      } else if (isNotEmail) {
        return validationIssue('Field must contain valid email address.')
      } else {
        return false
      }
    case 'GTFS_ZONE':
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      } else {
        return false
      }
    case 'TIMEZONE':
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      } else {
        return false
      }
    case 'LANGUAGE':
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      } else {
        return false
      }
    case 'LATITUDE':
      const isNotLat = value > 90 || value < -90
      if (isNotLat) {
        return validationIssue('Field must be valid latitude.')
      }
      if (isOptionalAndEmpty && locationType !== null && (typeof locationType === 'number' && locationType <= 2)) {
        return validationIssue('Latitude and Longitude are required for your current location type')
      }
      return false
    case 'LONGITUDE':
      const isNotLng = value > 180 || value < -180
      if (isNotLng) {
        return validationIssue('Field must be valid longitude.')
      }
      if (isOptionalAndEmpty && typeof locationType === 'number' && locationType <= 2) {
        return validationIssue('Latitude and Longitude are required for your current location type')
      }
      return false
    case 'TIME':
    case 'NUMBER':
      const isNotANumber = isNaN(value)
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      } else if (isNotANumber) {
        return validationIssue('Field must be valid number')
      } else {
        return false
      }
    case 'DAY_OF_WEEK_BOOLEAN':
      let hasService = false
      const DAYS_OF_WEEK: Array<string> = [
        'monday',
        'tuesday',
        'wednesday',
        'thursday',
        'friday',
        'saturday',
        'sunday'
      ]
      for (var i = 0; i < DAYS_OF_WEEK.length; i++) {
        if (entity && entity[DAYS_OF_WEEK[i]]) {
          hasService = true
        }
      }
      if (!hasService && name === 'monday') {
        // only add validation issue for one day of week (monday)
        return validationIssue('Calendar must have service for at least one day')
      }
      return false
    case 'DROPDOWN':
      if (
        isRequiredButEmpty &&
        field.options &&
        field.options.findIndex(o => o.value === '') === -1
      ) {
        return emptyFieldValidationIssue()
      } else {
        return false
      }
    case 'GTFS_AGENCY':
      if (
        name === 'agency_id' &&
        agencies.length > 1
      ) {
        if (valueDoesNotExist) {
          return validationIssue('Field must be populated for feeds with more than one agency.')
        }
      }
      return false
    case 'EXCEPTION_DATE': // a date cannot be selected more than once (for all exceptions)
      const dateMap = {}
      // Clone exceptions to avoid mutating table in store.
      const scheduleExceptions: Array<ScheduleException> = clone(getTableById(tableData, 'scheduleexception'))
      if (entity) {
        const entityId = entity.id
        const exceptionIndex = scheduleExceptions.findIndex(se => se.id === entityId)
        if (exceptionIndex !== -1) {
          scheduleExceptions.splice(exceptionIndex, 1)
        }
        const castedScheduleException: ScheduleException = ((entity: any): ScheduleException)
        scheduleExceptions.push(castedScheduleException)
      }

      for (let i = 0; i < scheduleExceptions.length; i++) {
        if (scheduleExceptions[i].dates) {
          scheduleExceptions[i].dates.map(d => {
            if (typeof dateMap[d] === 'undefined') {
              dateMap[d] = []
            }
            dateMap[d].push(scheduleExceptions[i].id)
          })
        }
      }
      if (!value || value.length === 0) {
        return emptyFieldValidationIssue('dates')
      }
      // check if date already exists in this or other exceptions
      for (let i = 0; i < value.length; i++) {
        const dateItemName = `dates-${i}`
        if (dateMap[value[i]] && dateMap[value[i]].length > 1) {
          // eslint-disable-next-line standard/computed-property-even-spacing
          const reason = `Date (${value[
            i
          ]}) cannot appear more than once for all exceptions`
          return validationIssue(reason, dateItemName)
        } else if (!moment(value[i], 'YYYYMMDD', true).isValid()) {
          return emptyFieldValidationIssue(dateItemName)
        }
      }
      return false
    case 'POSITIVE_INT':
      const positiveNumberCheck = checkPositiveNumber()
      if (positiveNumberCheck.result !== false) return positiveNumberCheck.result
      if (isOptionalAndEmpty) return false

      // check for floating value or decimal point
      if (
        typeof positiveNumberCheck.num === 'number' &&
        (
          positiveNumberCheck.num % 1 > 0 ||
          (
            value.indexOf &&
            value.indexOf('.') > -1
          )
        )
      ) {
        return validationIssue('Field must be a positive integer')
      }
      return false
    case 'POSITIVE_NUM':
      return checkPositiveNumber().result
    case 'GTFS_ROUTE':
    case 'GTFS_STOP':
    case 'DATE':
    case 'COLOR':
    default:
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      }
      return false
  }
}
<commit_msg>fix(validation.js): Remove ES lint waiver<commit_after>// @flow

import clone from 'lodash/cloneDeep'
import moment from 'moment'
import validator from 'validator'

import {getTableById} from './gtfs'

import type {Entity, GtfsSpecField, ScheduleException} from '../../types'
import type {EditorTables} from '../../types/reducers'

export type EditorValidationIssue = {
  field: string,
  invalid: boolean,
  reason: string
}

export function doesNotExist (value: any): boolean {
  return value === '' || value === null || typeof value === 'undefined'
}

/**
 * Validate if a value is ok.
 *
 * Returns false if the value is ok.
 * Returns an EditorValidationIssue object if the value is not ok.
 */
export function validate (
  field: GtfsSpecField,
  value: any,
  entities: ?Array<Entity>,
  entity: ?Entity,
  tableData: EditorTables
): EditorValidationIssue | false {
  const {inputType, required, name} = field
  const valueDoesNotExist = doesNotExist(value)
  const isRequiredButEmpty = required && valueDoesNotExist
  const isOptionalAndEmpty = !required && valueDoesNotExist
  const agencies = getTableById(tableData, 'agency')
  let locationType: ?number = null

  // entity.locationtype is a string.  Convert to number for conditinals later on.
  if (entity && entity.location_type !== null) {
    locationType = parseInt(entity.location_type, 10)
  }

  // setting as a variable here because of eslint bug
  type CheckPositiveOutput = {
    num?: number,
    result: false | EditorValidationIssue
  }

  /**
   * Construct an EditorValidationIssue for the field name and reason (defaults to
   * empty field message).
   */
  function validationIssue (reason: string, field = name) {
    return {field, invalid: true, reason}
  }

  /**
   * Construct an EditorValidationIssue for this field, used if it is required
   * and has an empty value.
   */
  function emptyFieldValidationIssue (field = name) {
    return validationIssue('Required field must not be empty', field)
  }

  /**
   * Checks whether value is a positive number
   */
  function checkPositiveNumber (): CheckPositiveOutput {
    if (isRequiredButEmpty) {
      return {
        result: emptyFieldValidationIssue()
      }
    } else if (isOptionalAndEmpty) {
      return {
        result: false
      }
    }
    const num = parseFloat(value)

    // make sure value is parseable to a number
    if (isNaN(num)) {
      return {
        result: validationIssue('Field must be a valid number')
      }
    }

    // make sure value is positive
    if (num < 0) {
      return {
        result: validationIssue('Field must be a positive number')
      }
    }

    return {
      num,
      result: false
    }
  }

  switch (inputType) {
    case 'GTFS_ID':
      // Indices contains list of all indexes of occurrences of the ID value.
      const indices = []
      const idList = entities ? entities.map(e => {
        // $FlowFixMe too many unions for flow to handle
        return name in e ? e[name] : null
      }) : []
      let idx = idList.indexOf(value)
      while (idx !== -1) {
        indices.push(idx)
        idx = idList.indexOf(value, idx + 1)
      }
      const isNotUnique = !!(
        entities &&
        entity &&
        value &&
        (indices.length > 1 ||
          (indices.length > 0 && entities[indices[0]].id !== entity.id))
      )
      if (
        name === 'agency_id' &&
        idList.length > 1 &&
        valueDoesNotExist
      ) {
        return validationIssue('Identifier is required if more than one agency exists')
      }
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      } else if (isNotUnique) {
        return validationIssue('Identifier must be unique')
      } else {
        return false
      }
    case 'TEXT':
      if (name === 'stop_name' && locationType !== null && (typeof locationType === 'number' && locationType <= 2)) {
        return validationIssue('Stop name is required for stop, station, and entrance location types.')
      }
      if (name === 'route_short_name' && !value && entity && entity.route_long_name) {
        return false
      } else if (
        name === 'route_long_name' &&
        !value &&
        entity &&
        entity.route_short_name
      ) {
        return false
      } else {
        if (isRequiredButEmpty) {
          return emptyFieldValidationIssue()
        } else {
          return false
        }
      }
    case 'GTFS_TRIP':
    case 'GTFS_SHAPE':
    case 'GTFS_BLOCK':
    case 'GTFS_FARE':
    case 'GTFS_SERVICE':
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      } else {
        return false
      }
    case 'URL':
      const isNotUrl = value && !validator.isURL(value)
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      } else if (isNotUrl) {
        return validationIssue('Field must contain valid URL.')
      } else {
        return false
      }
    case 'EMAIL':
      const isNotEmail = value && !validator.isEmail(value)
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      } else if (isNotEmail) {
        return validationIssue('Field must contain valid email address.')
      } else {
        return false
      }
    case 'GTFS_ZONE':
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      } else {
        return false
      }
    case 'TIMEZONE':
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      } else {
        return false
      }
    case 'LANGUAGE':
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      } else {
        return false
      }
    case 'LATITUDE':
      const isNotLat = value > 90 || value < -90
      if (isNotLat) {
        return validationIssue('Field must be valid latitude.')
      }
      if (isOptionalAndEmpty && locationType !== null && (typeof locationType === 'number' && locationType <= 2)) {
        return validationIssue('Latitude and Longitude are required for your current location type')
      }
      return false
    case 'LONGITUDE':
      const isNotLng = value > 180 || value < -180
      if (isNotLng) {
        return validationIssue('Field must be valid longitude.')
      }
      if (isOptionalAndEmpty && typeof locationType === 'number' && locationType <= 2) {
        return validationIssue('Latitude and Longitude are required for your current location type')
      }
      return false
    case 'TIME':
    case 'NUMBER':
      const isNotANumber = isNaN(value)
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      } else if (isNotANumber) {
        return validationIssue('Field must be valid number')
      } else {
        return false
      }
    case 'DAY_OF_WEEK_BOOLEAN':
      let hasService = false
      const DAYS_OF_WEEK: Array<string> = [
        'monday',
        'tuesday',
        'wednesday',
        'thursday',
        'friday',
        'saturday',
        'sunday'
      ]
      for (var i = 0; i < DAYS_OF_WEEK.length; i++) {
        if (entity && entity[DAYS_OF_WEEK[i]]) {
          hasService = true
        }
      }
      if (!hasService && name === 'monday') {
        // only add validation issue for one day of week (monday)
        return validationIssue('Calendar must have service for at least one day')
      }
      return false
    case 'DROPDOWN':
      if (
        isRequiredButEmpty &&
        field.options &&
        field.options.findIndex(o => o.value === '') === -1
      ) {
        return emptyFieldValidationIssue()
      } else {
        return false
      }
    case 'GTFS_AGENCY':
      if (
        name === 'agency_id' &&
        agencies.length > 1
      ) {
        if (valueDoesNotExist) {
          return validationIssue('Field must be populated for feeds with more than one agency.')
        }
      }
      return false
    case 'EXCEPTION_DATE': // a date cannot be selected more than once (for all exceptions)
      const dateMap = {}
      // Clone exceptions to avoid mutating table in store.
      const scheduleExceptions: Array<ScheduleException> = clone(getTableById(tableData, 'scheduleexception'))
      if (entity) {
        const entityId = entity.id
        const exceptionIndex = scheduleExceptions.findIndex(se => se.id === entityId)
        if (exceptionIndex !== -1) {
          scheduleExceptions.splice(exceptionIndex, 1)
        }
        const castedScheduleException: ScheduleException = ((entity: any): ScheduleException)
        scheduleExceptions.push(castedScheduleException)
      }

      for (let i = 0; i < scheduleExceptions.length; i++) {
        if (scheduleExceptions[i].dates) {
          scheduleExceptions[i].dates.map(d => {
            if (typeof dateMap[d] === 'undefined') {
              dateMap[d] = []
            }
            dateMap[d].push(scheduleExceptions[i].id)
          })
        }
      }
      if (!value || value.length === 0) {
        return emptyFieldValidationIssue('dates')
      }
      // check if date already exists in this or other exceptions
      for (let i = 0; i < value.length; i++) {
        const dateItemName = `dates-${i}`
        if (dateMap[value[i]] && dateMap[value[i]].length > 1) {
          // eslint-disable-next-line standard/computed-property-even-spacing
          const reason = `Date (${value[
            i
          ]}) cannot appear more than once for all exceptions`
          return validationIssue(reason, dateItemName)
        } else if (!moment(value[i], 'YYYYMMDD', true).isValid()) {
          return emptyFieldValidationIssue(dateItemName)
        }
      }
      return false
    case 'POSITIVE_INT':
      const positiveNumberCheck = checkPositiveNumber()
      if (positiveNumberCheck.result !== false) return positiveNumberCheck.result
      if (isOptionalAndEmpty) return false

      // check for floating value or decimal point
      if (
        typeof positiveNumberCheck.num === 'number' &&
        (
          positiveNumberCheck.num % 1 > 0 ||
          (
            value.indexOf &&
            value.indexOf('.') > -1
          )
        )
      ) {
        return validationIssue('Field must be a positive integer')
      }
      return false
    case 'POSITIVE_NUM':
      return checkPositiveNumber().result
    case 'GTFS_ROUTE':
    case 'GTFS_STOP':
    case 'DATE':
    case 'COLOR':
    default:
      if (isRequiredButEmpty) {
        return emptyFieldValidationIssue()
      }
      return false
  }
}
<|endoftext|>


<commit_before>'use strict';

angular.module('occupied.controllers', [])

    .controller('HomeController', ['$scope', 'CityData', function($scope, CityData) {
        var places = CityData.getPlaces();
        $scope.cities = places['cities'];
        $scope.countries = places['countries'];
    }])

    .controller('CityController', ['$scope', '$state', '$filter', 'CityData', 'HistoryData', 'MapHelper', function($scope, $state, $filter, CityData, HistoryData, MapHelper) {
        var city = CityData.getCity($state.params['city']);
        if (city === false) {
            $state.go('home');
            return;
        }

        $scope.thisYear = (new Date()).getFullYear();

        var history = HistoryData.get();
        var firstYear = history[0]['year'];
        var offset = $scope.thisYear - firstYear;

        if (!$state.params['year']) {
            $scope.activeYear = firstYear;
        } else {
            $scope.activeYear = $state.params['year'];
        }

        // If the case was wrong, redirect so we have consistent URLs for sharing
        if (city.name !== $state.params['city']) {
            if (!$state.params['year']) {
                $state.go('city', {city: city.name});
            } else {
                $state.go('city.year', {city: city.name, year: $state.params['year']});
            }
        }

        $scope.historyIndex = 0;

        $scope.updateFigures = function() {
            for (var ii = 0; ii < history.length; ii++) {
                if (history[ii].year == $scope.activeYear) {
                    $scope.historyIndex = ii;
                    break;
                }
            }

            // If an invalid year's specified, jump to the start for this city
            if (!!$state.params['year'] && $scope.historyIndex === 0 && $state.params['year'] != firstYear) {
                $state.go('city', {city: city.name}, {reload: true});
            }

            var current = history[$scope.historyIndex];

            $scope.dayHeading = parseInt($scope.activeYear) + offset;
            $scope.refugees = 0;
            $scope.population = parseInt(current.population * populationRatio);
            if ('refugees' in current) {
                $scope.refugees = parseInt(current.refugees * populationRatio);
            }
            if ('settlers' in current) {
                $scope.settlers = parseInt(current.settlers * populationRatio);
            }
            $scope.settlements = 0;
            if ('settlements' in current) {
                $scope.settlements = parseInt(current.settlements);
            }
            if ('settlerControlledArea' in current) {
                var originalArea = history[0]['area'];
                var areaRatio = city.area / originalArea;
                $scope.settlerArea = parseInt(current.settlerControlledArea * areaRatio);
            }

            $scope.events = current['events'];
            $scope.canGoForward = ($scope.historyIndex < history.length - 1);
            $scope.canGoBack = ($scope.historyIndex > 0);

            var mapColour = ($scope.refugees > 0 ? '#f99' : '#9f9');
            var existingColour = ('circle' in $scope.paths) ? $scope.paths.circle.color : $scope.geojson.style.fillColor;

            if (mapColour !== existingColour) {
                // Because we currently only change colour at points where there are no markers, we can afford to do a
                // full map redraw in these cases without breaking the settlement marker positions. We've gone for this
                // compromise because angular-leaflet-directive doesn't seem to update the render when a geojson style
                // changes like it does with other property updates.
                // See https://github.com/tombatossals/angular-leaflet-directive/issues/489
                $scope.createMap();
                return;
            }

            $scope.markers = MapHelper.getUpdatedMarkers(
                $scope.settlements,
                $scope.markers,
                city.lat,
                city.lng,
                city.area,
                $scope.geojson
            );
        };

        var originalPopulation = history[0]['population'];
        var populationRatio = city.population / originalPopulation;

        $scope.progress = function() {
            $state.go('city.year', {city: city.name, year: history[$scope.historyIndex + 1].year}).then(function(newState) {
                $scope.activeYear = $state.params['year'];
                $scope.updateFigures();
            });
        };

        $scope.back = function() {
            $state.go('city.year', {city: city.name, year: history[$scope.historyIndex - 1].year}).then(function(newState) {
                $scope.activeYear = $state.params['year'];
                $scope.updateFigures();
            });
        };

        $scope.restart = function() {
            $state.go('city', {city: city.name}, {reload: true});
        };

        $scope.city = city;
        $state.current.data = {'pageTitle': $state.params['city']};

        $scope.createMap = function() {
            // Sets $scope.centre, $scope.paths, $scope.geojson, $scope.markers
            angular.extend($scope, MapHelper.buildLeafletData(city, $scope.refugees, $scope.settlements));
        };

        $scope.defaults = {scrollWheelZoom: false};
        $scope.createMap();
        $scope.updateFigures();
    }])

    .controller('AboutController', function() {
    })

    .controller('ContactController', function() {
    })

;
<commit_msg>Initialise all scope numbers for 'vitals' - fixes bug with incorrect numbers staying in place when you go Back<commit_after>'use strict';

angular.module('occupied.controllers', [])

    .controller('HomeController', ['$scope', 'CityData', function($scope, CityData) {
        var places = CityData.getPlaces();
        $scope.cities = places['cities'];
        $scope.countries = places['countries'];
    }])

    .controller('CityController', ['$scope', '$state', '$filter', 'CityData', 'HistoryData', 'MapHelper', function($scope, $state, $filter, CityData, HistoryData, MapHelper) {
        var city = CityData.getCity($state.params['city']);
        if (city === false) {
            $state.go('home');
            return;
        }

        $scope.thisYear = (new Date()).getFullYear();

        var history = HistoryData.get();
        var firstYear = history[0]['year'];
        var offset = $scope.thisYear - firstYear;

        if (!$state.params['year']) {
            $scope.activeYear = firstYear;
        } else {
            $scope.activeYear = $state.params['year'];
        }

        // If the case was wrong, redirect so we have consistent URLs for sharing
        if (city.name !== $state.params['city']) {
            if (!$state.params['year']) {
                $state.go('city', {city: city.name});
            } else {
                $state.go('city.year', {city: city.name, year: $state.params['year']});
            }
        }

        $scope.historyIndex = 0;

        $scope.updateFigures = function() {
            for (var ii = 0; ii < history.length; ii++) {
                if (history[ii].year == $scope.activeYear) {
                    $scope.historyIndex = ii;
                    break;
                }
            }

            // If an invalid year's specified, jump to the start for this city
            if (!!$state.params['year'] && $scope.historyIndex === 0 && $state.params['year'] != firstYear) {
                $state.go('city', {city: city.name}, {reload: true});
            }

            var current = history[$scope.historyIndex];

            $scope.dayHeading = parseInt($scope.activeYear) + offset;
            $scope.refugees = 0;
            $scope.settlers = 0;
            $scope.settlements = 0;
            $scope.settlerArea = 0;

            $scope.population = parseInt(current.population * populationRatio);
            if ('refugees' in current) {
                $scope.refugees = parseInt(current.refugees * populationRatio);
            }
            if ('settlers' in current) {
                $scope.settlers = parseInt(current.settlers * populationRatio);
            }
            if ('settlements' in current) {
                $scope.settlements = parseInt(current.settlements);
            }
            if ('settlerControlledArea' in current) {
                var originalArea = history[0]['area'];
                var areaRatio = city.area / originalArea;
                $scope.settlerArea = parseInt(current.settlerControlledArea * areaRatio);
            }

            $scope.events = current['events'];
            $scope.canGoForward = ($scope.historyIndex < history.length - 1);
            $scope.canGoBack = ($scope.historyIndex > 0);

            var mapColour = ($scope.refugees > 0 ? '#f99' : '#9f9');
            var existingColour = ('circle' in $scope.paths) ? $scope.paths.circle.color : $scope.geojson.style.fillColor;

            if (mapColour !== existingColour) {
                // Because we currently only change colour at points where there are no markers, we can afford to do a
                // full map redraw in these cases without breaking the settlement marker positions. We've gone for this
                // compromise because angular-leaflet-directive doesn't seem to update the render when a geojson style
                // changes like it does with other property updates.
                // See https://github.com/tombatossals/angular-leaflet-directive/issues/489
                $scope.createMap();
                return;
            }

            $scope.markers = MapHelper.getUpdatedMarkers(
                $scope.settlements,
                $scope.markers,
                city.lat,
                city.lng,
                city.area,
                $scope.geojson
            );
        };

        var originalPopulation = history[0]['population'];
        var populationRatio = city.population / originalPopulation;

        $scope.progress = function() {
            $state.go('city.year', {city: city.name, year: history[$scope.historyIndex + 1].year}).then(function(newState) {
                $scope.activeYear = $state.params['year'];
                $scope.updateFigures();
            });
        };

        $scope.back = function() {
            $state.go('city.year', {city: city.name, year: history[$scope.historyIndex - 1].year}).then(function(newState) {
                $scope.activeYear = $state.params['year'];
                $scope.updateFigures();
            });
        };

        $scope.restart = function() {
            $state.go('city', {city: city.name}, {reload: true});
        };

        $scope.city = city;
        $state.current.data = {'pageTitle': $state.params['city']};

        $scope.createMap = function() {
            // Sets $scope.centre, $scope.paths, $scope.geojson, $scope.markers
            angular.extend($scope, MapHelper.buildLeafletData(city, $scope.refugees, $scope.settlements));
        };

        $scope.defaults = {scrollWheelZoom: false};
        $scope.createMap();
        $scope.updateFigures();
    }])

    .controller('AboutController', function() {
    })

    .controller('ContactController', function() {
    })

;
<|endoftext|>


<commit_before>import React from 'react'
import Relay from 'react-relay'
import {BernieColors, BernieText} from './styles/bernie-css'
import {Paper, Styles, FlatButton, LinearProgress} from 'material-ui'
import GCForm from './forms/GCForm'
import Form from 'react-formal'
import EventPreview from './EventPreview'
import Dropzone from 'react-dropzone'
import yup from 'yup'
import moment from 'moment'
import MuiThemeProvider from 'material-ui/lib/MuiThemeProvider'
import {BernieTheme} from './styles/bernie-theme'
import stripScripts from '../helpers/stripScripts'
import qs from 'querystring'
import superagent from 'superagent'

const momentWithTimezone = (startDate, timeZone) => {
  return moment(startDate).tz(timeZone)
}

class EventDataUpload extends React.Component {

  constructor(props) {
    super(props)

    this.state = {
      fileType: null,
      filesProcessed: {}
    }
  }

  styles = {
    detailsContainer: {
      float: 'left',
      marginTop: '1rem',
      padding: 10,
      width: '50%'
    },

    formContainer: {
      float: 'right',
      marginTop: '1rem',
      padding: 10,
      width: '40%'
    },

    pageContainer: {
      margin: '1rem'
    },

    dropzoneStyle: {
      font: BernieText.default,
      fontSize: '1.5em',
      color: BernieColors.green,
      margin: '0 auto',
      marginTop: '1em',
      width: '100%',
      minHeight: 300,
      borderWidth: 2,
      borderColor: BernieColors.green,
      borderStyle: 'dashed',
      borderRadius: 5,
      position: 'relative',
      textAlign: 'center',
      overflow: 'scroll'
    },

    dropzoneActiveStyle: {
      borderStyle: 'solid',
      backgroundColor: '#eee'
    },

    dropzoneRejectStyle: {
      borderStyle: 'solid',
      backgroundColor: '#ffdddd'
    },

    fileStatus: {
      ...BernieText.secondaryTitle,
      fontSize: '0.75em',
      textAlign: 'left',
    }
  }

  formSchema = yup.object({
    fileType: yup.string().required()
  })

  onDrop = (files) => {
    files.forEach((file) => {
      const processObj = {
        errors: []
      }
      let currentFiles = this.state.filesProcessed
      currentFiles[file.name] = processObj
      this.setState({filesProcessed: currentFiles})

      this.getSignedRequest(file)
    })
  }

  getSignedRequest = (file) => {
    const {name, type} = file
    const eventId = this.props.event.eventIdObfuscated
    superagent.get(`/events/${eventId}/upload/get-signed-request?${qs.stringify({name, type, eventId})}`)
      .end((err, res) => {
        if (err)
          return processObj.errors.push(res.body)
        this.uploadFile(file, res.body)
      })
  }

  uploadFile(file, signedRequest) {
    const {signedRequestEndpoint, fileUrl} = signedRequest
    superagent.put(signedRequestEndpoint)
      .set('x-amz-acl', 'public-read')
      .send(file)
      .end((err, res) => {
        let filesProcessed = this.state.filesProcessed
        let processObj = filesProcessed[file.name]

        if (err){
          processObj.errors = []
          processObj.errors.push(res.text)
        }

        processObj.url = fileUrl
        filesProcessed[file.name] = processObj
        this.setState(filesProcessed)
      })
  }

  renderDropZone() {
    if (this.state.fileType !== null){
      return (
        <Dropzone
          ref="dropzone"
          onDrop={this.onDrop}
          style={this.styles.dropzoneStyle}
          activeStyle={this.styles.dropzoneActiveStyle}
          rejectStyle={this.styles.dropzoneRejectStyle}
          disableClick={true}
        >

              Drop your Event Files here!

              <FlatButton
                label="Select Files to Upload"
                onTouchTap={() => this.refs.dropzone.open()}
              />

          {this.renderFileProgress()}
        </Dropzone>
      )
    }
    else
      return

  }

  renderFileProgress() {
    let count = 0
    const renderFileLink = (fileObj, fileName) => {
      if (fileObj.url)
        return {fileName}
      else
        return (

            {fileName}
            <LinearProgress mode="indeterminate"/>

        )
    }
    const renderErrors = (fileObj) => {
      if (fileObj.errors.length === 0)
        return

      return (

          {fileObj.errors.map((error, index) => {
            return (

                {`Here's some info`}

                {error}

            )
          })}

      )
    }
    let fileNodes = Object.keys(this.state.filesProcessed).map((fileName, index) => {
      count = count + 1
      let fileObj = this.state.filesProcessed[fileName]
      let color = (fileObj.errors.length === 0 ? (false ? BernieColors.green : BernieColors.gray) : BernieColors.red)

      return (

            {renderFileLink(fileObj, fileName)}
            {renderErrors(fileObj)}

      )
    })
    return (

        {fileNodes}

    )
  }

  render() {
    const event = this.props.event
    let formattedDateTime = momentWithTimezone(event.startDate, event.localTimezone)
    formattedDateTime = formattedDateTime ? formattedDateTime.format('LLLL') : event.startDate

    if (!event)
      return

Invalid Event

This event does not exist. If you've just recently created your event.
                this error may resolve itself in a short period of time. It's also
                possible your event was deleted. Please email help@berniesanders.com if you need help.

    return (
      <MuiThemeProvider muiTheme={Styles.getMuiTheme(BernieTheme)}>

{event.name}

{formattedDateTime}

              <GCForm
                schema={this.formSchema}
                onSubmit={(formValues) => {
                  this.refs.mutationHandler.send({
                    eventId: this.props.event.id,
                    ...formValues
                  })
                }}
              >

Upload Event Data

                <Form.Field
                  name='fileType'
                  type='select'
                  label='What are you uploading?'
                  fullWidth={true}
                  choices={{
                    eventPhotos: 'Event Photos',
                    signInSheets: 'Sign-In Sheets',
                    phonebankEventForms: 'Phonebank Event Forms'
                  }}
                  value={this.state.fileType}
                  onChange={(fileType) => {
                    this.setState({fileType})
                  }}
                />

                {this.renderDropZone()}

              </GCForm>

      </MuiThemeProvider>
    )
  }
}

export default Relay.createContainer(EventDataUpload, {
  fragments: {
    currentUser: () => Relay.QL`
      fragment on User {
        id
        email
      }
    `,
    event: () => Relay.QL`
      fragment on Event {
        attendeesCount
        attendeeVolunteerMessage
        attendeeVolunteerShow
        fastFwdRequest{
          hostMessage
        }
        capacity
        contactPhone
        createDate
        description
        duration
        eventIdObfuscated
        eventType {
          id
          name
        }
        flagApproval
        host {
          id
          firstName
          lastName
          email
        }
        hostReceiveRsvpEmails
        id
        isSearchable
        latitude
        localTimezone
        localUTCOffset
        longitude
        name
        nearbyPeople {
          id
          firstName
          lastName
          email
        }
        publicPhone
        rsvpEmailReminderHours
        rsvpUseReminderEmail
        startDate
        venueAddr1
        venueAddr2
        venueCity
        venueCountry
        venueDirections
        venueName
        venueState
        venueZip
      }
    `
  }
})
<commit_msg>Add file preview uri to process object<commit_after>import React from 'react'
import Relay from 'react-relay'
import {BernieColors, BernieText} from './styles/bernie-css'
import {Paper, Styles, FlatButton, LinearProgress} from 'material-ui'
import GCForm from './forms/GCForm'
import Form from 'react-formal'
import EventPreview from './EventPreview'
import Dropzone from 'react-dropzone'
import yup from 'yup'
import moment from 'moment'
import MuiThemeProvider from 'material-ui/lib/MuiThemeProvider'
import {BernieTheme} from './styles/bernie-theme'
import stripScripts from '../helpers/stripScripts'
import qs from 'querystring'
import superagent from 'superagent'

const momentWithTimezone = (startDate, timeZone) => {
  return moment(startDate).tz(timeZone)
}

class EventDataUpload extends React.Component {

  constructor(props) {
    super(props)

    this.state = {
      fileType: null,
      filesProcessed: {}
    }
  }

  styles = {
    detailsContainer: {
      float: 'left',
      marginTop: '1rem',
      padding: 10,
      width: '50%'
    },

    formContainer: {
      float: 'right',
      marginTop: '1rem',
      padding: 10,
      width: '40%'
    },

    pageContainer: {
      margin: '1rem'
    },

    dropzoneStyle: {
      font: BernieText.default,
      fontSize: '1.5em',
      color: BernieColors.green,
      margin: '0 auto',
      marginTop: '1em',
      width: '100%',
      minHeight: 300,
      borderWidth: 2,
      borderColor: BernieColors.green,
      borderStyle: 'dashed',
      borderRadius: 5,
      position: 'relative',
      textAlign: 'center',
      overflow: 'scroll'
    },

    dropzoneActiveStyle: {
      borderStyle: 'solid',
      backgroundColor: '#eee'
    },

    dropzoneRejectStyle: {
      borderStyle: 'solid',
      backgroundColor: '#ffdddd'
    },

    fileStatus: {
      ...BernieText.secondaryTitle,
      fontSize: '0.75em',
      textAlign: 'left',
    }
  }

  formSchema = yup.object({
    fileType: yup.string().required()
  })

  onDrop = (files) => {
    files.forEach((file) => {
      const processObj = {
        errors: [],
        preview: file.preview
      }
      let currentFiles = this.state.filesProcessed
      currentFiles[file.name] = processObj
      this.setState({filesProcessed: currentFiles})

      this.getSignedRequest(file)
    })
  }

  getSignedRequest = (file) => {
    const {name, type} = file
    const eventId = this.props.event.eventIdObfuscated
    superagent.get(`/events/${eventId}/upload/get-signed-request?${qs.stringify({name, type, eventId})}`)
      .end((err, res) => {
        if (err)
          return processObj.errors.push(res.body)
        this.uploadFile(file, res.body)
      })
  }

  uploadFile(file, signedRequest) {
    const {signedRequestEndpoint, fileUrl} = signedRequest
    superagent.put(signedRequestEndpoint)
      .set('x-amz-acl', 'public-read')
      .send(file)
      .end((err, res) => {
        let filesProcessed = this.state.filesProcessed
        let processObj = filesProcessed[file.name]

        if (err){
          processObj.errors = []
          processObj.errors.push(res.text)
        }

        processObj.url = fileUrl
        filesProcessed[file.name] = processObj
        this.setState(filesProcessed)
      })
  }

  renderDropZone() {
    if (this.state.fileType !== null){
      return (
        <Dropzone
          ref="dropzone"
          onDrop={this.onDrop}
          style={this.styles.dropzoneStyle}
          activeStyle={this.styles.dropzoneActiveStyle}
          rejectStyle={this.styles.dropzoneRejectStyle}
          disableClick={true}
        >

              Drop your Event Files here!

              <FlatButton
                label="Select Files to Upload"
                onTouchTap={() => this.refs.dropzone.open()}
              />

          {this.renderFileProgress()}
        </Dropzone>
      )
    }
    else
      return

  }

  renderFileProgress() {
    let count = 0
    const renderFileLink = (fileObj, fileName) => {
      if (fileObj.url)
        return {fileName}
      else
        return (

            {fileName}
            <LinearProgress mode="indeterminate"/>

        )
    }
    const renderErrors = (fileObj) => {
      if (fileObj.errors.length === 0)
        return

      return (

          {fileObj.errors.map((error, index) => {
            return (

                {`Here's some info`}

                {error}

            )
          })}

      )
    }
    let fileNodes = Object.keys(this.state.filesProcessed).map((fileName, index) => {
      count = count + 1
      let fileObj = this.state.filesProcessed[fileName]
      let color = (fileObj.errors.length === 0 ? (false ? BernieColors.green : BernieColors.gray) : BernieColors.red)

      return (

            {renderFileLink(fileObj, fileName)}
            {renderErrors(fileObj)}

      )
    })
    return (

        {fileNodes}

    )
  }

  render() {
    const event = this.props.event
    let formattedDateTime = momentWithTimezone(event.startDate, event.localTimezone)
    formattedDateTime = formattedDateTime ? formattedDateTime.format('LLLL') : event.startDate

    if (!event)
      return

Invalid Event

This event does not exist. If you've just recently created your event.
                this error may resolve itself in a short period of time. It's also
                possible your event was deleted. Please email help@berniesanders.com if you need help.

    return (
      <MuiThemeProvider muiTheme={Styles.getMuiTheme(BernieTheme)}>

{event.name}

{formattedDateTime}

              <GCForm
                schema={this.formSchema}
                onSubmit={(formValues) => {
                  this.refs.mutationHandler.send({
                    eventId: this.props.event.id,
                    ...formValues
                  })
                }}
              >

Upload Event Data

                <Form.Field
                  name='fileType'
                  type='select'
                  label='What are you uploading?'
                  fullWidth={true}
                  choices={{
                    eventPhotos: 'Event Photos',
                    signInSheets: 'Sign-In Sheets',
                    phonebankEventForms: 'Phonebank Event Forms'
                  }}
                  value={this.state.fileType}
                  onChange={(fileType) => {
                    this.setState({fileType})
                  }}
                />

                {this.renderDropZone()}

              </GCForm>

      </MuiThemeProvider>
    )
  }
}

export default Relay.createContainer(EventDataUpload, {
  fragments: {
    currentUser: () => Relay.QL`
      fragment on User {
        id
        email
      }
    `,
    event: () => Relay.QL`
      fragment on Event {
        attendeesCount
        attendeeVolunteerMessage
        attendeeVolunteerShow
        fastFwdRequest{
          hostMessage
        }
        capacity
        contactPhone
        createDate
        description
        duration
        eventIdObfuscated
        eventType {
          id
          name
        }
        flagApproval
        host {
          id
          firstName
          lastName
          email
        }
        hostReceiveRsvpEmails
        id
        isSearchable
        latitude
        localTimezone
        localUTCOffset
        longitude
        name
        nearbyPeople {
          id
          firstName
          lastName
          email
        }
        publicPhone
        rsvpEmailReminderHours
        rsvpUseReminderEmail
        startDate
        venueAddr1
        venueAddr2
        venueCity
        venueCountry
        venueDirections
        venueName
        venueState
        venueZip
      }
    `
  }
})
<|endoftext|>


<commit_before>(function() {
    'use strict';
    angular.module('theHiveControllers')
        .controller('AlertListCtrl', function($rootScope, $scope, $q, $state, $uibModal, TagSrv, CaseTemplateSrv, AlertingSrv, NotificationSrv, FilteringSrv, CortexSrv, Severity, VersionSrv) {
            var self = this;

            self.urls = VersionSrv.mispUrls();

            self.list = [];
            self.selection = [];
            self.menu = {
                follow: false,
                unfollow: false,
                markAsRead: false,
                markAsUnRead: false,
                selectAll: false
            };
            self.filtering = new FilteringSrv('alert-section', {
                defaults: {
                    showFilters: false,
                    showStats: false,
                    pageSize: 15,
                    sort: ['-date']
                },
                defaultFilter: {
                    status: {
                        field: 'status',
                        label: 'Status',
                        value: [{
                            text: 'New'
                        }, {
                            text: 'Updated'
                        }],
                        filter: '(status:"New" OR status:"Updated")'
                    }
                },
                filterDefs: {
                    keyword: {
                        field: 'keyword',
                        type: 'string',
                        defaultValue: []
                    },
                    status: {
                        field: 'status',
                        type: 'list',
                        defaultValue: [],
                        label: 'Status'
                    },
                    tags: {
                        field: 'tags',
                        type: 'list',
                        defaultValue: [],
                        label: 'Tags'
                    },
                    source: {
                        field: 'source',
                        type: 'list',
                        defaultValue: [],
                        label: 'Source'
                    },
                    type: {
                        field: 'type',
                        type: 'list',
                        defaultValue: [],
                        label: 'Type'
                    },
                    severity: {
                        field: 'severity',
                        type: 'list',
                        defaultValue: [],
                        label: 'Severity',
                        convert: function(value) {
                            // Convert the text value to its numeric representation
                            return Severity.keys[value];
                        }
                    },
                    title: {
                        field: 'title',
                        type: 'string',
                        defaultValue: '',
                        label: 'Title'
                    },
                    sourceRef: {
                        field: 'sourceRef',
                        type: 'string',
                        defaultValue: '',
                        label: 'Reference'
                    },
                    date: {
                        field: 'date',
                        type: 'date',
                        defaultValue: {
                            from: null,
                            to: null
                        },
                        label: 'Date'
                    }
                }
            });
            self.filtering.initContext('list');
            self.searchForm = {
                searchQuery: self.filtering.buildQuery() || ''
            };
            self.lastSearch = null;
            self.responders = null;

            $scope.$watch('$vm.list.pageSize', function (newValue) {
                self.filtering.setPageSize(newValue);
            });

            this.toggleStats = function () {
                this.filtering.toggleStats();
            };

            this.toggleFilters = function () {
                this.filtering.toggleFilters();
            };

            this.canMarkAsRead = AlertingSrv.canMarkAsRead;
            this.canMarkAsUnread = AlertingSrv.canMarkAsUnread;

            this.markAsRead = function(event) {
                var fn = angular.noop;

                if(this.canMarkAsRead(event)) {
                    fn = AlertingSrv.markAsRead;
                } else {
                    fn = AlertingSrv.markAsUnread;
                }

                fn(event.id).then(function( /*data*/ ) {
                }, function(response) {
                    NotificationSrv.error('AlertListCtrl', response.data, response.status);
                });
            };

            self.follow = function(event) {
                var fn = angular.noop;

                if (event.follow === true) {
                    fn = AlertingSrv.unfollow;
                } else {
                    fn = AlertingSrv.follow;
                }

                fn(event.id).then(function( /*data*/ ) {
                }, function(response) {
                    NotificationSrv.error('AlertListCtrl', response.data, response.status);
                });
            };

            self.bulkFollow = function(follow) {
                var ids = _.pluck(self.selection, 'id');
                var fn = angular.noop;

                if (follow === true) {
                    fn = AlertingSrv.follow;
                } else {
                    fn = AlertingSrv.unfollow;
                }

                var promises = _.map(ids, function(id) {
                    return fn(id);
                });

                $q.all(promises).then(function( /*response*/ ) {
                    NotificationSrv.log('The selected events have been ' + (follow ? 'followed' : 'unfollowed'), 'success');
                }, function(response) {
                    NotificationSrv.error('AlertListCtrl', response.data, response.status);
                });
            };

            self.bulkMarkAsRead = function(markAsReadFlag) {
                var ids = _.pluck(self.selection, 'id');
                var fn = angular.noop;
                var markAsRead = markAsReadFlag && this.canMarkAsRead(self.selection[0]);

                if(markAsRead) {
                    fn = AlertingSrv.markAsRead;
                } else {
                    fn = AlertingSrv.markAsUnread;
                }

                var promises = _.map(ids, function(id) {
                    return fn(id);
                });

                $q.all(promises).then(function( /*response*/ ) {
                    self.list.update();
                    NotificationSrv.log('The selected events have been ' + (markAsRead ? 'marked as read' : 'marked as unread'), 'success');
                }, function(response) {
                    NotificationSrv.error('AlertListCtrl', response.data, response.status);
                });
            };

            self.import = function(event) {
                $uibModal.open({
                    templateUrl: 'views/partials/alert/event.dialog.html',
                    controller: 'AlertEventCtrl',
                    controllerAs: 'dialog',
                    size: 'max',
                    resolve: {
                        event: event,
                        templates: function() {
                            return CaseTemplateSrv.list();
                        }
                    }
                });
            };

            self.resetSelection = function() {
                if (self.menu.selectAll) {
                    self.selectAll();
                } else {
                    self.selection = [];
                    self.menu.selectAll = false;
                    self.updateMenu();
                }
            };

            this.getResponders = function(eventId, force) {
                if(!force && this.responders !== null) {
                   return;
                }

                this.responders = null;
                CortexSrv.getResponders('alert', eventId)
                  .then(function(responders) {
                      self.responders = responders;
                  })
                  .catch(function(err) {
                      NotificationSrv.error('AlertList', err.data, err.status);
                  });
            };

            this.runResponder = function(responderId, responderName, event) {
                CortexSrv.runResponder(responderId, responderName, 'alert', _.pick(event, 'id', 'tlp'))
                  .then(function(response) {
                      NotificationSrv.log(['Responder', response.data.responderName, 'started successfully on alert', event.title].join(' '), 'success');
                  })
                  .catch(function(response) {
                      if(response && !_.isString(response)) {
                          NotificationSrv.error('CaseList', response.data, response.status);
                      }
                  });
            };

            self.load = function() {
                var config = {
                    scope: $scope,
                    filter: self.searchForm.searchQuery !== '' ? {
                        _string: self.searchForm.searchQuery
                    } : '',
                    loadAll: false,
                    sort: self.filtering.context.sort,
                    pageSize: self.filtering.context.pageSize,
                };

                self.list = AlertingSrv.list(config, self.resetSelection);
            };

            self.cancel = function() {
                self.modalInstance.close();
            };

            self.updateMenu = function() {
                var temp = _.uniq(_.pluck(self.selection, 'follow'));

                self.menu.unfollow = temp.length === 1 && temp[0] === true;
                self.menu.follow = temp.length === 1 && temp[0] === false;

                temp = _.uniq(_.pluck(self.selection, 'status'));

                self.menu.markAsRead = temp.indexOf('Ignored') === -1 && temp.indexOf('Imported') === -1;
                self.menu.markAsUnread = temp.indexOf('New') === -1 && temp.indexOf('Updated') === -1;

                self.menu.createNewCase = temp.indexOf('Imported') === -1;
                self.menu.mergeInCase = temp.indexOf('Imported') === -1;

            };

            self.select = function(event) {
                if (event.selected) {
                    self.selection.push(event);
                } else {
                    self.selection = _.reject(self.selection, function(item) {
                        return item.id === event.id;
                    });
                }

                self.updateMenu();

            };

            self.selectAll = function() {
                var selected = self.menu.selectAll;
                _.each(self.list.values, function(item) {
                    item.selected = selected;
                });

                if (selected) {
                    self.selection = self.list.values;
                } else {
                    self.selection = [];
                }

                self.updateMenu();

            };

            self.createNewCase = function() {
                var alertIds = _.pluck(self.selection, 'id');

                CaseTemplateSrv.list()
                  .then(function(templates) {

                      // Open template selection dialog
                      var modal = $uibModal.open({
                          templateUrl: 'views/partials/case/case.templates.selector.html',
                          controller: 'CaseTemplatesDialogCtrl',
                          controllerAs: 'dialog',
                          size: 'lg',
                          resolve: {
                              templates: function(){
                                  return templates;
                              },
                              uiSettings: function(UiSettingsSrv) {
                                  return UiSettingsSrv.all();
                              }
                          }
                      });

                      return modal.result;
                  })
                  .then(function(template) {

                      // Open case creation dialog
                      var modal = $uibModal.open({
                          templateUrl: 'views/partials/case/case.creation.html',
                          controller: 'CaseCreationCtrl',
                          size: 'lg',
                          resolve: {
                              template: template
                          }
                      });

                      return modal.result;
                  })
                  .then(function(createdCase) {
                      // Bulk merge the selected alerts into the created case
                      NotificationSrv.log('New case has been created', 'success');

                      return AlertingSrv.bulkMergeInto(alertIds, createdCase.id);
                  })
                  .then(function(response) {
                      if(alertIds.length === 1) {
                          NotificationSrv.log(alertIds.length + ' Alert has been merged into the newly created case.', 'success');
                      } else {
                          NotificationSrv.log(alertIds.length + ' Alert(s) have been merged into the newly created case.', 'success');
                      }

                      $rootScope.$broadcast('alert:event-imported');

                      $state.go('app.case.details', {
                          caseId: response.data.id
                      });
                  })
                  .catch(function(err) {
                      if(err && !_.isString(err)) {
                          NotificationSrv.error('AlertEventCtrl', err.data, err.status);
                      }
                  });

            };

            self.mergeInCase = function() {
                var caseModal = $uibModal.open({
                    templateUrl: 'views/partials/case/case.merge.html',
                    controller: 'CaseMergeModalCtrl',
                    controllerAs: 'dialog',
                    size: 'lg',
                    resolve: {
                        source: function() {
                            return self.event;
                        },
                        title: function() {
                            return 'Merge selected Alert(s)';
                        },
                        prompt: function() {
                            return 'the ' + self.selection.length + ' selected Alert(s)';
                        }
                    }
                });

                caseModal.result.then(function(selectedCase) {
                    return AlertingSrv.bulkMergeInto(_.pluck(self.selection, 'id'), selectedCase.id);
                })
                .then(function(response) {
                    $rootScope.$broadcast('alert:event-imported');

                    $state.go('app.case.details', {
                        caseId: response.data.id
                    });
                })
                .catch(function(err) {
                    if(err && !_.isString(err)) {
                        NotificationSrv.error('AlertEventCtrl', err.data, err.status);
                    }
                });
            };

            this.filter = function () {
                self.filtering.filter().then(this.applyFilters);
            };

            this.applyFilters = function () {
                self.searchForm.searchQuery = self.filtering.buildQuery();

                if(self.lastSearch !== self.searchForm.searchQuery) {
                    self.lastSearch = self.searchForm.searchQuery;
                    self.search();
                }
            };

            this.clearFilters = function () {
                self.filtering.clearFilters().then(this.applyFilters);
            };

            this.addFilter = function (field, value) {
                self.filtering.addFilter(field, value).then(this.applyFilters);
            };

            this.removeFilter = function (field) {
                self.filtering.removeFilter(field).then(this.applyFilters);
            };

            this.search = function () {
                this.list.filter = {
                    _string: this.searchForm.searchQuery
                };

                this.list.update();
            };
            this.addFilterValue = function (field, value) {
                var filterDef = self.filtering.filterDefs[field];
                var filter = self.filtering.activeFilters[field];
                var date;

                if (filter && filter.value) {
                    if (filterDef.type === 'list') {
                        if (_.pluck(filter.value, 'text').indexOf(value) === -1) {
                            filter.value.push({
                                text: value
                            });
                        }
                    } else if (filterDef.type === 'date') {
                        date = moment(value);
                        self.filtering.activeFilters[field] = {
                            value: {
                                from: date.hour(0).minutes(0).seconds(0).toDate(),
                                to: date.hour(23).minutes(59).seconds(59).toDate()
                            }
                        };
                    } else {
                        filter.value = value;
                    }
                } else {
                    if (filterDef.type === 'list') {
                        self.filtering.activeFilters[field] = {
                            value: [{
                                text: value
                            }]
                        };
                    } else if (filterDef.type === 'date') {
                        date = moment(value);
                        self.filtering.activeFilters[field] = {
                            value: {
                                from: date.hour(0).minutes(0).seconds(0).toDate(),
                                to: date.hour(23).minutes(59).seconds(59).toDate()
                            }
                        };
                    } else {
                        self.filtering.activeFilters[field] = {
                            value: value
                        };
                    }
                }

                this.filter();
            };

            this.filterByStatus = function(status) {
                self.filtering.clearFilters()
                    .then(function(){
                        self.addFilterValue('status', status);
                    });
            };

            this.filterBySeverity = function(numericSev) {
                self.addFilterValue('severity', Severity.values[numericSev]);
            };

            this.sortBy = function(sort) {
                self.list.sort = sort;
                self.list.update();
                self.filtering.setSort(sort);
            };

            this.sortByField = function(field) {
                var currentSort = Array.isArray(this.filtering.context.sort) ? this.filtering.context.sort[0] : this.filtering.context.sort;
                var sort = null;

                if(currentSort.substr(1) !== field) {
                    sort = ['+' + field];
                } else {
                    sort = [(currentSort === '+' + field) ? '-'+field : '+'+field];
                }

                self.list.sort = sort;
                self.list.update();
                self.filtering.setSort(sort);
            };

            this.getSeverities = self.filtering.getSeverities;

            this.getStatuses = function(query) {
                return AlertingSrv.statuses(query);
            };

            this.getTypes = function(query) {
                return AlertingSrv.types(query);
            };

            this.getSources = function(query) {
                return AlertingSrv.sources(query);
            };

            this.getTags = function(query) {
                return TagSrv.fromAlerts(query);
            };

            self.load();
        });
})();
<commit_msg>#893 Skip case template section when creating a case from alerts, if there are no case templates<commit_after>(function() {
    'use strict';
    angular.module('theHiveControllers')
        .controller('AlertListCtrl', function($rootScope, $scope, $q, $state, $uibModal, TagSrv, CaseTemplateSrv, AlertingSrv, NotificationSrv, FilteringSrv, CortexSrv, Severity, VersionSrv) {
            var self = this;

            self.urls = VersionSrv.mispUrls();

            self.list = [];
            self.selection = [];
            self.menu = {
                follow: false,
                unfollow: false,
                markAsRead: false,
                markAsUnRead: false,
                selectAll: false
            };
            self.filtering = new FilteringSrv('alert-section', {
                defaults: {
                    showFilters: false,
                    showStats: false,
                    pageSize: 15,
                    sort: ['-date']
                },
                defaultFilter: {
                    status: {
                        field: 'status',
                        label: 'Status',
                        value: [{
                            text: 'New'
                        }, {
                            text: 'Updated'
                        }],
                        filter: '(status:"New" OR status:"Updated")'
                    }
                },
                filterDefs: {
                    keyword: {
                        field: 'keyword',
                        type: 'string',
                        defaultValue: []
                    },
                    status: {
                        field: 'status',
                        type: 'list',
                        defaultValue: [],
                        label: 'Status'
                    },
                    tags: {
                        field: 'tags',
                        type: 'list',
                        defaultValue: [],
                        label: 'Tags'
                    },
                    source: {
                        field: 'source',
                        type: 'list',
                        defaultValue: [],
                        label: 'Source'
                    },
                    type: {
                        field: 'type',
                        type: 'list',
                        defaultValue: [],
                        label: 'Type'
                    },
                    severity: {
                        field: 'severity',
                        type: 'list',
                        defaultValue: [],
                        label: 'Severity',
                        convert: function(value) {
                            // Convert the text value to its numeric representation
                            return Severity.keys[value];
                        }
                    },
                    title: {
                        field: 'title',
                        type: 'string',
                        defaultValue: '',
                        label: 'Title'
                    },
                    sourceRef: {
                        field: 'sourceRef',
                        type: 'string',
                        defaultValue: '',
                        label: 'Reference'
                    },
                    date: {
                        field: 'date',
                        type: 'date',
                        defaultValue: {
                            from: null,
                            to: null
                        },
                        label: 'Date'
                    }
                }
            });
            self.filtering.initContext('list');
            self.searchForm = {
                searchQuery: self.filtering.buildQuery() || ''
            };
            self.lastSearch = null;
            self.responders = null;

            $scope.$watch('$vm.list.pageSize', function (newValue) {
                self.filtering.setPageSize(newValue);
            });

            this.toggleStats = function () {
                this.filtering.toggleStats();
            };

            this.toggleFilters = function () {
                this.filtering.toggleFilters();
            };

            this.canMarkAsRead = AlertingSrv.canMarkAsRead;
            this.canMarkAsUnread = AlertingSrv.canMarkAsUnread;

            this.markAsRead = function(event) {
                var fn = angular.noop;

                if(this.canMarkAsRead(event)) {
                    fn = AlertingSrv.markAsRead;
                } else {
                    fn = AlertingSrv.markAsUnread;
                }

                fn(event.id).then(function( /*data*/ ) {
                }, function(response) {
                    NotificationSrv.error('AlertListCtrl', response.data, response.status);
                });
            };

            self.follow = function(event) {
                var fn = angular.noop;

                if (event.follow === true) {
                    fn = AlertingSrv.unfollow;
                } else {
                    fn = AlertingSrv.follow;
                }

                fn(event.id).then(function( /*data*/ ) {
                }, function(response) {
                    NotificationSrv.error('AlertListCtrl', response.data, response.status);
                });
            };

            self.bulkFollow = function(follow) {
                var ids = _.pluck(self.selection, 'id');
                var fn = angular.noop;

                if (follow === true) {
                    fn = AlertingSrv.follow;
                } else {
                    fn = AlertingSrv.unfollow;
                }

                var promises = _.map(ids, function(id) {
                    return fn(id);
                });

                $q.all(promises).then(function( /*response*/ ) {
                    NotificationSrv.log('The selected events have been ' + (follow ? 'followed' : 'unfollowed'), 'success');
                }, function(response) {
                    NotificationSrv.error('AlertListCtrl', response.data, response.status);
                });
            };

            self.bulkMarkAsRead = function(markAsReadFlag) {
                var ids = _.pluck(self.selection, 'id');
                var fn = angular.noop;
                var markAsRead = markAsReadFlag && this.canMarkAsRead(self.selection[0]);

                if(markAsRead) {
                    fn = AlertingSrv.markAsRead;
                } else {
                    fn = AlertingSrv.markAsUnread;
                }

                var promises = _.map(ids, function(id) {
                    return fn(id);
                });

                $q.all(promises).then(function( /*response*/ ) {
                    self.list.update();
                    NotificationSrv.log('The selected events have been ' + (markAsRead ? 'marked as read' : 'marked as unread'), 'success');
                }, function(response) {
                    NotificationSrv.error('AlertListCtrl', response.data, response.status);
                });
            };

            self.import = function(event) {
                $uibModal.open({
                    templateUrl: 'views/partials/alert/event.dialog.html',
                    controller: 'AlertEventCtrl',
                    controllerAs: 'dialog',
                    size: 'max',
                    resolve: {
                        event: event,
                        templates: function() {
                            return CaseTemplateSrv.list();
                        }
                    }
                });
            };

            self.resetSelection = function() {
                if (self.menu.selectAll) {
                    self.selectAll();
                } else {
                    self.selection = [];
                    self.menu.selectAll = false;
                    self.updateMenu();
                }
            };

            this.getResponders = function(eventId, force) {
                if(!force && this.responders !== null) {
                   return;
                }

                this.responders = null;
                CortexSrv.getResponders('alert', eventId)
                  .then(function(responders) {
                      self.responders = responders;
                  })
                  .catch(function(err) {
                      NotificationSrv.error('AlertList', err.data, err.status);
                  });
            };

            this.runResponder = function(responderId, responderName, event) {
                CortexSrv.runResponder(responderId, responderName, 'alert', _.pick(event, 'id', 'tlp'))
                  .then(function(response) {
                      NotificationSrv.log(['Responder', response.data.responderName, 'started successfully on alert', event.title].join(' '), 'success');
                  })
                  .catch(function(response) {
                      if(response && !_.isString(response)) {
                          NotificationSrv.error('CaseList', response.data, response.status);
                      }
                  });
            };

            self.load = function() {
                var config = {
                    scope: $scope,
                    filter: self.searchForm.searchQuery !== '' ? {
                        _string: self.searchForm.searchQuery
                    } : '',
                    loadAll: false,
                    sort: self.filtering.context.sort,
                    pageSize: self.filtering.context.pageSize,
                };

                self.list = AlertingSrv.list(config, self.resetSelection);
            };

            self.cancel = function() {
                self.modalInstance.close();
            };

            self.updateMenu = function() {
                var temp = _.uniq(_.pluck(self.selection, 'follow'));

                self.menu.unfollow = temp.length === 1 && temp[0] === true;
                self.menu.follow = temp.length === 1 && temp[0] === false;

                temp = _.uniq(_.pluck(self.selection, 'status'));

                self.menu.markAsRead = temp.indexOf('Ignored') === -1 && temp.indexOf('Imported') === -1;
                self.menu.markAsUnread = temp.indexOf('New') === -1 && temp.indexOf('Updated') === -1;

                self.menu.createNewCase = temp.indexOf('Imported') === -1;
                self.menu.mergeInCase = temp.indexOf('Imported') === -1;

            };

            self.select = function(event) {
                if (event.selected) {
                    self.selection.push(event);
                } else {
                    self.selection = _.reject(self.selection, function(item) {
                        return item.id === event.id;
                    });
                }

                self.updateMenu();

            };

            self.selectAll = function() {
                var selected = self.menu.selectAll;
                _.each(self.list.values, function(item) {
                    item.selected = selected;
                });

                if (selected) {
                    self.selection = self.list.values;
                } else {
                    self.selection = [];
                }

                self.updateMenu();

            };

            self.createNewCase = function() {
                var alertIds = _.pluck(self.selection, 'id');

                CaseTemplateSrv.list()
                  .then(function(templates) {

                      if(!templates || templates.length === 0) {
                          return $q.resolve(undefined);
                      }

                      // Open template selection dialog
                      var modal = $uibModal.open({
                          templateUrl: 'views/partials/case/case.templates.selector.html',
                          controller: 'CaseTemplatesDialogCtrl',
                          controllerAs: 'dialog',
                          size: 'lg',
                          resolve: {
                              templates: function(){
                                  return templates;
                              },
                              uiSettings: function(UiSettingsSrv) {
                                  return UiSettingsSrv.all();
                              }
                          }
                      });

                      return modal.result;
                  })
                  .then(function(template) {

                      // Open case creation dialog
                      var modal = $uibModal.open({
                          templateUrl: 'views/partials/case/case.creation.html',
                          controller: 'CaseCreationCtrl',
                          size: 'lg',
                          resolve: {
                              template: template
                          }
                      });

                      return modal.result;
                  })
                  .then(function(createdCase) {
                      // Bulk merge the selected alerts into the created case
                      NotificationSrv.log('New case has been created', 'success');

                      return AlertingSrv.bulkMergeInto(alertIds, createdCase.id);
                  })
                  .then(function(response) {
                      if(alertIds.length === 1) {
                          NotificationSrv.log(alertIds.length + ' Alert has been merged into the newly created case.', 'success');
                      } else {
                          NotificationSrv.log(alertIds.length + ' Alert(s) have been merged into the newly created case.', 'success');
                      }

                      $rootScope.$broadcast('alert:event-imported');

                      $state.go('app.case.details', {
                          caseId: response.data.id
                      });
                  })
                  .catch(function(err) {
                      if(err && !_.isString(err)) {
                          NotificationSrv.error('AlertEventCtrl', err.data, err.status);
                      }
                  });

            };

            self.mergeInCase = function() {
                var caseModal = $uibModal.open({
                    templateUrl: 'views/partials/case/case.merge.html',
                    controller: 'CaseMergeModalCtrl',
                    controllerAs: 'dialog',
                    size: 'lg',
                    resolve: {
                        source: function() {
                            return self.event;
                        },
                        title: function() {
                            return 'Merge selected Alert(s)';
                        },
                        prompt: function() {
                            return 'the ' + self.selection.length + ' selected Alert(s)';
                        }
                    }
                });

                caseModal.result.then(function(selectedCase) {
                    return AlertingSrv.bulkMergeInto(_.pluck(self.selection, 'id'), selectedCase.id);
                })
                .then(function(response) {
                    $rootScope.$broadcast('alert:event-imported');

                    $state.go('app.case.details', {
                        caseId: response.data.id
                    });
                })
                .catch(function(err) {
                    if(err && !_.isString(err)) {
                        NotificationSrv.error('AlertEventCtrl', err.data, err.status);
                    }
                });
            };

            this.filter = function () {
                self.filtering.filter().then(this.applyFilters);
            };

            this.applyFilters = function () {
                self.searchForm.searchQuery = self.filtering.buildQuery();

                if(self.lastSearch !== self.searchForm.searchQuery) {
                    self.lastSearch = self.searchForm.searchQuery;
                    self.search();
                }
            };

            this.clearFilters = function () {
                self.filtering.clearFilters().then(this.applyFilters);
            };

            this.addFilter = function (field, value) {
                self.filtering.addFilter(field, value).then(this.applyFilters);
            };

            this.removeFilter = function (field) {
                self.filtering.removeFilter(field).then(this.applyFilters);
            };

            this.search = function () {
                this.list.filter = {
                    _string: this.searchForm.searchQuery
                };

                this.list.update();
            };
            this.addFilterValue = function (field, value) {
                var filterDef = self.filtering.filterDefs[field];
                var filter = self.filtering.activeFilters[field];
                var date;

                if (filter && filter.value) {
                    if (filterDef.type === 'list') {
                        if (_.pluck(filter.value, 'text').indexOf(value) === -1) {
                            filter.value.push({
                                text: value
                            });
                        }
                    } else if (filterDef.type === 'date') {
                        date = moment(value);
                        self.filtering.activeFilters[field] = {
                            value: {
                                from: date.hour(0).minutes(0).seconds(0).toDate(),
                                to: date.hour(23).minutes(59).seconds(59).toDate()
                            }
                        };
                    } else {
                        filter.value = value;
                    }
                } else {
                    if (filterDef.type === 'list') {
                        self.filtering.activeFilters[field] = {
                            value: [{
                                text: value
                            }]
                        };
                    } else if (filterDef.type === 'date') {
                        date = moment(value);
                        self.filtering.activeFilters[field] = {
                            value: {
                                from: date.hour(0).minutes(0).seconds(0).toDate(),
                                to: date.hour(23).minutes(59).seconds(59).toDate()
                            }
                        };
                    } else {
                        self.filtering.activeFilters[field] = {
                            value: value
                        };
                    }
                }

                this.filter();
            };

            this.filterByStatus = function(status) {
                self.filtering.clearFilters()
                    .then(function(){
                        self.addFilterValue('status', status);
                    });
            };

            this.filterBySeverity = function(numericSev) {
                self.addFilterValue('severity', Severity.values[numericSev]);
            };

            this.sortBy = function(sort) {
                self.list.sort = sort;
                self.list.update();
                self.filtering.setSort(sort);
            };

            this.sortByField = function(field) {
                var currentSort = Array.isArray(this.filtering.context.sort) ? this.filtering.context.sort[0] : this.filtering.context.sort;
                var sort = null;

                if(currentSort.substr(1) !== field) {
                    sort = ['+' + field];
                } else {
                    sort = [(currentSort === '+' + field) ? '-'+field : '+'+field];
                }

                self.list.sort = sort;
                self.list.update();
                self.filtering.setSort(sort);
            };

            this.getSeverities = self.filtering.getSeverities;

            this.getStatuses = function(query) {
                return AlertingSrv.statuses(query);
            };

            this.getTypes = function(query) {
                return AlertingSrv.types(query);
            };

            this.getSources = function(query) {
                return AlertingSrv.sources(query);
            };

            this.getTags = function(query) {
                return TagSrv.fromAlerts(query);
            };

            self.load();
        });
})();
<|endoftext|>


<commit_before>define(['app','css!/app/css/registry.css','services/search-service'], function(app) { 'use strict';

return ['$scope','searchService','$element', '$rootScope',
function($scope,searchService,$element, $rootScope) {
		$scope.isLoading = false;
		$scope.isError = false;
		$scope.deviceSize = $rootScope.deviceSize;

		function loadRecords(){

			$scope.isLoading = true;
			var searchQuery = {
				fields:  'recordId:uniqueIdentifier_s,identity:identity_s,taxonomicStatus:title_s,commonName:commonNames_EN_txt,description:relevantInformation_EN_t,url_ss',
				query:  'schema_s:organism',
				rowsPerPage: 10000

			};
			return searchService.list(searchQuery)
			.then(function(result){
				$scope.orgaRecords = [];
				$scope.numFound = 0;
				if(result.data){
					$scope.orgaRecords = result.data.response.docs;
					$scope.numFound = result.data.response.numFound;
				}
			  })
			  .catch(function(e){
				$scope.isError = true;
				throw e;
			})
			.finally(function(){
				$scope.isLoading = false;
			})
		}

		$scope.export = function () {
			$scope.readyForExport = true;
			require(['tableexport'], function () {
				$element.find('#forExport').tableExport({
					formats: ['xlsx'],
					filename: 'ORGA-registry',
				});
				$element.find('.xlsx').click();
				$scope.$applyAsync(function () {
					$scope.readyForExport = false;
				}, 200)
			});
		}

		loadRecords();
}];

});
<commit_msg>Organism Registry sorting, chnage Taxonomic status<commit_after>define(['app','css!/app/css/registry.css','services/search-service'], function(app) { 'use strict';

return ['$scope','searchService','$element', '$rootScope',
function($scope,searchService,$element, $rootScope) {
		$scope.isLoading = false;
		$scope.isError = false;
		$scope.deviceSize = $rootScope.deviceSize;

		function loadRecords(){

			$scope.isLoading = true;
			var searchQuery = {
				fields:  'recordId:uniqueIdentifier_s,taxonomicStatus:scientificName_s,commonName:commonNames_EN_txt,description:relevantInformation_EN_t,url_ss',
				query:  'schema_s:organism',
				sort:	'scientificName_s asc',
				rowsPerPage: 10000

			};
			return searchService.list(searchQuery)
			.then(function(result){
				$scope.orgaRecords = [];
				$scope.numFound = 0;
				if(result.data){
					$scope.orgaRecords = result.data.response.docs;
					$scope.numFound = result.data.response.numFound;
				}
			  })
			  .catch(function(e){
				$scope.isError = true;
				throw e;
			})
			.finally(function(){
				$scope.isLoading = false;
			})
		}

		$scope.export = function () {
			$scope.readyForExport = true;
			require(['tableexport'], function () {
				$element.find('#forExport').tableExport({
					formats: ['xlsx'],
					filename: 'ORGA-registry',
				});
				$element.find('.xlsx').click();
				$scope.$applyAsync(function () {
					$scope.readyForExport = false;
				}, 200)
			});
		}

		loadRecords();
}];

});
<|endoftext|>


<commit_before><commit_msg>Check that processed i18n nodes are defined before marking them.<commit_after><|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	Task = mongoose.model('Task'),
	_ = require('lodash');

/**
 * Create a Task
 */
exports.create = function(req, res) {
	var parTask = req.body.parTask;
	delete req.body.parTask;
	if(req.body.owners.users) {
		console.log(req.body.owners.users);
		req.body.owners.users = req.body.owners.users.map(function(usr) {
			return {
				user: usr
			};
		});
		console.log(req.body.owners.users);
	}
	if(req.body.workers.users) {
		req.body.workers.users = req.body.workers.users.map(function(usr) {
			return {
				user: usr
			};
		});
	}
	var task = new Task(req.body);
	if(parTask) {
		Task.findById(parTask).exec(function(err, parentTask) {
			if(err) {
				return res.status(400).send({
					message: errorHandler.getErrorMessage(err)
				});
			} else {
				task.path = parentTask.path.concat(parTask);
				task.project = parentTask.project;
				task.save(function(err) {
					if (err) {
						return res.status(400).send({
							message: errorHandler.getErrorMessage(err)
						});
					} else {
						res.jsonp(task);
					}
				});
			}
		});
	} else {
		task.save(function(err) {
			if (err) {
				return res.status(400).send({
					message: errorHandler.getErrorMessage(err)
				});
			} else {
				res.jsonp(task);
			}
		});
	}
};

/**
 * populate Thread
 */
exports.popTasks = function(rootTasks, callback) {
	var rootIds = rootTasks.map(function(a) {
		return a._id;
	});
	Task.find({'path': {$in: rootIds}}).populate('owners.users.user', 'displayName').populate('owners.team','name').populate('workers.users.user', 'displayName').populate('workers.team','name').lean().exec(function(err, tasks) {
		if(err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			tasks = tasks.sort(function(a, b) {
				return a.path.length - b.path.length;
			});
			var map = {};
			rootTasks.forEach(function(rootTask) {
				rootTask.subTasks = [];
				map[rootTask._id] = rootTask;
			});
			tasks.forEach(function(task) {
				map[task._id] = task;
				task.subTasks = [];
				map[task.path[task.path.length-1]].subTasks.push(task);
			});
			callback();
		}
	});
}

/**
 * Show the current Task
 */
exports.read = function(req, res) {
	exports.popTasks([req.task], function() {
		res.jsonp(req.task);
	})
};

/**
 * Update a Task
 */
exports.update = function(req, res) {
	var task = req.task ;

	task = _.extend(task , req.body);

	task.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(task);
		}
	});
};

/**
 * Delete an Task
 */
exports.delete = function(req, res) {
	var task = req.task ;

	task.remove(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(task);
		}
	});
};

/**
 * List of Tasks
 */
exports.list = function(req, res) {
	Task.find().sort('-created').populate('owners.users', 'displayName').populate('owner.team').populate('workers.users', 'displayName').populate('workers.team').populate('path').populate('project').exec(function(err, tasks) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(tasks);
		}
	});
};

/**
 * Task middleware
 */
exports.taskByID = function(req, res, next, id) {
	Task.findById(id).populate('owners.users.user', 'displayName').populate('owners.team').populate('workers.users.user', 'displayName').populate('workers.team').populate('path').populate('project').lean(req.originalMethod == 'GET').exec(function(err, task) {
		if (err) return next(err);
		if (! task) return next(new Error('Failed to load Task ' + id));
		req.task = task;
		next();
	});
};

/**
 * Task authorization middleware
 */
exports.hasAuthorization = function(req, res, next) {
//	if (req.task.user.id !== req.user.id) {
//		return res.status(403).send('User is not authorized');
//	}
	next();
};
<commit_msg>Fixed tasks not showing up in list view<commit_after>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	Task = mongoose.model('Task'),
	_ = require('lodash');

/**
 * Create a Task
 */
exports.create = function(req, res) {
	var parTask = req.body.parTask;
	delete req.body.parTask;
	if(req.body.owners.users) {
		console.log(req.body.owners.users);
		req.body.owners.users = req.body.owners.users.map(function(usr) {
			return {
				user: usr
			};
		});
		console.log(req.body.owners.users);
	}
	if(req.body.workers.users) {
		req.body.workers.users = req.body.workers.users.map(function(usr) {
			return {
				user: usr
			};
		});
	}
	var task = new Task(req.body);
	if(parTask) {
		Task.findById(parTask).exec(function(err, parentTask) {
			if(err) {
				return res.status(400).send({
					message: errorHandler.getErrorMessage(err)
				});
			} else {
				task.path = parentTask.path.concat(parTask);
				task.project = parentTask.project;
				task.save(function(err) {
					if (err) {
						return res.status(400).send({
							message: errorHandler.getErrorMessage(err)
						});
					} else {
						res.jsonp(task);
					}
				});
			}
		});
	} else {
		task.save(function(err) {
			if (err) {
				return res.status(400).send({
					message: errorHandler.getErrorMessage(err)
				});
			} else {
				res.jsonp(task);
			}
		});
	}
};

/**
 * populate Thread
 */
exports.popTasks = function(rootTasks, callback) {
	var rootIds = rootTasks.map(function(a) {
		return a._id;
	});
	Task.find({'path': {$in: rootIds}}).populate('owners.users.user', 'displayName').populate('owners.team','name').populate('workers.users.user', 'displayName').populate('workers.team','name').lean().exec(function(err, tasks) {
		if(err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			tasks = tasks.sort(function(a, b) {
				return a.path.length - b.path.length;
			});
			var map = {};
			rootTasks.forEach(function(rootTask) {
				rootTask.subTasks = [];
				map[rootTask._id] = rootTask;
			});
			tasks.forEach(function(task) {
				map[task._id] = task;
				task.subTasks = [];
				map[task.path[task.path.length-1]].subTasks.push(task);
			});
			callback();
		}
	});
}

/**
 * Show the current Task
 */
exports.read = function(req, res) {
	exports.popTasks([req.task], function() {
		res.jsonp(req.task);
	})
};

/**
 * Update a Task
 */
exports.update = function(req, res) {
	var task = req.task ;

	task = _.extend(task , req.body);

	task.save(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(task);
		}
	});
};

/**
 * Delete an Task
 */
exports.delete = function(req, res) {
	var task = req.task ;

	task.remove(function(err) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(task);
		}
	});
};

/**
 * List of Tasks
 */
exports.list = function(req, res) {
	Task.find().sort('-created').populate('owners.users.user', 'displayName').populate('owner.team').populate('workers.users.user', 'displayName').populate('workers.team').populate('path').populate('project').exec(function(err, tasks) {
		if (err) {
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		} else {
			res.jsonp(tasks);
		}
	});
};

/**
 * Task middleware
 */
exports.taskByID = function(req, res, next, id) {
	Task.findById(id).populate('owners.users.user', 'displayName').populate('owners.team').populate('workers.users.user', 'displayName').populate('workers.team').populate('path').populate('project').lean(req.originalMethod == 'GET').exec(function(err, task) {
		if (err) return next(err);
		if (! task) return next(new Error('Failed to load Task ' + id));
		req.task = task;
		next();
	});
};

/**
 * Task authorization middleware
 */
exports.hasAuthorization = function(req, res, next) {
//	if (req.task.user.id !== req.user.id) {
//		return res.status(403).send('User is not authorized');
//	}
	next();
};
<|endoftext|>