<commit_before>import React from 'react';
import './EditorForm.scss';
import update from 'immutability-helper';
import Select from 'react-select';
import includes from 'lodash/includes';
import capitalize from 'lodash/capitalize';
import sortBy from 'lodash/sortBy';

import { validateConditionalRules } from '../../../utils/validators';

export class EditorForm extends React.Component {
  constructor (props) {
    super(props);
    this.generateAttributeElements = this.generateAttributeElements.bind(this);
    this.getActiveValue = this.getActiveValue.bind(this);
    this.getCheckedState = this.getCheckedState.bind(this);
    this.closeEditorForm = this.closeEditorForm.bind(this);
    this.state = {
      newAttributes: this.initializeAttributes(this.props.attributeTypes)
    };
  }

  initializeAttributes (attributeTypes) {
    const { attributes } = this.props;
    let initialState = {};
    for (const key in attributeTypes) {
      if (attributeTypes.hasOwnProperty(key)) {
        initialState = Object.assign({}, initialState, {
          [key]: {
            value: attributes[key] ? attributes[key] : null,
            checked: true
          }
        });
      }
    }
    return initialState;
  }

  onChange (value, key, field) {
    this.setState(update(this.state, {
      newAttributes: {
        [key]: {
          [field]: {
            $set: value
          }
        }
      }
    }));
  }

  getActiveValue (key) {
    if (this.state.newAttributes[key].value) {
      return this.state.newAttributes[key].value;
    }
  }

  getCheckedState (key) {
    if (this.props.editorConfig.action === 'edit' && !this.state.newAttributes[key].value) {
      return false;
    } else {
      return this.state.newAttributes[key].checked;
    }
  }

  getAttributesToShow (attributeTypes, attributes) {
    const { newAttributes } = this.state;
    const attributesToShow = [];
    const getAttributeKeys = (attributes) => {
      const attributeKeys = [];
      for (const key in attributes) {
        attributeKeys.push(key);
      }
      return attributeKeys;
    };

    for (const attributeType in attributeTypes) {
      if (includes(attributeTypes[attributeType].allowedIn, this.props.editorConfig.type)) {
        if (attributeTypes[attributeType].required ||
          includes(getAttributeKeys(attributes), attributeType)) {
          attributesToShow.push(attributeType);
        }
        if (attributeTypes[attributeType].requiredIf.length) {
          if (validateConditionalRules(attributeType, attributeTypes, newAttributes)) {
            attributesToShow.push(attributeType);
          }
        }
      }
    }

    attributesToShow.splice(attributesToShow.indexOf(`${capitalize(this.props.editorConfig.type)}Type`), 1);
    attributesToShow.splice(attributesToShow.indexOf('TypeSpecifier'), 1);

    const sortedAttributes = sortBy(attributesToShow, (attribute) => (
      attributeTypes[attribute].index
    ));
    return sortedAttributes;
  }

  getComplementAttributes (attributeTypes, attributesToShow) {
    const { newAttributes } = this.state;
    const complementAttributes = [];

    for (const key in attributeTypes) {
      if (includes(attributeTypes[key].allowedIn, this.props.editorConfig.type)) {
        if (attributeTypes[key].requiredIf.length) {
          if (validateConditionalRules(key, attributeTypes, newAttributes)) {
            complementAttributes.push(key);
          }
        } else {
          complementAttributes.push(key);
        }
      }
    }

    const sortedAttributes = sortBy(complementAttributes, (attribute) => (
      attributeTypes[attribute].index
    ));
    return sortedAttributes;
  }

  generateOptions (array) {
    return array.map(item => ({
      value: item.value,
      label: item.value
    }));
  }

  generateAttributeElements (attributeTypes) {
    let attributesToShow = this.getAttributesToShow(attributeTypes, this.props.attributes);
    if (this.props.editorConfig.action === 'complement') {
      attributesToShow = this.getComplementAttributes(attributeTypes, attributesToShow);
    }
    const attributeElements = [];

    if (attributesToShow.length) {
      for (const key of attributesToShow) {
        if (attributeTypes.hasOwnProperty(key)) {
          if (attributeTypes[key].values.length) {
            const options = this.generateOptions(attributeTypes[key].values);

            attributeElements.push(

                <input
                  type='checkbox'
                  checked={this.getCheckedState(key)}
                  value={this.state.newAttributes[key].checked}
                  onChange={(e) => this.onChange(!this.state.newAttributes[key].checked, key, 'checked')}/>

                  {attributeTypes[key].name}
                  { attributeTypes[key].required &&

                  }

                <Select
                  placeholder='Valitse...'
                  autoBlur={true}
                  autofocus={false}
                  className='form-control editor-form__select'
                  clearable={true}
                  disabled={!this.state.newAttributes[key].checked}
                  onChange={(option) => this.onChange(option ? option.value : null, key, 'value')}
                  openOnFocus={true}
                  options={options}
                  value={this.getActiveValue(key)}
                />

            );
          } else if (attributeTypes[key].values.length === 0) {
            attributeElements.push(

                <input
                  type='checkbox'
                  checked={this.getCheckedState(key)}
                  value={this.state.newAttributes[key].checked}
                  onChange={(e) => this.onChange(!this.state.newAttributes[key].checked, key, 'checked')}
                />

                  {attributeTypes[key].name}
                  { attributeTypes[key].required &&

                  }

                <input
                  className='form-control edit-attribute__input'
                  value={this.getActiveValue(key)}
                  placeholder={attributeTypes[key].name}
                  onChange={(e) => this.onChange(e.target.value, key, 'value')}
                  disabled={!this.state.newAttributes[key].checked}
                />

            );
          }
        }
      }
    } else {
      attributeElements.push(

Ei täydennettäviä metatietoja

      );
    }

    return attributeElements;
  }

  generateDropdown (elementTypes) {
    const type = [`${capitalize(this.props.editorConfig.type)}Type`];
    const optionsRaw = Object.keys(elementTypes).map(type => {
      return { value: elementTypes[type].name, label: elementTypes[type].name };
    });
    const options = this.generateOptions(optionsRaw);

    return (
      <Select
        placeholder='Valitse...'
        autoBlur={true}
        autofocus={false}
        className='form-control col-xs-6'
        clearable={true}
        onChange={(option) => this.onChange(option ? option.value : null, type, 'value')}
        openOnFocus={true}
        options={options}
        value={this.state.newAttributes[type] ? this.state.newAttributes[type].value : '---'}
      />
    );
  }

  filterAttributes (attributes) {
    const filteredAttributes = Object.assign({}, attributes);
    for (const key in filteredAttributes) {
      if (!filteredAttributes[key].value) {
        delete filteredAttributes[key];
      }
    }
    return filteredAttributes;
  }

  editMetaData (e) {
    e.preventDefault();
    const { newAttributes } = this.state;
    this.props.editMetaDataWithForm(this.filterAttributes(newAttributes));
    this.props.displayMessage({
      title: 'Metatiedot',
      body: 'Tietojen muokkaus onnistui!'
    });
  }

  addRecord (e, targetId) {
    e.preventDefault();
    const { newAttributes } = this.state;

    this.props.elementConfig.createRecord(
      this.filterAttributes(newAttributes),
      targetId
    );
    this.props.displayMessage({
      title: 'Asiakirja',
      body: 'Asiakirjan lisäys onnistui!'
    });
  }

  editElement (e, targetId) {
    e.preventDefault();
    const { newAttributes } = this.state;
    this.props.elementConfig.editWithForm(
      this.filterAttributes(newAttributes),
      targetId
    );
  }

  resolveLabel () {
    const { type, action } = this.props.editorConfig;

    switch (type) {
      case 'function':
        return 'Metatiedot';
      case 'phase':
        if (action === 'add') {
          return 'Uusi käsittelyvaihe';
        }
        if (action === 'edit') {
          return 'Muokkaa käsittelyvaihetta';
        }
        if (action === 'complement') {
          return 'Täydennä käsittelyvaihetta';
        }
        break;
      case 'action':
        if (action === 'add') {
          return 'Uusi toimenpide';
        }
        if (action === 'edit') {
          return 'Muokkaa toimenpidettä';
        }
        if (action === 'complement') {
          return 'Täydennä toimenpidettä';
        }
        break;
      case 'record':
        if (action === 'add') {
          return 'Uusi asiakirja';
        }
        if (action === 'edit') {
          return 'Muokkaa asiakirjaa';
        }
        if (action === 'complement') {
          return 'Täydennä asiakirjaa';
        }
        break;
    }
  }

  resolveOnSubmit (e, targetId) {
    const { action, type } = this.props.editorConfig;

    switch (type) {
      case 'function':
        if (action === 'edit' || action === 'complement') {
          this.editMetaData(e);
        }
        break;
      case 'phase':
        if (action === 'edit' || action === 'complement') {
          this.editElement(e, targetId);
          this.props.displayMessage({
            title: 'Käsittelyvaihe',
            body: 'Käsittelyvaiheen muokkaus onnistui!'
          });
        }
        break;
      case 'action':
        if (action === 'edit' || action === 'complement') {
          this.editElement(e, targetId);
          this.props.displayMessage({
            title: 'Toimenpide',
            body: 'Toimenpiteen muokkaus onnistui!'
          });
        }
        break;
      case 'record':
        if (action === 'add') {
          this.addRecord(e, targetId);
        }
        if (action === 'edit' || action === 'complement') {
          this.editElement(e, targetId);
          this.props.displayMessage({
            title: 'Asiakirja',
            body: 'Asiakirjan muokkaus onnistui!'
          });
        }
    }
  }

  closeEditorForm (e) {
    e.preventDefault();
    this.props.closeEditorForm();
  }

  renderDescriptions () {
    const typeDropdown = this.generateDropdown(this.props.elementConfig.elementTypes);

    return (

          Tarkenne

          <input
            className='col-xs-6 form-control edit-attribute__input'
            placeholder='Tarkenne'
            value={this.state.newAttributes.TypeSpecifier.value}
            onChange={(e) => this.onChange(e.target.value, 'TypeSpecifier', 'value')}/>

          Tyyppi

          { typeDropdown }

    );
  }

  render () {
    const { attributeTypes, targetId } = this.props;
    const attributeElements = this.generateAttributeElements(attributeTypes);

    return (

{this.resolveLabel()}

        <form onSubmit={(e) => this.resolveOnSubmit(e, targetId)}
              className='editor-form'>
          { this.props.editorConfig.type !== 'function' ? this.renderDescriptions() : null }
          { attributeElements }

            <button
              className='btn btn-primary pull-right editor-form__submit'
              type='submit'>
              Valmis
            </button>
            <button
              className='btn btn-danger pull-right editor-form__cancel'
              onClick={(e) => this.closeEditorForm(e)}>
              Peruuta
            </button>

        </form>

    );
  }
}

EditorForm.propTypes = {
  attributeTypes: React.PropTypes.object.isRequired,
  attributes: React.PropTypes.object.isRequired,
  closeEditorForm: React.PropTypes.func.isRequired,
  displayMessage: React.PropTypes.func.isRequired,
  editMetaDataWithForm: React.PropTypes.func,
  editorConfig: React.PropTypes.shape({
    type: React.PropTypes.string.isRequired,
    action: React.PropTypes.string.isRequired
  }),
  elementConfig: React.PropTypes.shape({
    editWithForm: React.PropTypes.func,
    elementTypes: React.PropTypes.object.isRequired,
    createRecord: React.PropTypes.func, // only records created with editorform
    elementId: React.PropTypes.string,
    typeSpecifier: React.PropTypes.string
  }),
  targetId: React.PropTypes.string
};

export default EditorForm;
<commit_msg>Create function for trimming attributesToShow<commit_after>import React from 'react';
import './EditorForm.scss';
import update from 'immutability-helper';
import Select from 'react-select';
import includes from 'lodash/includes';
import capitalize from 'lodash/capitalize';
import sortBy from 'lodash/sortBy';

import { validateConditionalRules } from '../../../utils/validators';

export class EditorForm extends React.Component {
  constructor (props) {
    super(props);
    this.generateAttributeElements = this.generateAttributeElements.bind(this);
    this.getActiveValue = this.getActiveValue.bind(this);
    this.getCheckedState = this.getCheckedState.bind(this);
    this.closeEditorForm = this.closeEditorForm.bind(this);
    this.state = {
      newAttributes: this.initializeAttributes(this.props.attributeTypes)
    };
  }

  initializeAttributes (attributeTypes) {
    const { attributes } = this.props;
    let initialState = {};
    for (const key in attributeTypes) {
      if (attributeTypes.hasOwnProperty(key)) {
        initialState = Object.assign({}, initialState, {
          [key]: {
            value: attributes[key] ? attributes[key] : null,
            checked: true
          }
        });
      }
    }
    return initialState;
  }

  onChange (value, key, field) {
    this.setState(update(this.state, {
      newAttributes: {
        [key]: {
          [field]: {
            $set: value
          }
        }
      }
    }));
  }

  getActiveValue (key) {
    if (this.state.newAttributes[key].value) {
      return this.state.newAttributes[key].value;
    }
  }

  getCheckedState (key) {
    if (this.props.editorConfig.action === 'edit' && !this.state.newAttributes[key].value) {
      return false;
    } else {
      return this.state.newAttributes[key].checked;
    }
  }

  getAttributesToShow (attributeTypes, attributes) {
    const { newAttributes } = this.state;
    const attributesToShow = [];
    const getAttributeKeys = (attributes) => {
      const attributeKeys = [];
      for (const key in attributes) {
        attributeKeys.push(key);
      }
      return attributeKeys;
    };

    for (const attributeType in attributeTypes) {
      if (includes(attributeTypes[attributeType].allowedIn, this.props.editorConfig.type)) {
        if (attributeTypes[attributeType].required ||
          includes(getAttributeKeys(attributes), attributeType)) {
          attributesToShow.push(attributeType);
        }
        if (attributeTypes[attributeType].requiredIf.length) {
          if (validateConditionalRules(attributeType, attributeTypes, newAttributes)) {
            attributesToShow.push(attributeType);
          }
        }
      }
    }

    return this.prepareAttributes(attributesToShow);
  }

  getComplementAttributes (attributeTypes, attributesToShow) {
    const { newAttributes } = this.state;
    const complementAttributes = [];

    for (const key in attributeTypes) {
      if (includes(attributeTypes[key].allowedIn, this.props.editorConfig.type)) {
        if (attributeTypes[key].requiredIf.length) {
          if (validateConditionalRules(key, attributeTypes, newAttributes)) {
            complementAttributes.push(key);
          }
        } else {
          complementAttributes.push(key);
        }
      }
    }

    return this.prepareAttributes(complementAttributes);
  }

  prepareAttributes (attributesToShow) {
    attributesToShow.splice(attributesToShow.indexOf(`${capitalize(this.props.editorConfig.type)}Type`), 1);
    attributesToShow.splice(attributesToShow.indexOf('TypeSpecifier'), 1);

    const sortedAttributes = sortBy(attributesToShow, (attribute) => (
      this.props.attributeTypes[attribute].index
    ));

    return sortedAttributes;
  }

  generateOptions (array) {
    return array.map(item => ({
      value: item.value,
      label: item.value
    }));
  }

  generateAttributeElements (attributeTypes) {
    let attributesToShow = this.getAttributesToShow(attributeTypes, this.props.attributes);
    if (this.props.editorConfig.action === 'complement') {
      attributesToShow = this.getComplementAttributes(attributeTypes, attributesToShow);
    }
    const attributeElements = [];

    if (attributesToShow.length) {
      for (const key of attributesToShow) {
        if (attributeTypes.hasOwnProperty(key)) {
          if (attributeTypes[key].values.length) {
            const options = this.generateOptions(attributeTypes[key].values);

            attributeElements.push(

                <input
                  type='checkbox'
                  checked={this.getCheckedState(key)}
                  value={this.state.newAttributes[key].checked}
                  onChange={(e) => this.onChange(!this.state.newAttributes[key].checked, key, 'checked')}/>

                  {attributeTypes[key].name}
                  { attributeTypes[key].required &&

                  }

                <Select
                  placeholder='Valitse...'
                  autoBlur={true}
                  autofocus={false}
                  className='form-control editor-form__select'
                  clearable={true}
                  disabled={!this.state.newAttributes[key].checked}
                  onChange={(option) => this.onChange(option ? option.value : null, key, 'value')}
                  openOnFocus={true}
                  options={options}
                  value={this.getActiveValue(key)}
                />

            );
          } else if (attributeTypes[key].values.length === 0) {
            attributeElements.push(

                <input
                  type='checkbox'
                  checked={this.getCheckedState(key)}
                  value={this.state.newAttributes[key].checked}
                  onChange={(e) => this.onChange(!this.state.newAttributes[key].checked, key, 'checked')}
                />

                  {attributeTypes[key].name}
                  { attributeTypes[key].required &&

                  }

                <input
                  className='form-control edit-attribute__input'
                  value={this.getActiveValue(key)}
                  placeholder={attributeTypes[key].name}
                  onChange={(e) => this.onChange(e.target.value, key, 'value')}
                  disabled={!this.state.newAttributes[key].checked}
                />

            );
          }
        }
      }
    } else {
      attributeElements.push(

Ei täydennettäviä metatietoja

      );
    }

    return attributeElements;
  }

  generateDropdown (elementTypes) {
    const type = [`${capitalize(this.props.editorConfig.type)}Type`];
    const optionsRaw = Object.keys(elementTypes).map(type => {
      return { value: elementTypes[type].name, label: elementTypes[type].name };
    });
    const options = this.generateOptions(optionsRaw);

    return (
      <Select
        placeholder='Valitse...'
        autoBlur={true}
        autofocus={false}
        className='form-control col-xs-6'
        clearable={true}
        onChange={(option) => this.onChange(option ? option.value : null, type, 'value')}
        openOnFocus={true}
        options={options}
        value={this.state.newAttributes[type] ? this.state.newAttributes[type].value : '---'}
      />
    );
  }

  filterAttributes (attributes) {
    const filteredAttributes = Object.assign({}, attributes);
    for (const key in filteredAttributes) {
      if (!filteredAttributes[key].value) {
        delete filteredAttributes[key];
      }
    }
    return filteredAttributes;
  }

  editMetaData (e) {
    e.preventDefault();
    const { newAttributes } = this.state;
    this.props.editMetaDataWithForm(this.filterAttributes(newAttributes));
    this.props.displayMessage({
      title: 'Metatiedot',
      body: 'Tietojen muokkaus onnistui!'
    });
  }

  addRecord (e, targetId) {
    e.preventDefault();
    const { newAttributes } = this.state;

    this.props.elementConfig.createRecord(
      this.filterAttributes(newAttributes),
      targetId
    );
    this.props.displayMessage({
      title: 'Asiakirja',
      body: 'Asiakirjan lisäys onnistui!'
    });
  }

  editElement (e, targetId) {
    e.preventDefault();
    const { newAttributes } = this.state;
    this.props.elementConfig.editWithForm(
      this.filterAttributes(newAttributes),
      targetId
    );
  }

  resolveLabel () {
    const { type, action } = this.props.editorConfig;

    switch (type) {
      case 'function':
        return 'Metatiedot';
      case 'phase':
        if (action === 'add') {
          return 'Uusi käsittelyvaihe';
        }
        if (action === 'edit') {
          return 'Muokkaa käsittelyvaihetta';
        }
        if (action === 'complement') {
          return 'Täydennä käsittelyvaihetta';
        }
        break;
      case 'action':
        if (action === 'add') {
          return 'Uusi toimenpide';
        }
        if (action === 'edit') {
          return 'Muokkaa toimenpidettä';
        }
        if (action === 'complement') {
          return 'Täydennä toimenpidettä';
        }
        break;
      case 'record':
        if (action === 'add') {
          return 'Uusi asiakirja';
        }
        if (action === 'edit') {
          return 'Muokkaa asiakirjaa';
        }
        if (action === 'complement') {
          return 'Täydennä asiakirjaa';
        }
        break;
    }
  }

  resolveOnSubmit (e, targetId) {
    const { action, type } = this.props.editorConfig;

    switch (type) {
      case 'function':
        if (action === 'edit' || action === 'complement') {
          this.editMetaData(e);
        }
        break;
      case 'phase':
        if (action === 'edit' || action === 'complement') {
          this.editElement(e, targetId);
          this.props.displayMessage({
            title: 'Käsittelyvaihe',
            body: 'Käsittelyvaiheen muokkaus onnistui!'
          });
        }
        break;
      case 'action':
        if (action === 'edit' || action === 'complement') {
          this.editElement(e, targetId);
          this.props.displayMessage({
            title: 'Toimenpide',
            body: 'Toimenpiteen muokkaus onnistui!'
          });
        }
        break;
      case 'record':
        if (action === 'add') {
          this.addRecord(e, targetId);
        }
        if (action === 'edit' || action === 'complement') {
          this.editElement(e, targetId);
          this.props.displayMessage({
            title: 'Asiakirja',
            body: 'Asiakirjan muokkaus onnistui!'
          });
        }
    }
  }

  closeEditorForm (e) {
    e.preventDefault();
    this.props.closeEditorForm();
  }

  renderDescriptions () {
    const typeDropdown = this.generateDropdown(this.props.elementConfig.elementTypes);

    return (

          Tarkenne

          <input
            className='col-xs-6 form-control edit-attribute__input'
            placeholder='Tarkenne'
            value={this.state.newAttributes.TypeSpecifier.value}
            onChange={(e) => this.onChange(e.target.value, 'TypeSpecifier', 'value')}/>

          Tyyppi

          { typeDropdown }

    );
  }

  render () {
    const { attributeTypes, targetId } = this.props;
    const attributeElements = this.generateAttributeElements(attributeTypes);

    return (

{this.resolveLabel()}

        <form onSubmit={(e) => this.resolveOnSubmit(e, targetId)}
              className='editor-form'>
          { this.props.editorConfig.type !== 'function' ? this.renderDescriptions() : null }
          { attributeElements }

            <button
              className='btn btn-primary pull-right editor-form__submit'
              type='submit'>
              Valmis
            </button>
            <button
              className='btn btn-danger pull-right editor-form__cancel'
              onClick={(e) => this.closeEditorForm(e)}>
              Peruuta
            </button>

        </form>

    );
  }
}

EditorForm.propTypes = {
  attributeTypes: React.PropTypes.object.isRequired,
  attributes: React.PropTypes.object.isRequired,
  closeEditorForm: React.PropTypes.func.isRequired,
  displayMessage: React.PropTypes.func.isRequired,
  editMetaDataWithForm: React.PropTypes.func,
  editorConfig: React.PropTypes.shape({
    type: React.PropTypes.string.isRequired,
    action: React.PropTypes.string.isRequired
  }),
  elementConfig: React.PropTypes.shape({
    editWithForm: React.PropTypes.func,
    elementTypes: React.PropTypes.object.isRequired,
    createRecord: React.PropTypes.func, // only records created with editorform
    elementId: React.PropTypes.string,
    typeSpecifier: React.PropTypes.string
  }),
  targetId: React.PropTypes.string
};

export default EditorForm;
<|endoftext|>


<commit_before>
function checkOverflow() {
  const swiper = this;
  const wasLocked = swiper.isLocked;

  swiper.isLocked = swiper.snapGrid.length === 1;
  swiper.allowSlideNext = !swiper.isLocked;
  swiper.allowSlidePrev = !swiper.isLocked;

  // events
  if (wasLocked !== swiper.isLocked) swiper.emit(swiper.isLocked ? 'lock' : 'unlock');

  if (wasLocked && wasLocked !== swiper.isLocked) {
    swiper.isEnd = false;
    swiper.navigation.update();
  }
}

export default { checkOverflow };
<commit_msg>Modified checkOverflow() to account for slide offsets<commit_after>
function checkOverflow() {
  const swiper = this;
  const params = swiper.params;
  const wasLocked = swiper.isLocked;
  let lastSlidePosition = (params.slidesOffsetBefore + (params.spaceBetween * (swiper.slides.length - 1)) + ((swiper.slides[0]).offsetWidth) * swiper.slides.length)

  if ((typeof params.slidesOffsetBefore !== 'undefined' && params.slidesOffsetBefore !== 0) || (typeof params.slidesOffsetAfter !== 'undefined' && params.slidesOffsetAfter !== 0)) {
    swiper.isLocked = lastSlidePosition <= swiper.size;
  } else {
    swiper.isLocked = swiper.snapGrid.length === 1;
  }

  swiper.allowSlideNext = !swiper.isLocked;
  swiper.allowSlidePrev = !swiper.isLocked;

  // events
  if (wasLocked !== swiper.isLocked) swiper.emit(swiper.isLocked ? 'lock' : 'unlock');

  if (wasLocked && wasLocked !== swiper.isLocked) {
    swiper.isEnd = false;
    swiper.navigation.update();
  }
}

export default { checkOverflow };
<|endoftext|>


<commit_before>import React, {PropTypes} from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'

import * as courseActions from '../../actions/course-actions';
import CourseForm from './course-form';

class ManageCoursePage extends React.Component {
    constructor(props, context) {
        super(props, context);

        this.state = {
            course: Object.assign({}, props.course),
            errors: {},
        };
    }
    render() {
        return (
            <CourseForm
                allAuthors={[]}
                course={this.state.course}
                errors={this.state.errors}
            />
        );
    }
}

ManageCoursePage.propTypes = {
    course: PropTypes.object.isRequired,
}

function mapStateToProps(state, ownProps) {
    let course = {id: '', watchHref: '', title: '', authorId: '', length: '', category: ''};

    return {
        course: course,
    }
}

function mapDispatchToProps(dispatch) {
    return {
        actions: bindActionCreators(courseActions, dispatch),
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(ManageCoursePage);
<commit_msg>Map author data for dropdown<commit_after>import React, {PropTypes} from 'react'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'

import * as courseActions from '../../actions/course-actions';
import CourseForm from './course-form';

class ManageCoursePage extends React.Component {
    constructor(props, context) {
        super(props, context);

        this.state = {
            course: Object.assign({}, props.course),
            errors: {},
        };
    }
    render() {
        return (
            <CourseForm
                allAuthors={this.props.authors}
                course={this.state.course}
                errors={this.state.errors}
            />
        );
    }
}

ManageCoursePage.propTypes = {
    course: PropTypes.object.isRequired,
    authors: PropTypes.array.isRequired,
}

function mapStateToProps(state, ownProps) {
    let course = {id: '', watchHref: '', title: '', authorId: '', length: '', category: ''};

    const authorsFormattedForDropdown = state.authors.map(author => {
        return {
            value: author.id,
            text: `${author.firstName} ${author.lastName}`,
        };
    });

    return {
        course: course,
        authors: authorsFormattedForDropdown,
    }
}

function mapDispatchToProps(dispatch) {
    return {
        actions: bindActionCreators(courseActions, dispatch),
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(ManageCoursePage);
<|endoftext|>


<commit_before>import React, { Component, Fragment } from "react";
import gql from "graphql-tag";
import { InputField } from "../../generic/core";
import { graphql, withApollo } from "react-apollo";
import { Container, Row, Col, Input } from "reactstrap";

import "./style.css";

const REACTOR_SUB = gql`
  subscription ReactorsUpdate($simulatorId: ID!) {
    reactorUpdate(simulatorId: $simulatorId) {
      id
      type
      name
      heat
      heatRate
      model
      coolant
      damage {
        damaged
      }
      ejected
      efficiency
      displayName
      powerOutput
      batteryChargeRate
      batteryChargeLevel
    }
  }
`;

class ReactorControl extends Component {
  constructor(props) {
    super(props);
    this.internalSub = null;
    this.systemSub = null;
  }
  componentWillReceiveProps(nextProps) {
    if (!this.internalSub && !nextProps.data.loading) {
      this.internalSub = nextProps.data.subscribeToMore({
        document: REACTOR_SUB,
        variables: {
          simulatorId: nextProps.simulator.id
        },
        updateQuery: (previousResult, { subscriptionData }) => {
          return Object.assign({}, previousResult, {
            reactors: subscriptionData.data.reactorUpdate
          });
        }
      });
    }
  }
  componentWillUnmount() {
    this.internalSub && this.internalSub();
  }
  setEfficiency = e => {
    if (!e) return;
    const { reactors } = this.props.data;
    const reactor = reactors.find(r => r.model === "reactor");
    const mutation = gql`
      mutation SetReactorEfficiency($id: ID!, $e: Float) {
        reactorChangeEfficiency(id: $id, efficiency: $e)
      }
    `;
    const variables = {
      id: reactor.id,
      e
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  };
  setPowerLevel = e => {
    if (!e || !parseFloat(e)) return;
    const { reactors } = this.props.data;
    const reactor = reactors.find(r => r.model === "reactor");
    const mutation = gql`
      mutation ReactorPowerLevel($id: ID!, $e: Int!) {
        reactorChangeOutput(id: $id, output: $e)
      }
    `;
    const variables = {
      id: reactor.id,
      e
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  };
  setHeat = e => {
    return;
    /*const { reactors } = this.props.data;
    const reactor = reactors.find(r => r.model === "reactor");
    const mutation = gql`
      mutation SetReactorEfficiency($id: ID!, $e: Float) {
        reactorChangeEfficiency(id: $id, efficiency: $e)
      }
    `;
    const variables = {
      id: reactor.id,
      e
    };
    this.props.client.mutate({
      mutation,
      variables
    });*/
  };
  setChargeLevel = e => {
    if (!e) return;
    const { reactors } = this.props.data;
    const battery = reactors.find(r => r.model === "battery");
    const mutation = gql`
      mutation BatteryChargeLevel($id: ID!, $e: Float!) {
        reactorBatteryChargeLevel(id: $id, level: $e)
      }
    `;
    const variables = {
      id: battery.id,
      e: e / 100
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  };
  setChargeRate = e => {
    if (!e) return;
    const { reactors } = this.props.data;
    const battery = reactors.find(r => r.model === "battery");
    const mutation = gql`
      mutation BatteryChargeRate($id: ID!, $e: Float!) {
        reactorBatteryChargeRate(id: $id, rate: $e)
      }
    `;
    const variables = {
      id: battery.id,
      e: e / 100
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  };
  updateHeat = heat => {
    const { reactors } = this.props.data;
    const reactor = reactors.find(r => r.model === "reactor") || {};
    const mutation = gql`
      mutation SystemHeat($id: ID!, $heat: Float) {
        addHeat(id: $id, heat: $heat)
      }
    `;
    const variables = {
      id: reactor.id,
      heat: heat / 100
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  };
  setHeatRate = value => {
    const { reactors } = this.props.data;
    const reactor = reactors.find(r => r.model === "reactor") || {};
    const mutation = gql`
      mutation SetHeatRate($id: ID!, $rate: Float!) {
        setHeatRate(id: $id, rate: $rate)
      }
    `;
    const variables = {
      id: reactor.id,
      rate: value
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  };
  render() {
    if (this.props.data.loading || !this.props.data.reactors) return null;
    const { reactors } = this.props.data;
    const reactor = reactors.find(r => r.model === "reactor");
    const battery = reactors.find(r => r.model === "battery");
    if (!reactor) return
No Reactor
;
    const efficiencies = [
      {
        label: "Cruise",
        efficiency: 1
      },
      {
        label: "Silent Running",
        efficiency: 0.87
      },
      {
        label: "Reduced",
        efficiency: 0.5
      },
      {
        label: "Auxilliary",
        efficiency: 0.38
      },
      {
        label: "Minimal",
        efficiency: 0.27
      },
      {
        label: "Power Down",
        efficiency: 0
      },
      {
        label: "External Power"
      }
    ];
    return (
      <Container className="reactor-control-core">
        <Row>
          <Col sm={12}>
            {reactor && (
              <Fragment>

Reactor Output:

                <InputField
                  prompt="What is the new power output?"
                  onClick={this.setPowerLevel}
                >
                  {reactor.powerOutput}
                </InputField>

Reactor Efficiency:

                <Input
                  size="sm"
                  type="select"
                  onChange={evt => this.setEfficiency(evt.target.value)}
                  value={reactor.efficiency}
                >
                  {efficiencies.map(e => (
                    <option key={e.label} value={e.efficiency}>
                      {e.label}
                    </option>
                  ))}
                </Input>

Heat Rate:

                <Input
                  size="sm"
                  type="select"
                  onChange={evt => this.setHeatRate(evt.target.value)}
                  value={reactor.heatRate}
                >
                  <option value={1.5}>Fast</option>
                  <option value={1}>Normal</option>
                  <option value={0.5}>Slow</option>
                  <option value={0}>Stop</option>
                  <option value={-1}>Reverse</option>
                </Input>

Reactor Heat:

                <InputField
                  prompt="What is the new reactor heat?"
                  onClick={this.updateHeat}
                >
                  {Math.round(reactor.heat * 1000) / 10}%
                </InputField>
              </Fragment>
            )}
            {battery && (
              <Fragment>
                {" "}

Battery Output:

                <InputField
                  prompt="What is the new battery charge level?"
                  onClick={this.setChargeLevel}
                >
                  {Math.round(battery.batteryChargeLevel * 100)}%
                </InputField>

Battery Rate:

                <InputField
                  prompt="What is the new battery charge rate?"
                  onClick={this.setChargeRate}
                >
                  {battery.batteryChargeRate * 100}
                </InputField>
              </Fragment>
            )}
          </Col>
        </Row>
      </Container>
    );
  }
}

const REACTOR_QUERY = gql`
  query Reactors($simulatorId: ID!) {
    reactors(simulatorId: $simulatorId) {
      id
      type
      name
      heat
      heatRate
      model
      coolant
      damage {
        damaged
      }
      ejected
      efficiency
      displayName
      powerOutput
      batteryChargeRate
      batteryChargeLevel
    }
  }
`;
export default graphql(REACTOR_QUERY, {
  options: ownProps => ({
    fetchPolicy: "cache-and-network",
    variables: {
      simulatorId: ownProps.simulator.id
    }
  })
})(withApollo(ReactorControl));
<commit_msg>Added efficiency labels next to the reactor control core dropdown. Closes #749<commit_after>import React, { Component, Fragment } from "react";
import gql from "graphql-tag";
import { InputField } from "../../generic/core";
import { graphql, withApollo } from "react-apollo";
import { Container, Row, Col, Input } from "reactstrap";

import "./style.css";

const REACTOR_SUB = gql`
  subscription ReactorsUpdate($simulatorId: ID!) {
    reactorUpdate(simulatorId: $simulatorId) {
      id
      type
      name
      heat
      heatRate
      model
      coolant
      damage {
        damaged
      }
      ejected
      efficiency
      displayName
      powerOutput
      batteryChargeRate
      batteryChargeLevel
    }
  }
`;

class ReactorControl extends Component {
  constructor(props) {
    super(props);
    this.internalSub = null;
    this.systemSub = null;
  }
  componentWillReceiveProps(nextProps) {
    if (!this.internalSub && !nextProps.data.loading) {
      this.internalSub = nextProps.data.subscribeToMore({
        document: REACTOR_SUB,
        variables: {
          simulatorId: nextProps.simulator.id
        },
        updateQuery: (previousResult, { subscriptionData }) => {
          return Object.assign({}, previousResult, {
            reactors: subscriptionData.data.reactorUpdate
          });
        }
      });
    }
  }
  componentWillUnmount() {
    this.internalSub && this.internalSub();
  }
  setEfficiency = e => {
    if (!e) return;
    const { reactors } = this.props.data;
    const reactor = reactors.find(r => r.model === "reactor");
    const mutation = gql`
      mutation SetReactorEfficiency($id: ID!, $e: Float) {
        reactorChangeEfficiency(id: $id, efficiency: $e)
      }
    `;
    const variables = {
      id: reactor.id,
      e
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  };
  setPowerLevel = e => {
    if (!e || !parseFloat(e)) return;
    const { reactors } = this.props.data;
    const reactor = reactors.find(r => r.model === "reactor");
    const mutation = gql`
      mutation ReactorPowerLevel($id: ID!, $e: Int!) {
        reactorChangeOutput(id: $id, output: $e)
      }
    `;
    const variables = {
      id: reactor.id,
      e
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  };
  setHeat = e => {
    return;
    /*const { reactors } = this.props.data;
    const reactor = reactors.find(r => r.model === "reactor");
    const mutation = gql`
      mutation SetReactorEfficiency($id: ID!, $e: Float) {
        reactorChangeEfficiency(id: $id, efficiency: $e)
      }
    `;
    const variables = {
      id: reactor.id,
      e
    };
    this.props.client.mutate({
      mutation,
      variables
    });*/
  };
  setChargeLevel = e => {
    if (!e) return;
    const { reactors } = this.props.data;
    const battery = reactors.find(r => r.model === "battery");
    const mutation = gql`
      mutation BatteryChargeLevel($id: ID!, $e: Float!) {
        reactorBatteryChargeLevel(id: $id, level: $e)
      }
    `;
    const variables = {
      id: battery.id,
      e: e / 100
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  };
  setChargeRate = e => {
    if (!e) return;
    const { reactors } = this.props.data;
    const battery = reactors.find(r => r.model === "battery");
    const mutation = gql`
      mutation BatteryChargeRate($id: ID!, $e: Float!) {
        reactorBatteryChargeRate(id: $id, rate: $e)
      }
    `;
    const variables = {
      id: battery.id,
      e: e / 100
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  };
  updateHeat = heat => {
    const { reactors } = this.props.data;
    const reactor = reactors.find(r => r.model === "reactor") || {};
    const mutation = gql`
      mutation SystemHeat($id: ID!, $heat: Float) {
        addHeat(id: $id, heat: $heat)
      }
    `;
    const variables = {
      id: reactor.id,
      heat: heat / 100
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  };
  setHeatRate = value => {
    const { reactors } = this.props.data;
    const reactor = reactors.find(r => r.model === "reactor") || {};
    const mutation = gql`
      mutation SetHeatRate($id: ID!, $rate: Float!) {
        setHeatRate(id: $id, rate: $rate)
      }
    `;
    const variables = {
      id: reactor.id,
      rate: value
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  };
  render() {
    if (this.props.data.loading || !this.props.data.reactors) return null;
    const { reactors } = this.props.data;
    const reactor = reactors.find(r => r.model === "reactor");
    const battery = reactors.find(r => r.model === "battery");
    if (!reactor) return
No Reactor
;
    const efficiencies = [
      {
        label: "Cruise",
        efficiency: 1
      },
      {
        label: "Silent Running",
        efficiency: 0.87
      },
      {
        label: "Reduced",
        efficiency: 0.5
      },
      {
        label: "Auxilliary",
        efficiency: 0.38
      },
      {
        label: "Minimal",
        efficiency: 0.27
      },
      {
        label: "Power Down",
        efficiency: 0
      },
      {
        label: "External Power",
        efficiency: 0
      }
    ];
    return (
      <Container className="reactor-control-core">
        <Row>
          <Col sm={12}>
            {reactor && (
              <Fragment>

Reactor Output:

                <InputField
                  prompt="What is the new power output?"
                  onClick={this.setPowerLevel}
                >
                  {reactor.powerOutput}
                </InputField>

Reactor Efficiency:

                <Input
                  size="sm"
                  type="select"
                  onChange={evt => this.setEfficiency(evt.target.value)}
                  value={reactor.efficiency}
                >
                  {efficiencies.map(e => (
                    <option key={e.label} value={e.efficiency}>
                      {e.label} - {e.efficiency * 100}%{" "}
                    </option>
                  ))}
                </Input>

Heat Rate:

                <Input
                  size="sm"
                  type="select"
                  onChange={evt => this.setHeatRate(evt.target.value)}
                  value={reactor.heatRate}
                >
                  <option value={1.5}>Fast</option>
                  <option value={1}>Normal</option>
                  <option value={0.5}>Slow</option>
                  <option value={0}>Stop</option>
                  <option value={-1}>Reverse</option>
                </Input>

Reactor Heat:

                <InputField
                  prompt="What is the new reactor heat?"
                  onClick={this.updateHeat}
                >
                  {Math.round(reactor.heat * 1000) / 10}%
                </InputField>
              </Fragment>
            )}
            {battery && (
              <Fragment>
                {" "}

Battery Output:

                <InputField
                  prompt="What is the new battery charge level?"
                  onClick={this.setChargeLevel}
                >
                  {Math.round(battery.batteryChargeLevel * 100)}%
                </InputField>

Battery Rate:

                <InputField
                  prompt="What is the new battery charge rate?"
                  onClick={this.setChargeRate}
                >
                  {battery.batteryChargeRate * 100}
                </InputField>
              </Fragment>
            )}
          </Col>
        </Row>
      </Container>
    );
  }
}

const REACTOR_QUERY = gql`
  query Reactors($simulatorId: ID!) {
    reactors(simulatorId: $simulatorId) {
      id
      type
      name
      heat
      heatRate
      model
      coolant
      damage {
        damaged
      }
      ejected
      efficiency
      displayName
      powerOutput
      batteryChargeRate
      batteryChargeLevel
    }
  }
`;
export default graphql(REACTOR_QUERY, {
  options: ownProps => ({
    fetchPolicy: "cache-and-network",
    variables: {
      simulatorId: ownProps.simulator.id
    }
  })
})(withApollo(ReactorControl));
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import Table from '../../../components/uielements/table';
import Pagination from '../../../components/uielements/pagination';
import Box from '../../../components/utility/box';
import ContentHolder from '../../../components/utility/contentHolder';
import Button from '../../../components/uielements/button';
import campaignActions from '../../../redux/campaign/actions';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';

const {
  indexAllCampaigns
} = campaignActions;

const columns = [{
  title: 'Id',
  dataIndex: 'id',
  key: 'id'
}, {
  title: 'Título',
  dataIndex: 'title',
  key: 'title'
}, {
  title: 'Descripción',
  dataIndex: 'description',
  key: 'description'
}];

const itemsPerPage = 10

class Campaigns extends Component {

	constructor(props) {
		super(props);
		this.state = { page: 1 };
	}

	loadCampaignsPage() {
		this.props.indexAllCampaigns(this.state.page, itemsPerPage)
	}

	componentWillMount() {
		this.loadCampaignsPage()
	}

	pageSelect = (e) => {
		this.setState(e)
		this.loadCampaignsPage()
	}

  render() {
		return (

          <Box>
          	<ContentHolder>
          		<Pagination defaultPageSize={itemsPerPage}
          			defaultCurrent={this.state.page}
          			total={this.props.total_pages}
          			onChange={this.pageSelect} />
            </ContentHolder>
          </Box>
          <Button type="primary">
            <Link to={'/admin/campaigns/new'}>Nueva campaña</Link>
          </Button>

    );
  }
}

function mapStateToProps(state) {
  const { campaigns, total_pages, total_count } = state.Campaign;
  return {
    campaigns: campaigns,
    total_pages: total_pages,
    total_count: total_count
  };
}

export default connect(mapStateToProps, { indexAllCampaigns })(Campaigns);<commit_msg>Implementing the according design to Campaigns list<commit_after>import React, { Component } from 'react';
import Table from '../../../components/uielements/table';
import Pagination from '../../../components/uielements/pagination';
import Button from '../../../components/uielements/button';
import { Row, Col } from 'antd'
import campaignActions from '../../../redux/campaign/actions';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';

const {
  indexAllCampaigns
} = campaignActions;

const columns = [{
  title: 'Id',
  dataIndex: 'id',
  key: 'id'
}, {
  title: 'Título',
  dataIndex: 'title',
  key: 'title'
}, {
  title: 'Descripción',
  dataIndex: 'description',
  key: 'description'
}];

const initialPage = 1
const itemsPerPage = 10

class Campaigns extends Component {

  componentWillMount() {
    this.loadCampaignsPage()
  }

	loadCampaignsPage = (page) => {
		this.props.indexAllCampaigns(page, itemsPerPage)
	}

	pageSelect = (e) => {
		this.loadCampaignsPage(e)
	}

  render() {
    return (

            <Row type="flex" justify="space-between">
              <Col span={6}>

Lista de campañas

              </Col>
              <Col span={5}>
                <Button type="primary">
                  <Link to={'/admin/campaigns/new'}>Nueva campaña</Link>
                </Button>
              </Col>
            </Row>

            <Pagination defaultPageSize={ itemsPerPage }
              defaultCurrent={ initialPage }
              total={ this.props.total_count }
              onChange={ this.loadCampaignsPage } />

    );
  }
}

function mapStateToProps(state) {
  const { campaigns, total_pages, total_count } = state.Campaign;
  return {
    campaigns: campaigns,
    total_pages: total_pages,
    total_count: total_count
  };
}

export default connect(mapStateToProps, { indexAllCampaigns })(Campaigns);<|endoftext|>


<commit_before><commit_msg>delete unused file<commit_after><|endoftext|>


<commit_before>'use strict';
var React = require('react');
var url = require('url');
var globals = require('./globals');
var parseAndLogError = require('./mixins').parseAndLogError;
var StickyHeader = require('./StickyHeader');

var lookup_column = function (result, column) {
    var value = result;
    var names = column.split('.');
    for (var i = 0, len = names.length; i < len && value !== undefined; i++) {
        if (value[0]) {
            if (value[0][names[i]]){
                value = value[0][names[i]];
            }
        }else{
            value = value[names[i]];
        }
    }
    return value;
};

    var Collection = module.exports.Collection = React.createClass({
        render: function () {
            var context = this.props.context;
            return (

{context.title}

{context.description}

            );
        }
    });

    globals.content_views.register(Collection, 'Collection');

    class Cell {
        constructor(value, sortable) {
            this.value = value;
            this.sortable = sortable;
        }
    }

    class Row {
        constructor(item, cells, text) {
            this.item = item;
            this.cells = cells;
            this.text = text;
        }
    }

    class Data {
        constructor(rows) {
            this.rows = rows;
            this.sortedOn = null;
            this.reversed = false;
        }
        sort(sortColumn, reverse) {
            reverse = !!reverse;
            if (this.sortedOn === sortColumn && this.reversed === reverse) return;
            this.sortedOn = sortColumn;
            this.reversed = reverse;
            this.rows.sort(function (rowA, rowB) {
                var a = '' + rowA.cells[sortColumn].sortable;
                var b = '' + rowB.cells[sortColumn].sortable;
                if (a  b) {
                    return reverse ? -1 : 1;
                }
                return 0;
            });
        }
    }

    var RowView = function (props) {
        var row = props.row;
        var id = row.item['@id'];
        var tds = row.cells.map( function (cell, index) {
            if (index === 0) {
                return (
                    {cell.value}
                );
            }
            return (
                {cell.value}
            );
        });
        return (

{tds}

        );
    };

    var Table = module.exports.Table = React.createClass({
        contextTypes: {
            fetch: React.PropTypes.func,
            location_href: React.PropTypes.string
        },

        getDefaultProps: function () {
            return {
                defaultSortOn: 0,
                showControls: true,
            };
        },

        getInitialState: function () {
            var state = this.extractParams(this.props, this.context);
            state.columns = this.guessColumns(this.props);
            state.data = new Data([]);  // Tables may be long so render empty first
            state.communicating = true;
            return state;
        },

        componentWillReceiveProps: function (nextProps, nextContext) {
            var updateData = false;
            if (nextProps.context !== this.props.context) {
                updateData = true;
                this.setState({
                    communicating: this.fetchAll(nextProps)
                });
            }
            if (nextProps.columns !== this.props.columns) {
                updateData = true;
            }
            if (updateData) {
                var columns = this.guessColumns(nextProps);
                this.extractData(nextProps, columns);
            }
            if (nextContext.location_href !== this.context.location_href) {
                this.extractParams(nextProps, nextContext);
            }

        },

        extractParams: function(props, context) {
            var params = url.parse(context.location_href, true).query;
            var sorton = parseInt(params.sorton, 10);
            if (isNaN(sorton)) {
                sorton = props.defaultSortOn;
            }
            var state = {
                sortOn: sorton,
                reversed: params.reversed || false,
                searchTerm: params.q || ''
            };
            this.setState(state);
            return state;
        },

        guessColumns: function (props) {
            var column_list = props.columns || props.context.columns;
            var columns = [];
            if (!column_list || Object.keys(column_list).length === 0) {
                for (var key in props.context['@graph'][0]) {
                    if (key.slice(0, 1) != '@' && key.search(/(uuid|_no|accession)/) == -1) {
                        columns.push(key);
                    }
                }
                columns.sort();
                columns.unshift('@id');
            } else {
                for(var column in column_list) {
                    columns.push(column);
                }
            }
            this.setState({columns: columns});
            return columns;
        },

        extractData: function (props, columns) {
            var context = props.context;
            columns = columns || this.state.columns;
            var rows = context['@graph'].map(function (item) {
                var cells = columns.map(function (column) {
                    var factory;
                    // cell factories
                    //if (factory) {
                    //    return factory({context: item, column: column});
                    //};
                    var value = lookup_column(item, column);
                    if (column == '@id') {
                        factory = globals.listing_titles.lookup(item);
                        value = factory({context: item});
                    } else if(typeof value === 'string') {
                        value = value;
                    } else if (value == null) {
                        value = '';
                    } else if (value[0]['@type']) {
                        if(value[0]['description']){
                            var str = value[0]['description'];
                            // If str length is > 100 characters, split at closest word and concat '...'
                            if (str.length > 100){
                                value = (str.match(/^.{80}\w*/))[0].concat(' ...');
                            }else{
                                value = str;
                            }
                        }else{
                            factory = globals.listing_titles.lookup(value[0]);
                            value = factory({context: value[0]});
                        }
                    } else if (value instanceof Array) {
                        value = value;
                    }
                    var sortable = ('' + value).toLowerCase();
                    return new Cell(value, sortable);
                });
                var text = cells.map(function (cell) {
                    return cell.value;
                }).join(' ').toLowerCase();
                return new Row(item, cells, text);
            });
            var data = new Data(rows);
            this.setState({data: data});
            return data;
        },

        fetchAll: function (props) {
            var context = props.context;
            var communicating;
            var request = this.state.allRequest;
            if (request) request.abort();
            var self = this;
            if (context.all) {
                communicating = true;
                request = this.context.fetch(context.all, {
                    headers: {'Accept': 'application/json'}
                });
                request.then(response => {
                    if (!response.ok) throw response;
                    return response.json();
                })
                .then(data => {
                    self.extractData({context: data});
                    self.setState({communicating: false});
                }, parseAndLogError.bind(undefined, 'allRequest'));
                this.setState({
                    allRequest: request,
                    communicating: true
                });
            }
            return communicating;
        },

        render: function () {
            var columns = this.state.columns;
            var context = this.props.context;
            var defaultSortOn = this.props.defaultSortOn;
            var sortOn = this.state.sortOn;
            var reversed = this.state.reversed;
            var searchTerm = this.state.searchTerm;
            this.state.searchTerm = searchTerm;
            var titles = context.columns || {};
            var data = this.state.data;
            var params = url.parse(this.context.location_href, true).query;
            var total = context.count || data.rows.length;
            data.sort(sortOn, reversed);
            var self = this;
            var headers = columns.map(function (column, index) {
                var className = "icon";
                if (index === sortOn) {
                    className += reversed ? " icon-chevron-down" : " icon-chevron-up";
                }
                return (

                        {titles[column] && titles[column]['title'] || column}

                );
            });
            var actions = (context.actions || []).map(action =>

                        <button className={'btn ' + action.className || ''}>{action.title}</button>

            );
            var searchTermLower = this.state.searchTerm.trim().toLowerCase();
            var matching = [];
            var not_matching = [];
            // Reorder rows so that the nth-child works
            if (searchTerm) {
                data.rows.forEach(function (row) {
                    if (row.text.indexOf(searchTermLower) == -1) {
                        not_matching.push(row);
                    } else {
                        matching.push(row);
                    }
                });
            } else {
                matching = data.rows;
            }
            var rows = matching.map(function (row) {
                return RowView({row: row});
            });
            rows.push.apply(rows, not_matching.map(function (row) {
                return RowView({row: row, hidden: true});
            }));
            var table_class = "sticky-area collection-table";
            var loading_or_total;
            if (this.state.communicating) {
                table_class += ' communicating';
                loading_or_total = (
                    Loading...
                );
            } else {
                loading_or_total = (

                        {matching.length}
                        of {total} records

                );
            }
            return (

                      <StickyHeader>

                            {this.props.showControls ?

                                    {loading_or_total}
                                    {actions}
                                    <form ref="form" className="table-filter" onKeyUp={this.handleKeyUp}
                                        data-skiprequest="true" data-removeempty="true">
                                        <input ref="q" disabled={this.state.communicating || undefined}
                                            name="q" type="search" defaultValue={searchTerm}
                                            placeholder="Filter table by..." className="filter form-control"
                                            id="table-filter" />

                                        <input ref="sorton" type="hidden" name="sorton" defaultValue={sortOn !== defaultSortOn ? sortOn : ''} />
                                        <input ref="reversed" type="hidden" name="reversed" defaultValue={!!reversed || ''} />
                                    </form>

 : ''}

                                {headers}

                      </StickyHeader>

                            {rows}

            );
        },

        componentDidMount: function () {
            this.setState({
                data: this.extractData(this.props),
                communicating: this.fetchAll(this.props),
                mounted: true,
            });
        },

        handleClickHeader: function (event) {
            var target = event.target;
            while (target.tagName != 'TH') {
                target = target.parentElement;
            }
            var cellIndex = target.cellIndex;
            var reversed = '';
            var sorton = this.refs.sorton.getDOMNode();
            if (this.props.defaultSortOn !== cellIndex) {
                sorton.value = cellIndex;
            } else {
                sorton.value = '';
            }
            if (this.state.sortOn == cellIndex) {
                reversed = !this.state.reversed || '';
            }
            this.refs.reversed.getDOMNode().value = reversed;
            event.preventDefault();
            event.stopPropagation();
            this.submit();
        },

        handleKeyUp: function (event) {
            if (typeof this.submitTimer != 'undefined') {
                clearTimeout(this.submitTimer);
            }
            // Skip when enter key is pressed
            if (event.nativeEvent.keyCode == 13) return;
            // IE8 should only submit on enter as page reload is triggered
            if (!this.hasEvent) return;
            this.submitTimer = setTimeout(this.submit, 200);
        },

        hasEvent: typeof Event !== 'undefined',

        submit: function () {
            // form.submit() does not fire onsubmit handlers...
            var target = this.refs.form.getDOMNode();

            // IE8 does not support the Event constructor
            if (!this.hasEvent) {
                target.submit();
                return;
            }

            var event = new Event('submit', {bubbles: true, cancelable: true});
            target.dispatchEvent(event);
        },

        clearFilter: function (event) {
            this.refs.q.getDOMNode().value = '';
            this.submitTimer = setTimeout(this.submit);
        },

        componentWillUnmount: function () {
            if (typeof this.submitTimer != 'undefined') {
                clearTimeout(this.submitTimer);
            }
            var request = this.state.allRequest;
            if (request) request.abort();
        }

    });
<commit_msg>Added hardcoded case to collection.js to make PMIDs into clickable links.<commit_after>'use strict';
var React = require('react');
var url = require('url');
var globals = require('./globals');
var parseAndLogError = require('./mixins').parseAndLogError;
var StickyHeader = require('./StickyHeader');

var lookup_column = function (result, column) {
    var value = result;
    var names = column.split('.');
    for (var i = 0, len = names.length; i < len && value !== undefined; i++) {
        if (value[0]) {
            if (value[0][names[i]]){
                value = value[0][names[i]];
            }
        }else{
            value = value[names[i]];
        }
    }
    return value;
};

    var Collection = module.exports.Collection = React.createClass({
        render: function () {
            var context = this.props.context;
            return (

{context.title}

{context.description}

            );
        }
    });

    globals.content_views.register(Collection, 'Collection');

    class Cell {
        constructor(value, sortable) {
            this.value = value;
            this.sortable = sortable;
        }
    }

    class Row {
        constructor(item, cells, text) {
            this.item = item;
            this.cells = cells;
            this.text = text;
        }
    }

    class Data {
        constructor(rows) {
            this.rows = rows;
            this.sortedOn = null;
            this.reversed = false;
        }
        sort(sortColumn, reverse) {
            reverse = !!reverse;
            if (this.sortedOn === sortColumn && this.reversed === reverse) return;
            this.sortedOn = sortColumn;
            this.reversed = reverse;
            this.rows.sort(function (rowA, rowB) {
                var a = '' + rowA.cells[sortColumn].sortable;
                var b = '' + rowB.cells[sortColumn].sortable;
                if (a  b) {
                    return reverse ? -1 : 1;
                }
                return 0;
            });
        }
    }

    var RowView = function (props) {
        var row = props.row;
        var id = row.item['@id'];
        var tds = row.cells.map( function (cell, index) {
            if (index === 0) {
                return (
                    {cell.value}
                );
            // hardcode pmid link activity. TODO make more generic?
            } else if (cell.value.slice(0,5) === "PMID:") {
                if (cell.value.length > 5){
                    var pmid_link = 'http://www.ncbi.nlm.nih.gov/pubmed/?term=' + cell.value.slice(5);
                    return (
                        {cell.value}
                    );
                }
            }
            return (
                {cell.value}
            );
        });
        return (

{tds}

        );
    };

    var Table = module.exports.Table = React.createClass({
        contextTypes: {
            fetch: React.PropTypes.func,
            location_href: React.PropTypes.string
        },

        getDefaultProps: function () {
            return {
                defaultSortOn: 0,
                showControls: true,
            };
        },

        getInitialState: function () {
            var state = this.extractParams(this.props, this.context);
            state.columns = this.guessColumns(this.props);
            state.data = new Data([]);  // Tables may be long so render empty first
            state.communicating = true;
            return state;
        },

        componentWillReceiveProps: function (nextProps, nextContext) {
            var updateData = false;
            if (nextProps.context !== this.props.context) {
                updateData = true;
                this.setState({
                    communicating: this.fetchAll(nextProps)
                });
            }
            if (nextProps.columns !== this.props.columns) {
                updateData = true;
            }
            if (updateData) {
                var columns = this.guessColumns(nextProps);
                this.extractData(nextProps, columns);
            }
            if (nextContext.location_href !== this.context.location_href) {
                this.extractParams(nextProps, nextContext);
            }

        },

        extractParams: function(props, context) {
            var params = url.parse(context.location_href, true).query;
            var sorton = parseInt(params.sorton, 10);
            if (isNaN(sorton)) {
                sorton = props.defaultSortOn;
            }
            var state = {
                sortOn: sorton,
                reversed: params.reversed || false,
                searchTerm: params.q || ''
            };
            this.setState(state);
            return state;
        },

        guessColumns: function (props) {
            var column_list = props.columns || props.context.columns;
            var columns = [];
            if (!column_list || Object.keys(column_list).length === 0) {
                for (var key in props.context['@graph'][0]) {
                    if (key.slice(0, 1) != '@' && key.search(/(uuid|_no|accession)/) == -1) {
                        columns.push(key);
                    }
                }
                columns.sort();
                columns.unshift('@id');
            } else {
                for(var column in column_list) {
                    columns.push(column);
                }
            }
            this.setState({columns: columns});
            return columns;
        },

        extractData: function (props, columns) {
            var context = props.context;
            columns = columns || this.state.columns;
            var rows = context['@graph'].map(function (item) {
                var cells = columns.map(function (column) {
                    var factory;
                    // cell factories
                    //if (factory) {
                    //    return factory({context: item, column: column});
                    //};
                    var value = lookup_column(item, column);
                    if (column == '@id') {
                        factory = globals.listing_titles.lookup(item);
                        value = factory({context: item});
                    } else if(typeof value === 'string') {
                        value = value;
                    } else if (value == null) {
                        value = '';
                    } else if (value[0]['@type']) {
                        if(value[0]['description']){
                            var str = value[0]['description'];
                            // If str length is > 100 characters, split at closest word and concat '...'
                            if (str.length > 100){
                                value = (str.match(/^.{80}\w*/))[0].concat(' ...');
                            }else{
                                value = str;
                            }
                        }else{
                            factory = globals.listing_titles.lookup(value[0]);
                            value = factory({context: value[0]});
                        }
                    } else if (value instanceof Array) {
                        value = value;
                    }
                    var sortable = ('' + value).toLowerCase();
                    return new Cell(value, sortable);
                });
                var text = cells.map(function (cell) {
                    return cell.value;
                }).join(' ').toLowerCase();
                return new Row(item, cells, text);
            });
            var data = new Data(rows);
            this.setState({data: data});
            return data;
        },

        fetchAll: function (props) {
            var context = props.context;
            var communicating;
            var request = this.state.allRequest;
            if (request) request.abort();
            var self = this;
            if (context.all) {
                communicating = true;
                request = this.context.fetch(context.all, {
                    headers: {'Accept': 'application/json'}
                });
                request.then(response => {
                    if (!response.ok) throw response;
                    return response.json();
                })
                .then(data => {
                    self.extractData({context: data});
                    self.setState({communicating: false});
                }, parseAndLogError.bind(undefined, 'allRequest'));
                this.setState({
                    allRequest: request,
                    communicating: true
                });
            }
            return communicating;
        },

        render: function () {
            var columns = this.state.columns;
            var context = this.props.context;
            var defaultSortOn = this.props.defaultSortOn;
            var sortOn = this.state.sortOn;
            var reversed = this.state.reversed;
            var searchTerm = this.state.searchTerm;
            this.state.searchTerm = searchTerm;
            var titles = context.columns || {};
            var data = this.state.data;
            var params = url.parse(this.context.location_href, true).query;
            var total = context.count || data.rows.length;
            data.sort(sortOn, reversed);
            var self = this;
            var headers = columns.map(function (column, index) {
                var className = "icon";
                if (index === sortOn) {
                    className += reversed ? " icon-chevron-down" : " icon-chevron-up";
                }
                return (

                        {titles[column] && titles[column]['title'] || column}

                );
            });
            var actions = (context.actions || []).map(action =>

                        <button className={'btn ' + action.className || ''}>{action.title}</button>

            );
            var searchTermLower = this.state.searchTerm.trim().toLowerCase();
            var matching = [];
            var not_matching = [];
            // Reorder rows so that the nth-child works
            if (searchTerm) {
                data.rows.forEach(function (row) {
                    if (row.text.indexOf(searchTermLower) == -1) {
                        not_matching.push(row);
                    } else {
                        matching.push(row);
                    }
                });
            } else {
                matching = data.rows;
            }
            var rows = matching.map(function (row) {
                return RowView({row: row});
            });
            rows.push.apply(rows, not_matching.map(function (row) {
                return RowView({row: row, hidden: true});
            }));
            var table_class = "sticky-area collection-table";
            var loading_or_total;
            if (this.state.communicating) {
                table_class += ' communicating';
                loading_or_total = (
                    Loading...
                );
            } else {
                loading_or_total = (

                        {matching.length}
                        of {total} records

                );
            }
            return (

                      <StickyHeader>

                            {this.props.showControls ?

                                    {loading_or_total}
                                    {actions}
                                    <form ref="form" className="table-filter" onKeyUp={this.handleKeyUp}
                                        data-skiprequest="true" data-removeempty="true">
                                        <input ref="q" disabled={this.state.communicating || undefined}
                                            name="q" type="search" defaultValue={searchTerm}
                                            placeholder="Filter table by..." className="filter form-control"
                                            id="table-filter" />

                                        <input ref="sorton" type="hidden" name="sorton" defaultValue={sortOn !== defaultSortOn ? sortOn : ''} />
                                        <input ref="reversed" type="hidden" name="reversed" defaultValue={!!reversed || ''} />
                                    </form>

 : ''}

                                {headers}

                      </StickyHeader>

                            {rows}

            );
        },

        componentDidMount: function () {
            this.setState({
                data: this.extractData(this.props),
                communicating: this.fetchAll(this.props),
                mounted: true,
            });
        },

        handleClickHeader: function (event) {
            var target = event.target;
            while (target.tagName != 'TH') {
                target = target.parentElement;
            }
            var cellIndex = target.cellIndex;
            var reversed = '';
            var sorton = this.refs.sorton.getDOMNode();
            if (this.props.defaultSortOn !== cellIndex) {
                sorton.value = cellIndex;
            } else {
                sorton.value = '';
            }
            if (this.state.sortOn == cellIndex) {
                reversed = !this.state.reversed || '';
            }
            this.refs.reversed.getDOMNode().value = reversed;
            event.preventDefault();
            event.stopPropagation();
            this.submit();
        },

        handleKeyUp: function (event) {
            if (typeof this.submitTimer != 'undefined') {
                clearTimeout(this.submitTimer);
            }
            // Skip when enter key is pressed
            if (event.nativeEvent.keyCode == 13) return;
            // IE8 should only submit on enter as page reload is triggered
            if (!this.hasEvent) return;
            this.submitTimer = setTimeout(this.submit, 200);
        },

        hasEvent: typeof Event !== 'undefined',

        submit: function () {
            // form.submit() does not fire onsubmit handlers...
            var target = this.refs.form.getDOMNode();

            // IE8 does not support the Event constructor
            if (!this.hasEvent) {
                target.submit();
                return;
            }

            var event = new Event('submit', {bubbles: true, cancelable: true});
            target.dispatchEvent(event);
        },

        clearFilter: function (event) {
            this.refs.q.getDOMNode().value = '';
            this.submitTimer = setTimeout(this.submit);
        },

        componentWillUnmount: function () {
            if (typeof this.submitTimer != 'undefined') {
                clearTimeout(this.submitTimer);
            }
            var request = this.state.allRequest;
            if (request) request.abort();
        }

    });
<|endoftext|>


<commit_before>foam.CLASS({
  package: 'foam.nanos.notification',
  name: 'EmailSetting',
  extends: 'foam.nanos.notification.NotificationSetting',

  javaImports: [
    'foam.dao.ArraySink',
    'foam.dao.DAO',
    'foam.nanos.auth.User',
    'foam.nanos.auth.UserUserJunction',
    'foam.nanos.logger.Logger',
    'foam.nanos.notification.email.EmailMessage',
    'foam.util.Emails.EmailsUtility',
    'java.util.Arrays',
    'java.util.List',
    'net.nanopay.model.Business',
    'static foam.mlang.MLang.EQ'
  ],

  methods: [
    {
      name: 'sendNotification',
      javaCode: `
        if ( user.getDisabledTopicsEmail() != null ) {
          List disabledTopics = Arrays.asList(user.getDisabledTopicsEmail());
          if ( ! disabledTopics.contains(notification.getNotificationType()) ) {
            EmailMessage message = new EmailMessage();
            message.setTo(new String[]{user.getEmail()});

            try {
              if ( foam.util.SafetyUtil.isEmpty(notification.getEmailName()) ) {
                message.setSubject(notification.getTemplate());
                message.setBody(notification.getBody());
                EmailsUtility.sendEmailFromTemplate(x, null, message, null, null);
              } else {
                EmailsUtility.sendEmailFromTemplate(x, user, message, notification.getEmailName(), notification.getEmailArgs());
              }
            } catch(Throwable t) {
              Logger logger = (Logger) x.get("logger");
              logger.error("Error sending notification email message: "+message+". Error: " + t);
            }
          }
        }
      `
    }
  ]
});
  <commit_msg>Update src/foam/nanos/notification/EmailSetting.js<commit_after>foam.CLASS({
  package: 'foam.nanos.notification',
  name: 'EmailSetting',
  extends: 'foam.nanos.notification.NotificationSetting',

  javaImports: [
    'foam.dao.ArraySink',
    'foam.dao.DAO',
    'foam.nanos.auth.User',
    'foam.nanos.auth.UserUserJunction',
    'foam.nanos.logger.Logger',
    'foam.nanos.notification.email.EmailMessage',
    'foam.util.Emails.EmailsUtility',
    'java.util.Arrays',
    'java.util.List',
    'net.nanopay.model.Business',
    'static foam.mlang.MLang.EQ'
  ],

  methods: [
    {
      name: 'sendNotification',
      javaCode: `
        if ( user.getDisabledTopicsEmail() != null ) {
          List disabledTopics = Arrays.asList(user.getDisabledTopicsEmail());
          if ( ! disabledTopics.contains(notification.getNotificationType()) ) {
            EmailMessage message = new EmailMessage();
            message.setTo(new String[]{user.getEmail()});

            try {
              if ( foam.util.SafetyUtil.isEmpty(notification.getEmailName()) ) {
                message.setSubject(notification.getTemplate());
                message.setBody(notification.getBody());
                EmailsUtility.sendEmailFromTemplate(x, null, message, null, null);
              } else {
                EmailsUtility.sendEmailFromTemplate(x, user, message, notification.getEmailName(), notification.getEmailArgs());
              }
            } catch(Throwable t) {
              Logger logger = (Logger) x.get("logger");
              logger.error("Error sending notification email message: " + message + ". Error: " + t);
            }
          }
        }
      `
    }
  ]
});

<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2018 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.notification',
  name: 'Notification',

  implements: [
    'foam.nanos.auth.Authorizable'
  ],

  documentation: 'Notification model responsible for system and integrated messaging notifications.',

  javaImports: [
    'foam.nanos.auth.AuthService',
    'foam.nanos.auth.AuthorizationException',
    'foam.nanos.auth.User'
  ],

  tableColumns: ['id', 'body', 'notificationType', 'broadcasted', 'userId', 'groupId' ],

  properties: [
    {
      class: 'Boolean',
      name: 'read',
      documentation: 'Determines if notification has been read.',
      visibility: 'RO'
    },
    {
      class: 'Long',
      name: 'id',
      visibility: 'RO'
    },
    {
      class: 'String',
      name: 'hostname',
      visibility: 'RO',
      javaFactory: 'return System.getProperty("hostname", "localhost");'
    },
    {
      class: 'String',
      name: 'template'
    },
    {
      class: 'String',
      name: 'notificationType',
      label: 'Notification type',
      documentation: 'Type of notification.',
      value: 'General'
    },
    {
      class: 'Date',
      name: 'issuedDate',
      factory: function() { return new Date(); },
      label: 'Notification Date',
      documentation: 'Date notification was created.',
      visibility: 'RO',
      javaFactory: `return new java.util.Date();`
    },
    {
      class: 'Date',
      name: 'expiryDate',
      documentation: 'Expiration date of notification.',
      factory: function() {
        // 90 days since creation date
        return new Date(Date.now() + (90 * 24 * 60 * 60 * 1000));
      }
    },
    {
      class: 'String',
      name: 'body',
      documentation: 'Notification body'
    },
    {
      class: 'Boolean',
      name: 'broadcasted',
      documentation: 'Determines if notification is sent to all users in a group or system.'
    },
    {
      class: 'Reference',
      of: 'foam.nanos.auth.User',
      name: 'userId',
      documentation: 'User notification will be sent to.',
      view: { class: 'foam.u2.view.ReferenceView', placeholder: 'select user' }
    },
    {
      class: 'Reference',
      of: 'foam.nanos.auth.Group',
      name: 'groupId',
      documentation: 'Group notification will be sent to.',
      view: { class: 'foam.u2.view.ReferenceView', placeholder: 'select group' }
    },
    {
      class: 'Map',
      name: 'emailArgs',
      visibility: 'HIDDEN',
      documentation: 'Arguments for email template.',
      javaFactory: 'return new java.util.HashMap<String, Object>();'
    },
    {
      class: 'String',
      name: 'emailName',
      label: 'Email template name',
      value: 'notification',
      documentation: 'Email template name.'
    },
    {
      class: 'String',
      name: 'slackWebhook',
      documentation: 'Webhook associated to Slack.'
    },
    {
      class: 'String',
      name: 'slackMessage',
      documentation: 'Message to be sent to Slack.'
    }
  ],

  methods: [
    {
      name: 'checkOwnership',
      args: [
        { name: 'x', type: 'Context' }
      ],
      type: 'Boolean',
      javaCode: `
        User user = (User) x.get("user");
        return user != null && getUserId() == user.getId();
      `
    },
    {
      name: 'authorizeOnCreate',
      javaCode: `
      AuthService auth = (AuthService) x.get("auth");
      if ( ! checkOwnership(x) && ! auth.check(x, createPermission("create")) ) throw new AuthorizationException("You don't have permission to create this notification.");
      `
    },
    {
      name: 'authorizeOnUpdate',
      javaCode: `
      AuthService auth = (AuthService) x.get("auth");
      if ( ! checkOwnership(x) && ! auth.check(x, createPermission("update")) ) throw new AuthorizationException("You don't have permission to update notifications you do not own.");
      `
    },
    {
      name: 'authorizeOnDelete',
      javaCode: `
      AuthService auth = (AuthService) x.get("auth");
      if ( ! checkOwnership(x) && ! auth.check(x, "*") ) throw new AuthorizationException("You don't have permission to delete notifications you do not own.");
      `
    },
    {
      name: 'authorizeOnRead',
      javaCode: `
      AuthService auth = (AuthService) x.get("auth");
      if ( ! checkOwnership(x) && ! auth.check(x, createPermission("read")) ) throw new AuthorizationException("You don't have permission to read notifications you do not own.");
      `
    },
    {
      name: 'createPermission',
      args: [
        { name: 'operation', type: 'String' }
      ],
      type: 'String',
      javaCode: `
        return "notification." + operation + "." + getId();
      `
    }
  ]
});
<commit_msg>Added list view to default notification model<commit_after>/**
 * @license
 * Copyright 2018 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.notification',
  name: 'Notification',

  implements: [
    'foam.nanos.auth.Authorizable'
  ],

  documentation: 'Notification model responsible for system and integrated messaging notifications.',

  javaImports: [
    'foam.nanos.auth.AuthService',
    'foam.nanos.auth.AuthorizationException',
    'foam.nanos.auth.User'
  ],

  tableColumns: ['id', 'body', 'notificationType', 'broadcasted', 'userId', 'groupId' ],

  axioms: [
    {
      class: 'foam.comics.v2.namedViews.NamedViewCollection',
      name: 'List',
      view: {
        class: 'foam.u2.DAOList'
        // TODO: Add CitationView for RowView
      },
    },
  ],

  properties: [
    {
      class: 'Boolean',
      name: 'read',
      documentation: 'Determines if notification has been read.',
      visibility: 'RO'
    },
    {
      class: 'Long',
      name: 'id',
      visibility: 'RO'
    },
    {
      class: 'String',
      name: 'hostname',
      visibility: 'RO',
      javaFactory: 'return System.getProperty("hostname", "localhost");'
    },
    {
      class: 'String',
      name: 'template'
    },
    {
      class: 'String',
      name: 'notificationType',
      label: 'Notification type',
      documentation: 'Type of notification.',
      value: 'General'
    },
    {
      class: 'Date',
      name: 'issuedDate',
      factory: function() { return new Date(); },
      label: 'Notification Date',
      documentation: 'Date notification was created.',
      visibility: 'RO',
      javaFactory: `return new java.util.Date();`
    },
    {
      class: 'Date',
      name: 'expiryDate',
      documentation: 'Expiration date of notification.',
      factory: function() {
        // 90 days since creation date
        return new Date(Date.now() + (90 * 24 * 60 * 60 * 1000));
      }
    },
    {
      class: 'String',
      name: 'body',
      documentation: 'Notification body'
    },
    {
      class: 'Boolean',
      name: 'broadcasted',
      documentation: 'Determines if notification is sent to all users in a group or system.'
    },
    {
      class: 'Reference',
      of: 'foam.nanos.auth.User',
      name: 'userId',
      documentation: 'User notification will be sent to.',
      view: { class: 'foam.u2.view.ReferenceView', placeholder: 'select user' }
    },
    {
      class: 'Reference',
      of: 'foam.nanos.auth.Group',
      name: 'groupId',
      documentation: 'Group notification will be sent to.',
      view: { class: 'foam.u2.view.ReferenceView', placeholder: 'select group' }
    },
    {
      class: 'Map',
      name: 'emailArgs',
      visibility: 'HIDDEN',
      documentation: 'Arguments for email template.',
      javaFactory: 'return new java.util.HashMap<String, Object>();'
    },
    {
      class: 'String',
      name: 'emailName',
      label: 'Email template name',
      value: 'notification',
      documentation: 'Email template name.'
    },
    {
      class: 'String',
      name: 'slackWebhook',
      documentation: 'Webhook associated to Slack.'
    },
    {
      class: 'String',
      name: 'slackMessage',
      documentation: 'Message to be sent to Slack.'
    }
  ],

  methods: [
    {
      name: 'checkOwnership',
      args: [
        { name: 'x', type: 'Context' }
      ],
      type: 'Boolean',
      javaCode: `
        User user = (User) x.get("user");
        return user != null && getUserId() == user.getId();
      `
    },
    {
      name: 'authorizeOnCreate',
      javaCode: `
      AuthService auth = (AuthService) x.get("auth");
      if ( ! checkOwnership(x) && ! auth.check(x, createPermission("create")) ) throw new AuthorizationException("You don't have permission to create this notification.");
      `
    },
    {
      name: 'authorizeOnUpdate',
      javaCode: `
      AuthService auth = (AuthService) x.get("auth");
      if ( ! checkOwnership(x) && ! auth.check(x, createPermission("update")) ) throw new AuthorizationException("You don't have permission to update notifications you do not own.");
      `
    },
    {
      name: 'authorizeOnDelete',
      javaCode: `
      AuthService auth = (AuthService) x.get("auth");
      if ( ! checkOwnership(x) && ! auth.check(x, "*") ) throw new AuthorizationException("You don't have permission to delete notifications you do not own.");
      `
    },
    {
      name: 'authorizeOnRead',
      javaCode: `
      AuthService auth = (AuthService) x.get("auth");
      if ( ! checkOwnership(x) && ! auth.check(x, createPermission("read")) ) throw new AuthorizationException("You don't have permission to read notifications you do not own.");
      `
    },
    {
      name: 'createPermission',
      args: [
        { name: 'operation', type: 'String' }
      ],
      type: 'String',
      javaCode: `
        return "notification." + operation + "." + getId();
      `
    }
  ]
});
<|endoftext|>