<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

cr.define('options', function() {

var OptionsPage = options.OptionsPage;

  //
  // AdvancedOptions class
  // Encapsulated handling of advanced options page.
  //
  function AdvancedOptions() {
    OptionsPage.call(this, 'advanced', templateData.advancedPage,
                     'advancedPage');
  }

  cr.addSingletonGetter(AdvancedOptions);

  AdvancedOptions.prototype = {
    // Inherit AdvancedOptions from OptionsPage.
    __proto__: options.OptionsPage.prototype,

    /**
     * Initializes the page.
     */
    initializePage: function() {
      // Call base class implementation to starts preference initialization.
      OptionsPage.prototype.initializePage.call(this);

      // Set up click handlers for buttons.
      $('privacyContentSettingsButton').onclick = function(event) {
        OptionsPage.showPageByName('content');
        OptionsPage.showTab($('cookies-nav-tab'));
        chrome.send('coreOptionsUserMetricsAction',
            ['Options_ContentSettings']);
      };
      $('privacyClearDataButton').onclick = function(event) {
        OptionsPage.showPageByName('clearBrowserDataPage');
        chrome.send('coreOptionsUserMetricsAction', ['Options_ClearData']);
      };

      // 'metricsReportingEnabled' element is only present on Chrome branded
      // builds.
      if ($('metricsReportingEnabled')) {
        $('metricsReportingEnabled').onclick = function(event) {
          chrome.send('metricsReportingCheckboxAction',
              [String(event.target.checked)]);
        };
      }

      $('autoOpenFileTypesResetToDefault').onclick = function(event) {
        chrome.send('autoOpenFileTypesAction');
      };
      $('fontSettingsCustomizeFontsButton').onclick = function(event) {
        OptionsPage.showPageByName('fontSettings');
        chrome.send('coreOptionsUserMetricsAction', ['Options_FontSettings']);
      };
      $('defaultZoomLevel').onchange = function(event) {
        chrome.send('defaultZoomLevelAction',
            [String(event.target.options[event.target.selectedIndex].value)]);
      };
      $('defaultFontSize').onchange = function(event) {
        chrome.send('defaultFontSizeAction',
            [String(event.target.options[event.target.selectedIndex].value)]);
      };

      if (cr.isWindows || cr.isMac) {
        $('certificatesManageButton').onclick = function(event) {
          chrome.send('showManageSSLCertificates');
        };
      } else {
        $('certificatesManageButton').onclick = function(event) {
          OptionsPage.showPageByName('certificateManager');
          OptionsPage.showTab($('personal-certs-nav-tab'));
          chrome.send('coreOptionsUserMetricsAction',
                      ['Options_ManageSSLCertificates']);
        };
      }

      if (!cr.isChromeOS) {
        $('proxiesConfigureButton').onclick = function(event) {
          chrome.send('showNetworkProxySettings');
        };
        $('downloadLocationChangeButton').onclick = function(event) {
          chrome.send('selectDownloadLocation');
        };

        // Remove Windows-style accelerators from the Browse button label.
        // TODO(csilv): Remove this after the accelerator has been removed from
        // the localized strings file, pending removal of old options window.
        $('downloadLocationChangeButton').textContent =
            localStrings.getStringWithoutAccelerator(
                'downloadLocationChangeButton');
      } else {
        $('proxiesConfigureButton').onclick = function(event) {
          OptionsPage.showPageByName('proxy');
          chrome.send('coreOptionsUserMetricsAction',
              ['Options_ShowProxySettings']);
        };
      }

      if (cr.isWindows) {
        $('sslCheckRevocation').onclick = function(event) {
          chrome.send('checkRevocationCheckboxAction',
              [String($('sslCheckRevocation').checked)]);
        };
        $('sslUseSSL3').onclick = function(event) {
          chrome.send('useSSL3CheckboxAction',
              [String($('sslUseSSL3').checked)]);
        };
        $('sslUseTLS1').onclick = function(event) {
          chrome.send('useTLS1CheckboxAction',
              [String($('sslUseTLS1').checked)]);
        };
        $('gearSettingsConfigureGearsButton').onclick = function(event) {
          chrome.send('showGearsSettings');
        };
      }

      // 'cloudPrintProxyEnabled' is true for Chrome branded builds on
      // certain platforms, or could be enabled by a lab.
      if (!cr.isChromeOS &&
          localStrings.getString('enable-cloud-print-proxy') == 'true') {
        $('cloudPrintProxySetupButton').onclick = function(event) {
          if ($('cloudPrintProxyManageButton').style.display == 'none') {
            // Disable the button, set it's text to the intermediate state.
            $('cloudPrintProxySetupButton').textContent =
              localStrings.getString('cloudPrintProxyEnablingButton');
            $('cloudPrintProxySetupButton').disabled = true;
            chrome.send('showCloudPrintSetupDialog');
          } else {
            chrome.send('disableCloudPrintProxy');
          }
        };
        $('cloudPrintProxyManageButton').onclick = function(event) {
          chrome.send('showCloudPrintManagePage');
        };
      }

      $('remotingSetupButton').onclick = function(event) {
        chrome.send('showRemotingSetupDialog');
      }
    }
  };

  //
  // Chrome callbacks
  //

  // Set the checked state of the metrics reporting checkbox.
  AdvancedOptions.SetMetricsReportingCheckboxState = function(
      checked, disabled) {
    $('metricsReportingEnabled').checked = checked;
    $('metricsReportingEnabled').disabled = disabled;
    if (disabled)
      $('metricsReportingEnabledText').className = 'disable-services-span';
  }

  AdvancedOptions.SetMetricsReportingSettingVisibility = function(visible) {
    if (visible) {
      $('metricsReportingSetting').style.display = 'block';
    } else {
      $('metricsReportingSetting').style.display = 'none';
    }
  }

  // Set the default zoom level selected item.
  AdvancedOptions.SetDefaultZoomLevel = function(value) {
    var selectCtl = $('defaultZoomLevel');
    for (var i = 0; i < selectCtl.options.length; i++) {
      if (selectCtl.options[i].value == value) {
        selectCtl.selectedIndex = i;
        return;
      }
    }
    selectCtl.selectedIndex = 4;  // 100%
  };

  // Set the font size selected item.
  AdvancedOptions.SetFontSize = function(fixed_font_size_value,
      font_size_value) {
    var selectCtl = $('defaultFontSize');
    if (fixed_font_size_value == font_size_value) {
      for (var i = 0; i < selectCtl.options.length; i++) {
        if (selectCtl.options[i].value == font_size_value) {
          selectCtl.selectedIndex = i;
          if ($('Custom'))
            selectCtl.remove($('Custom').index);
          return;
        }
      }
    }

    // Add/Select Custom Option in the font size label list.
    if (!$('Custom')) {
      var option = new Option(localStrings.getString('fontSizeLabelCustom'),
                              -1, false, true);
      option.setAttribute("id", "Custom");
      selectCtl.add(option);
    }
    $('Custom').selected = true;
  };

  // Set the download path.
  AdvancedOptions.SetDownloadLocationPath = function(path) {
    if (!cr.isChromeOS)
      $('downloadLocationPath').value = path;
  };

  // Set the enabled state for the autoOpenFileTypesResetToDefault button.
  AdvancedOptions.SetAutoOpenFileTypesDisabledAttribute = function(disabled) {
    $('autoOpenFileTypesResetToDefault').disabled = disabled;
  };

  // Set the enabled state for the proxy settings button.
  AdvancedOptions.SetupProxySettingsSection = function(disabled, label) {
    $('proxiesConfigureButton').disabled = disabled;
    $('proxiesLabel').textContent = label;
  };

  // Set the checked state for the sslCheckRevocation checkbox.
  AdvancedOptions.SetCheckRevocationCheckboxState = function(
      checked, disabled) {
    $('sslCheckRevocation').checked = checked;
    $('sslCheckRevocation').disabled = disabled;
  };

  // Set the checked state for the sslUseSSL3 checkbox.
  AdvancedOptions.SetUseSSL3CheckboxState = function(checked, disabled) {
    $('sslUseSSL3').checked = checked;
    $('sslUseSSL3').disabled = disabled;
  };

  // Set the checked state for the sslUseTLS1 checkbox.
  AdvancedOptions.SetUseTLS1CheckboxState = function(checked, disabled) {
    $('sslUseTLS1').checked = checked;
    $('sslUseTLS1').disabled = disabled;
  };

  // Set the Cloud Print proxy UI to enabled, disabled, or processing.
  AdvancedOptions.SetupCloudPrintProxySection = function(disabled, label) {
    if (!cr.isChromeOS) {
      $('cloudPrintProxyLabel').textContent = label;
      if (disabled) {
        $('cloudPrintProxySetupButton').textContent =
          localStrings.getString('cloudPrintProxyDisabledButton');
        $('cloudPrintProxyManageButton').style.display = 'none';
      } else {
        $('cloudPrintProxySetupButton').textContent =
          localStrings.getString('cloudPrintProxyEnabledButton');
        $('cloudPrintProxyManageButton').style.display = 'inline';
      }
      $('cloudPrintProxySetupButton').disabled = false;
    }
  };

  AdvancedOptions.RemoveCloudPrintProxySection = function() {
    if (!cr.isChromeOS) {
      var proxySectionElm = $('cloud-print-proxy-section');
      proxySectionElm.parentNode.removeChild(proxySectionElm);
    }
  };

  AdvancedOptions.RemoveRemotingSection = function() {
    var proxySectionElm = $('remoting-section');
    proxySectionElm.parentNode.removeChild(proxySectionElm);
  };

  // Export
  return {
    AdvancedOptions: AdvancedOptions
  };

});
<commit_msg>Fix non-existent remotingSetupButton that breaks settings page.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

cr.define('options', function() {

var OptionsPage = options.OptionsPage;

  //
  // AdvancedOptions class
  // Encapsulated handling of advanced options page.
  //
  function AdvancedOptions() {
    OptionsPage.call(this, 'advanced', templateData.advancedPage,
                     'advancedPage');
  }

  cr.addSingletonGetter(AdvancedOptions);

  AdvancedOptions.prototype = {
    // Inherit AdvancedOptions from OptionsPage.
    __proto__: options.OptionsPage.prototype,

    /**
     * Initializes the page.
     */
    initializePage: function() {
      // Call base class implementation to starts preference initialization.
      OptionsPage.prototype.initializePage.call(this);

      // Set up click handlers for buttons.
      $('privacyContentSettingsButton').onclick = function(event) {
        OptionsPage.showPageByName('content');
        OptionsPage.showTab($('cookies-nav-tab'));
        chrome.send('coreOptionsUserMetricsAction',
            ['Options_ContentSettings']);
      };
      $('privacyClearDataButton').onclick = function(event) {
        OptionsPage.showPageByName('clearBrowserDataPage');
        chrome.send('coreOptionsUserMetricsAction', ['Options_ClearData']);
      };

      // 'metricsReportingEnabled' element is only present on Chrome branded
      // builds.
      if ($('metricsReportingEnabled')) {
        $('metricsReportingEnabled').onclick = function(event) {
          chrome.send('metricsReportingCheckboxAction',
              [String(event.target.checked)]);
        };
      }

      $('autoOpenFileTypesResetToDefault').onclick = function(event) {
        chrome.send('autoOpenFileTypesAction');
      };
      $('fontSettingsCustomizeFontsButton').onclick = function(event) {
        OptionsPage.showPageByName('fontSettings');
        chrome.send('coreOptionsUserMetricsAction', ['Options_FontSettings']);
      };
      $('defaultZoomLevel').onchange = function(event) {
        chrome.send('defaultZoomLevelAction',
            [String(event.target.options[event.target.selectedIndex].value)]);
      };
      $('defaultFontSize').onchange = function(event) {
        chrome.send('defaultFontSizeAction',
            [String(event.target.options[event.target.selectedIndex].value)]);
      };

      if (cr.isWindows || cr.isMac) {
        $('certificatesManageButton').onclick = function(event) {
          chrome.send('showManageSSLCertificates');
        };
      } else {
        $('certificatesManageButton').onclick = function(event) {
          OptionsPage.showPageByName('certificateManager');
          OptionsPage.showTab($('personal-certs-nav-tab'));
          chrome.send('coreOptionsUserMetricsAction',
                      ['Options_ManageSSLCertificates']);
        };
      }

      if (!cr.isChromeOS) {
        $('proxiesConfigureButton').onclick = function(event) {
          chrome.send('showNetworkProxySettings');
        };
        $('downloadLocationChangeButton').onclick = function(event) {
          chrome.send('selectDownloadLocation');
        };

        // Remove Windows-style accelerators from the Browse button label.
        // TODO(csilv): Remove this after the accelerator has been removed from
        // the localized strings file, pending removal of old options window.
        $('downloadLocationChangeButton').textContent =
            localStrings.getStringWithoutAccelerator(
                'downloadLocationChangeButton');
      } else {
        $('proxiesConfigureButton').onclick = function(event) {
          OptionsPage.showPageByName('proxy');
          chrome.send('coreOptionsUserMetricsAction',
              ['Options_ShowProxySettings']);
        };
      }

      if (cr.isWindows) {
        $('sslCheckRevocation').onclick = function(event) {
          chrome.send('checkRevocationCheckboxAction',
              [String($('sslCheckRevocation').checked)]);
        };
        $('sslUseSSL3').onclick = function(event) {
          chrome.send('useSSL3CheckboxAction',
              [String($('sslUseSSL3').checked)]);
        };
        $('sslUseTLS1').onclick = function(event) {
          chrome.send('useTLS1CheckboxAction',
              [String($('sslUseTLS1').checked)]);
        };
        $('gearSettingsConfigureGearsButton').onclick = function(event) {
          chrome.send('showGearsSettings');
        };
      }

      // 'cloudPrintProxyEnabled' is true for Chrome branded builds on
      // certain platforms, or could be enabled by a lab.
      if (!cr.isChromeOS &&
          localStrings.getString('enable-cloud-print-proxy') == 'true') {
        $('cloudPrintProxySetupButton').onclick = function(event) {
          if ($('cloudPrintProxyManageButton').style.display == 'none') {
            // Disable the button, set it's text to the intermediate state.
            $('cloudPrintProxySetupButton').textContent =
              localStrings.getString('cloudPrintProxyEnablingButton');
            $('cloudPrintProxySetupButton').disabled = true;
            chrome.send('showCloudPrintSetupDialog');
          } else {
            chrome.send('disableCloudPrintProxy');
          }
        };
        $('cloudPrintProxyManageButton').onclick = function(event) {
          chrome.send('showCloudPrintManagePage');
        };
      }

      if ($('remotingSetupButton')) {
        $('remotingSetupButton').onclick = function(event) {
          chrome.send('showRemotingSetupDialog');
        }
      }
    }
  };

  //
  // Chrome callbacks
  //

  // Set the checked state of the metrics reporting checkbox.
  AdvancedOptions.SetMetricsReportingCheckboxState = function(
      checked, disabled) {
    $('metricsReportingEnabled').checked = checked;
    $('metricsReportingEnabled').disabled = disabled;
    if (disabled)
      $('metricsReportingEnabledText').className = 'disable-services-span';
  }

  AdvancedOptions.SetMetricsReportingSettingVisibility = function(visible) {
    if (visible) {
      $('metricsReportingSetting').style.display = 'block';
    } else {
      $('metricsReportingSetting').style.display = 'none';
    }
  }

  // Set the default zoom level selected item.
  AdvancedOptions.SetDefaultZoomLevel = function(value) {
    var selectCtl = $('defaultZoomLevel');
    for (var i = 0; i < selectCtl.options.length; i++) {
      if (selectCtl.options[i].value == value) {
        selectCtl.selectedIndex = i;
        return;
      }
    }
    selectCtl.selectedIndex = 4;  // 100%
  };

  // Set the font size selected item.
  AdvancedOptions.SetFontSize = function(fixed_font_size_value,
      font_size_value) {
    var selectCtl = $('defaultFontSize');
    if (fixed_font_size_value == font_size_value) {
      for (var i = 0; i < selectCtl.options.length; i++) {
        if (selectCtl.options[i].value == font_size_value) {
          selectCtl.selectedIndex = i;
          if ($('Custom'))
            selectCtl.remove($('Custom').index);
          return;
        }
      }
    }

    // Add/Select Custom Option in the font size label list.
    if (!$('Custom')) {
      var option = new Option(localStrings.getString('fontSizeLabelCustom'),
                              -1, false, true);
      option.setAttribute("id", "Custom");
      selectCtl.add(option);
    }
    $('Custom').selected = true;
  };

  // Set the download path.
  AdvancedOptions.SetDownloadLocationPath = function(path) {
    if (!cr.isChromeOS)
      $('downloadLocationPath').value = path;
  };

  // Set the enabled state for the autoOpenFileTypesResetToDefault button.
  AdvancedOptions.SetAutoOpenFileTypesDisabledAttribute = function(disabled) {
    $('autoOpenFileTypesResetToDefault').disabled = disabled;
  };

  // Set the enabled state for the proxy settings button.
  AdvancedOptions.SetupProxySettingsSection = function(disabled, label) {
    $('proxiesConfigureButton').disabled = disabled;
    $('proxiesLabel').textContent = label;
  };

  // Set the checked state for the sslCheckRevocation checkbox.
  AdvancedOptions.SetCheckRevocationCheckboxState = function(
      checked, disabled) {
    $('sslCheckRevocation').checked = checked;
    $('sslCheckRevocation').disabled = disabled;
  };

  // Set the checked state for the sslUseSSL3 checkbox.
  AdvancedOptions.SetUseSSL3CheckboxState = function(checked, disabled) {
    $('sslUseSSL3').checked = checked;
    $('sslUseSSL3').disabled = disabled;
  };

  // Set the checked state for the sslUseTLS1 checkbox.
  AdvancedOptions.SetUseTLS1CheckboxState = function(checked, disabled) {
    $('sslUseTLS1').checked = checked;
    $('sslUseTLS1').disabled = disabled;
  };

  // Set the Cloud Print proxy UI to enabled, disabled, or processing.
  AdvancedOptions.SetupCloudPrintProxySection = function(disabled, label) {
    if (!cr.isChromeOS) {
      $('cloudPrintProxyLabel').textContent = label;
      if (disabled) {
        $('cloudPrintProxySetupButton').textContent =
          localStrings.getString('cloudPrintProxyDisabledButton');
        $('cloudPrintProxyManageButton').style.display = 'none';
      } else {
        $('cloudPrintProxySetupButton').textContent =
          localStrings.getString('cloudPrintProxyEnabledButton');
        $('cloudPrintProxyManageButton').style.display = 'inline';
      }
      $('cloudPrintProxySetupButton').disabled = false;
    }
  };

  AdvancedOptions.RemoveCloudPrintProxySection = function() {
    if (!cr.isChromeOS) {
      var proxySectionElm = $('cloud-print-proxy-section');
      proxySectionElm.parentNode.removeChild(proxySectionElm);
    }
  };

  AdvancedOptions.RemoveRemotingSection = function() {
    var proxySectionElm = $('remoting-section');
    proxySectionElm.parentNode.removeChild(proxySectionElm);
  };

  // Export
  return {
    AdvancedOptions: AdvancedOptions
  };

});
<|endoftext|>


<commit_before>/*global moment: false*/
/*global prefix: false*/

function formatDate(date) {
  return date ? moment(date).fromNow() : '';
}

function formatDateLong(date) {
  return date ? moment(date).format('YYYY-MM-DD HH:mm:ss') : '';
}

function selectEvent() {
  $('tbody').on('click', 'input.select-row', function (e) {
    if ($(this).prop('checked')) {
      $(e.target).closest('tr').addClass('row-selected');
    } else {
      $(e.target).closest('tr').removeClass('row-selected');
    }
  });
}

function filterEvent() {
  $('tfoot').on('keyup', 'input', function (e) {
    var table = $(this).closest('table');
    var th = $(this).closest('th');
    table.dataTable().fnFilter(this.value, $('tfoot th', table).index(th));
  });
}

function dateColumn(title, key) {
  return {
    sTitle: title,
    mData: function (source, type, val) {
      if (type === 'sort') {
        return source[key];
      }
      return formatDate(source[key]);
    },
    sDefaultContent: ''
  };
}

function personColumn(title, key) {
  return {
    sTitle: title,
    mData: key,
    sDefaultContent: '',
    mRender: function (data, type, full) {
      return '' + data + '';
    },
    bFilter: true
  };
}

function fnWrap(oTableLocal) {
  $(oTableLocal.fnSettings().aoData).each(function () {
    $(this.nTr).removeClass('nowrap');
  });
  oTableLocal.fnAdjustColumnSizing();
}

function fnUnwrap(oTableLocal) {
  $(oTableLocal.fnSettings().aoData).each(function () {
    $(this.nTr).addClass('nowrap');
  });
  oTableLocal.fnAdjustColumnSizing();
}

function fnGetSelected(oTableLocal, selectedClass) {
  var aReturn = [];
  var aTrs = oTableLocal.fnGetNodes();
  var i;
  for (i = 0; i < aTrs.length; i++) {
    if ($(aTrs[i]).hasClass(selectedClass)) {
      aReturn.push(aTrs[i]);
    }
  }
  return aReturn;
}

function fnDeselect(oTableLocal, selectedClass, checkboxClass) {
  var aTrs = oTableLocal.fnGetNodes();
  var i;
  for (i = 0; i < aTrs.length; i++) {
    if ($(aTrs[i]).hasClass(selectedClass)) {
      $(aTrs[i]).removeClass(selectedClass);
      $(aTrs[i]).find('input.' + checkboxClass + ':checked').prop('checked', false);
    }
  }
}

function fnSelectAll(oTableLocal, selectedClass, checkboxClass, filtered) {
  fnDeselect(oTableLocal, selectedClass, checkboxClass);
  var settings = oTableLocal.fnSettings();
  var indexes = (filtered === true) ? settings.aiDisplay : settings.aiDisplayMaster;
  indexes.forEach(function (i) {
    var r = oTableLocal.fnGetNodes(i);
    $(r).addClass(selectedClass);
    $(r).find('input.' + checkboxClass).prop('checked', true);
  });
}

function fnSetDeselect(nTr, selectedClass, checkboxClass) {
  if ($(nTr).hasClass(selectedClass)) {
    $(nTr).removeClass(selectedClass);
    $(nTr).find('input.' + checkboxClass + ':checked').prop('checked', false);
  }
}

function fnSetColumnsVis(oTableLocal, columns, show) {
  columns.forEach(function (e, i, a) {
    oTableLocal.fnSetColumnVis(e, show);
  });
}

function fnAddFilterFoot(sTable, aoColumns) {
  var tr = $('
');
  aoColumns.forEach(function (c) {
    if (c.bFilter) {
      tr.append('<input type="text" placeholder="' + c.sTitle + '" style="width:80%;" autocomplete="off">	');
    } else {
      tr.append('	');
    }
  });
  $(sTable).append($('
').append(tr));
}

function formatTravelerStatus(s) {
  var status = {
    '1': 'active',
    '1.5': 'submitted for completion',
    '2': 'completed',
    '3': 'frozen',
    '0': 'initialized'
  };
  if (status[s.toString()]) {
    return status[s.toString()];
  }
  return 'unknown';
}

$.fn.dataTableExt.oApi.fnAddDataAndDisplay = function (oSettings, aData) {
  /* Add the data */
  var iAdded = this.oApi._fnAddData(oSettings, aData);
  var nAdded = oSettings.aoData[iAdded].nTr;

  /* Need to re-filter and re-sort the table to get positioning correct, not perfect
   * as this will actually redraw the table on screen, but the update should be so fast (and
   * possibly not alter what is already on display) that the user will not notice
   */
  this.oApi._fnReDraw(oSettings);

  /* Find it's position in the table */
  var iPos = -1;
  var i, iLen;

  for (i = 0, iLen = oSettings.aiDisplay.length; i < iLen; i++) {
    if (oSettings.aoData[oSettings.aiDisplay[i]].nTr === nAdded) {
      iPos = i;
      break;
    }
  }

  /* Get starting point, taking account of paging */
  if (iPos >= 0) {
    oSettings._iDisplayStart = (Math.floor(i / oSettings._iDisplayLength)) * oSettings._iDisplayLength;
    this.oApi._fnCalculateEnd(oSettings);
  }

  this.oApi._fnDraw(oSettings);
  return {
    "nTr": nAdded,
    "iPos": iAdded
  };
};

$.fn.dataTableExt.oApi.fnDisplayRow = function (oSettings, nRow) {
  // Account for the "display" all case - row is already displayed
  if (oSettings._iDisplayLength === -1) {
    return;
  }

  // Find the node in the table
  var iPos = -1;
  var i, iLen;
  for (i = 0, iLen = oSettings.aiDisplay.length; i < iLen; i++) {
    if (oSettings.aoData[oSettings.aiDisplay[i]].nTr === nRow) {
      iPos = i;
      break;
    }
  }

  // Alter the start point of the paging display
  if (iPos >= 0) {
    oSettings._iDisplayStart = (Math.floor(i / oSettings._iDisplayLength)) * oSettings._iDisplayLength;
    this.oApi._fnCalculateEnd(oSettings);
  }

  this.oApi._fnDraw(oSettings);
};

var selectColumn = {
  sTitle: '',
  sDefaultContent: '<input type="checkbox" class="select-row">',
  sSortDataType: 'dom-checkbox',
  asSorting: ['desc', 'asc']
};

var idColumn = {
  sTitle: '',
  mData: '_id',
  bVisible: false
};

var formLinkColumn = {
  sTitle: '',
  mData: '_id',
  mRender: function (data, type, full) {
    return '';
  },
  bSortable: false
};

var formShareLinkColumn = {
  sTitle: '',
  mData: '_id',
  mRender: function (data, type, full) {
    return '';
  },
  bSortable: false
};

var createdOnColumn = dateColumn('Created', 'createdOn');
var createdByColumn = personColumn('Created by', 'createdBy');

var clonedByColumn = personColumn('Cloned by', 'clonedBy');

var updatedOnColumn = dateColumn('Updated', 'updatedOn');
var updatedByColumn = personColumn('Updated by', 'updatedBy');

var deadlineColumn = dateColumn('Deadline', 'deadline');

var tagsColumn = {
  sTitle: 'Tags',
  sDefaultContent: '',
  mData: function (source, type, val) {
    if (source.tags) {
      return source.tags.join();
    }
    return '';
  },
  bFilter: true
};

var commentsColumn = {
  sTitle: 'Comments',
  sDefaultContent: '',
  mData: 'comments',
  bFilter: true
};

var titleColumn = {
  sTitle: 'Title',
  mData: 'title',
  bFilter: true
};

var travelerLinkColumn = {
  sTitle: '',
  mData: function (source, type, val) {
    if (source.hasOwnProperty('url')) {
      return '';
    }
    if (source.hasOwnProperty('_id')) {
      return '';
    }
    return 'unknown';
  },
  // mData: '_id',
  // mRender: function(data, type, full) {
  //   return '';
  // },
  bSortable: false
};

var travelerConfigLinkColumn = {
  sTitle: '',
  mData: '_id',
  mRender: function (data, type, full) {
    return '';
  },
  bSortable: false
};

var travelerShareLinkColumn = {
  sTitle: '',
  mData: '_id',
  mRender: function (data, type, full) {
    return '';
  },
  bSortable: false
};

var deviceTravelerLinkColumn = {
  sTitle: '',
  mData: 'serialNumber',
  mRender: function (data, type, full) {
    return '';
  },
  bSortable: false
};

var progressColumn = {
  sTitle: 'Estimated progress',
  bSortable: true,
  sType: 'numeric',
  mData: function (source, type, val) {
    if (!source.hasOwnProperty('totalInput')) {
      if (type === 'sort') {
        return 0;
      }
      return '';
    }
    if (source.totalInput === 0) {
      if (type === 'sort') {
        return 0;
      }
      return '';
    }
    if (!source.hasOwnProperty('finishedInput')) {
      if (type === 'sort') {
        return 0;
      }
      return 'unknown';
    }
    var percentage = Math.floor((source.finishedInput / source.totalInput) * 100);
    if (type === 'sort') {
      return percentage;
    }
    return '

' + source.finishedInput + '/' + source.totalInput + '
';
  }
};

var deviceColumn = {
  sTitle: 'Devices',
  mData: function (source, type, val) {
    if (source.devices) {
      return source.devices.join('; ');
    }
    return '';
  },
  bFilter: true
};

var sharedWithColumn = {
  sTitle: 'Shared with',
  mData: function (source, type, val) {
    if (source.sharedWith) {
      if (source.sharedWith.length === 0) {
        return '';
      }
      var names = source.sharedWith.map(function (u) {
        return u.username;
      });
      return names.join('; ');
    }
    return '';
  },
  bFilter: true
};

var statusColumn = {
  sTitle: 'Status',
  mData: 'status',
  mRender: function (data, type, full) {
    return formatTravelerStatus(data);
  },
  bFilter: true
};

/*shared user columns*/
var useridColumn = personColumn('User id', '_id');

var useridNoLinkColumn = {
  sTitle: 'User id',
  mData: '_id',
  sDefaultContent: '',
  bFilter: true
};

var userNameColumn = {
  sTitle: 'Full name',
  mData: 'username',
  sDefaultContent: '',
  mRender: function (data, type, full) {
    return '' + data + '';
  },
  bFilter: true
};

var userNameNoLinkColumn = {
  sTitle: 'Full name',
  mData: 'username',
  sDefaultContent: '',
  bFilter: true
};

var fullNameNoLinkColumn = {
  sTitle: 'Full name',
  mData: 'name',
  sDefaultContent: '',
  bFilter: true
};

var accessColumn = {
  sTitle: 'Privilege',
  mData: 'access',
  sDefaultContent: '',
  mRender: function (data, type, full) {
    if (data === 0) {
      return 'read';
    }
    if (data === 1) {
      return 'write';
    }
  },
  bFilter: true
};

/*user columns*/
var rolesColumn = {
  sTitle: 'Roles',
  mData: 'roles',
  sDefaultContent: '',
  mRender: function (data, type, full) {
    return data.join();
  },
  bFilter: true
};

var lastVisitedOnColumn = dateColumn('Last visited', 'lastVisitedOn');

/*device columns*/

var serialColumn = {
  sTitle: 'Serial',
  mData: 'serialNumber',
  sDefaultContent: '',
  bFilter: true
};

var typeColumn = {
  sTitle: 'Type',
  mData: 'componentType',
  sDefaultContent: '',
  bFilter: true
};

var descriptionColumn = {
  sTitle: 'Description',
  mData: 'description',
  sDefaultContent: '',
  bFilter: true
};

var modifiedOnColumn = dateColumn('Modified', 'dateModified');

var modifiedByColumn = {
  sTitle: 'Modified by',
  mData: 'modifiedBy',
  sDefaultContent: '',
  bFilter: true
};

var oTableTools = {
  sSwfPath: prefix ? prefix + '/datatables/swf/copy_csv_xls_pdf.swf' : '/datatables/swf/copy_csv_xls_pdf.swf',
  aButtons: [
    'copy',
    'print', {
      'sExtends': 'collection',
      'sButtonText': 'Save ',
      'aButtons': ['csv', 'xls', 'pdf']
    }
  ]
};

var sDom = "<'row-fluid'<'span6'<'control-group'T>>><'row-fluid'<'span6'l><'span6'f>r>t<'row-fluid'<'span6'i><'span6'p>>";
<commit_msg>prefix links in table<commit_after>/*global moment: false*/
/*global prefix: false*/

function formatDate(date) {
  return date ? moment(date).fromNow() : '';
}

function formatDateLong(date) {
  return date ? moment(date).format('YYYY-MM-DD HH:mm:ss') : '';
}

function selectEvent() {
  $('tbody').on('click', 'input.select-row', function (e) {
    if ($(this).prop('checked')) {
      $(e.target).closest('tr').addClass('row-selected');
    } else {
      $(e.target).closest('tr').removeClass('row-selected');
    }
  });
}

function filterEvent() {
  $('tfoot').on('keyup', 'input', function (e) {
    var table = $(this).closest('table');
    var th = $(this).closest('th');
    table.dataTable().fnFilter(this.value, $('tfoot th', table).index(th));
  });
}

function dateColumn(title, key) {
  return {
    sTitle: title,
    mData: function (source, type, val) {
      if (type === 'sort') {
        return source[key];
      }
      return formatDate(source[key]);
    },
    sDefaultContent: ''
  };
}

function personColumn(title, key) {
  return {
    sTitle: title,
    mData: key,
    sDefaultContent: '',
    mRender: function (data, type, full) {
      return '' + data + '';
    },
    bFilter: true
  };
}

function fnWrap(oTableLocal) {
  $(oTableLocal.fnSettings().aoData).each(function () {
    $(this.nTr).removeClass('nowrap');
  });
  oTableLocal.fnAdjustColumnSizing();
}

function fnUnwrap(oTableLocal) {
  $(oTableLocal.fnSettings().aoData).each(function () {
    $(this.nTr).addClass('nowrap');
  });
  oTableLocal.fnAdjustColumnSizing();
}

function fnGetSelected(oTableLocal, selectedClass) {
  var aReturn = [];
  var aTrs = oTableLocal.fnGetNodes();
  var i;
  for (i = 0; i < aTrs.length; i++) {
    if ($(aTrs[i]).hasClass(selectedClass)) {
      aReturn.push(aTrs[i]);
    }
  }
  return aReturn;
}

function fnDeselect(oTableLocal, selectedClass, checkboxClass) {
  var aTrs = oTableLocal.fnGetNodes();
  var i;
  for (i = 0; i < aTrs.length; i++) {
    if ($(aTrs[i]).hasClass(selectedClass)) {
      $(aTrs[i]).removeClass(selectedClass);
      $(aTrs[i]).find('input.' + checkboxClass + ':checked').prop('checked', false);
    }
  }
}

function fnSelectAll(oTableLocal, selectedClass, checkboxClass, filtered) {
  fnDeselect(oTableLocal, selectedClass, checkboxClass);
  var settings = oTableLocal.fnSettings();
  var indexes = (filtered === true) ? settings.aiDisplay : settings.aiDisplayMaster;
  indexes.forEach(function (i) {
    var r = oTableLocal.fnGetNodes(i);
    $(r).addClass(selectedClass);
    $(r).find('input.' + checkboxClass).prop('checked', true);
  });
}

function fnSetDeselect(nTr, selectedClass, checkboxClass) {
  if ($(nTr).hasClass(selectedClass)) {
    $(nTr).removeClass(selectedClass);
    $(nTr).find('input.' + checkboxClass + ':checked').prop('checked', false);
  }
}

function fnSetColumnsVis(oTableLocal, columns, show) {
  columns.forEach(function (e, i, a) {
    oTableLocal.fnSetColumnVis(e, show);
  });
}

function fnAddFilterFoot(sTable, aoColumns) {
  var tr = $('
');
  aoColumns.forEach(function (c) {
    if (c.bFilter) {
      tr.append('<input type="text" placeholder="' + c.sTitle + '" style="width:80%;" autocomplete="off">	');
    } else {
      tr.append('	');
    }
  });
  $(sTable).append($('
').append(tr));
}

function formatTravelerStatus(s) {
  var status = {
    '1': 'active',
    '1.5': 'submitted for completion',
    '2': 'completed',
    '3': 'frozen',
    '0': 'initialized'
  };
  if (status[s.toString()]) {
    return status[s.toString()];
  }
  return 'unknown';
}

$.fn.dataTableExt.oApi.fnAddDataAndDisplay = function (oSettings, aData) {
  /* Add the data */
  var iAdded = this.oApi._fnAddData(oSettings, aData);
  var nAdded = oSettings.aoData[iAdded].nTr;

  /* Need to re-filter and re-sort the table to get positioning correct, not perfect
   * as this will actually redraw the table on screen, but the update should be so fast (and
   * possibly not alter what is already on display) that the user will not notice
   */
  this.oApi._fnReDraw(oSettings);

  /* Find it's position in the table */
  var iPos = -1;
  var i, iLen;

  for (i = 0, iLen = oSettings.aiDisplay.length; i < iLen; i++) {
    if (oSettings.aoData[oSettings.aiDisplay[i]].nTr === nAdded) {
      iPos = i;
      break;
    }
  }

  /* Get starting point, taking account of paging */
  if (iPos >= 0) {
    oSettings._iDisplayStart = (Math.floor(i / oSettings._iDisplayLength)) * oSettings._iDisplayLength;
    this.oApi._fnCalculateEnd(oSettings);
  }

  this.oApi._fnDraw(oSettings);
  return {
    "nTr": nAdded,
    "iPos": iAdded
  };
};

$.fn.dataTableExt.oApi.fnDisplayRow = function (oSettings, nRow) {
  // Account for the "display" all case - row is already displayed
  if (oSettings._iDisplayLength === -1) {
    return;
  }

  // Find the node in the table
  var iPos = -1;
  var i, iLen;
  for (i = 0, iLen = oSettings.aiDisplay.length; i < iLen; i++) {
    if (oSettings.aoData[oSettings.aiDisplay[i]].nTr === nRow) {
      iPos = i;
      break;
    }
  }

  // Alter the start point of the paging display
  if (iPos >= 0) {
    oSettings._iDisplayStart = (Math.floor(i / oSettings._iDisplayLength)) * oSettings._iDisplayLength;
    this.oApi._fnCalculateEnd(oSettings);
  }

  this.oApi._fnDraw(oSettings);
};

var selectColumn = {
  sTitle: '',
  sDefaultContent: '<input type="checkbox" class="select-row">',
  sSortDataType: 'dom-checkbox',
  asSorting: ['desc', 'asc']
};

var idColumn = {
  sTitle: '',
  mData: '_id',
  bVisible: false
};

var formLinkColumn = {
  sTitle: '',
  mData: '_id',
  mRender: function (data, type, full) {
    return '';
  },
  bSortable: false
};

var formShareLinkColumn = {
  sTitle: '',
  mData: '_id',
  mRender: function (data, type, full) {
    return '';
  },
  bSortable: false
};

var createdOnColumn = dateColumn('Created', 'createdOn');
var createdByColumn = personColumn('Created by', 'createdBy');

var clonedByColumn = personColumn('Cloned by', 'clonedBy');

var updatedOnColumn = dateColumn('Updated', 'updatedOn');
var updatedByColumn = personColumn('Updated by', 'updatedBy');

var deadlineColumn = dateColumn('Deadline', 'deadline');

var tagsColumn = {
  sTitle: 'Tags',
  sDefaultContent: '',
  mData: function (source, type, val) {
    if (source.tags) {
      return source.tags.join();
    }
    return '';
  },
  bFilter: true
};

var commentsColumn = {
  sTitle: 'Comments',
  sDefaultContent: '',
  mData: 'comments',
  bFilter: true
};

var titleColumn = {
  sTitle: 'Title',
  mData: 'title',
  bFilter: true
};

var travelerLinkColumn = {
  sTitle: '',
  mData: function (source, type, val) {
    if (source.hasOwnProperty('url')) {
      return '';
    }
    if (source.hasOwnProperty('_id')) {
      return '';
    }
    return 'unknown';
  },
  bSortable: false
};

var travelerConfigLinkColumn = {
  sTitle: '',
  mData: '_id',
  mRender: function (data, type, full) {
    return '';
  },
  bSortable: false
};

var travelerShareLinkColumn = {
  sTitle: '',
  mData: '_id',
  mRender: function (data, type, full) {
    return '';
  },
  bSortable: false
};

var deviceTravelerLinkColumn = {
  sTitle: '',
  mData: 'serialNumber',
  mRender: function (data, type, full) {
    return '';
  },
  bSortable: false
};

var progressColumn = {
  sTitle: 'Estimated progress',
  bSortable: true,
  sType: 'numeric',
  mData: function (source, type, val) {
    if (!source.hasOwnProperty('totalInput')) {
      if (type === 'sort') {
        return 0;
      }
      return '';
    }
    if (source.totalInput === 0) {
      if (type === 'sort') {
        return 0;
      }
      return '';
    }
    if (!source.hasOwnProperty('finishedInput')) {
      if (type === 'sort') {
        return 0;
      }
      return 'unknown';
    }
    var percentage = Math.floor((source.finishedInput / source.totalInput) * 100);
    if (type === 'sort') {
      return percentage;
    }
    return '

' + source.finishedInput + '/' + source.totalInput + '
';
  }
};

var deviceColumn = {
  sTitle: 'Devices',
  mData: function (source, type, val) {
    if (source.devices) {
      return source.devices.join('; ');
    }
    return '';
  },
  bFilter: true
};

var sharedWithColumn = {
  sTitle: 'Shared with',
  mData: function (source, type, val) {
    if (source.sharedWith) {
      if (source.sharedWith.length === 0) {
        return '';
      }
      var names = source.sharedWith.map(function (u) {
        return u.username;
      });
      return names.join('; ');
    }
    return '';
  },
  bFilter: true
};

var statusColumn = {
  sTitle: 'Status',
  mData: 'status',
  mRender: function (data, type, full) {
    return formatTravelerStatus(data);
  },
  bFilter: true
};

/*shared user columns*/
var useridColumn = personColumn('User id', '_id');

var useridNoLinkColumn = {
  sTitle: 'User id',
  mData: '_id',
  sDefaultContent: '',
  bFilter: true
};

var userNameColumn = {
  sTitle: 'Full name',
  mData: 'username',
  sDefaultContent: '',
  mRender: function (data, type, full) {
    return '' + data + '';
  },
  bFilter: true
};

var userNameNoLinkColumn = {
  sTitle: 'Full name',
  mData: 'username',
  sDefaultContent: '',
  bFilter: true
};

var fullNameNoLinkColumn = {
  sTitle: 'Full name',
  mData: 'name',
  sDefaultContent: '',
  bFilter: true
};

var accessColumn = {
  sTitle: 'Privilege',
  mData: 'access',
  sDefaultContent: '',
  mRender: function (data, type, full) {
    if (data === 0) {
      return 'read';
    }
    if (data === 1) {
      return 'write';
    }
  },
  bFilter: true
};

/*user columns*/
var rolesColumn = {
  sTitle: 'Roles',
  mData: 'roles',
  sDefaultContent: '',
  mRender: function (data, type, full) {
    return data.join();
  },
  bFilter: true
};

var lastVisitedOnColumn = dateColumn('Last visited', 'lastVisitedOn');

/*device columns*/

var serialColumn = {
  sTitle: 'Serial',
  mData: 'serialNumber',
  sDefaultContent: '',
  bFilter: true
};

var typeColumn = {
  sTitle: 'Type',
  mData: 'componentType',
  sDefaultContent: '',
  bFilter: true
};

var descriptionColumn = {
  sTitle: 'Description',
  mData: 'description',
  sDefaultContent: '',
  bFilter: true
};

var modifiedOnColumn = dateColumn('Modified', 'dateModified');

var modifiedByColumn = {
  sTitle: 'Modified by',
  mData: 'modifiedBy',
  sDefaultContent: '',
  bFilter: true
};

var oTableTools = {
  sSwfPath: prefix ? prefix + '/datatables/swf/copy_csv_xls_pdf.swf' : '/datatables/swf/copy_csv_xls_pdf.swf',
  aButtons: [
    'copy',
    'print', {
      'sExtends': 'collection',
      'sButtonText': 'Save ',
      'aButtons': ['csv', 'xls', 'pdf']
    }
  ]
};

var sDom = "<'row-fluid'<'span6'<'control-group'T>>><'row-fluid'<'span6'l><'span6'f>r>t<'row-fluid'<'span6'i><'span6'p>>";
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import shallowCompare from 'react/lib/shallowCompare';

function create(asyncFn, getVariables = () => ({})) {
  const componentName = asyncFn.name || asyncFn.displayName;

  return class AsyncComponent extends Component {
    static get displayName() {
      return `Coroutine(${componentName})`;
    }

    constructor(props, context) {
      super(props, context);
      this.state = { body: React.createElement('noscript'),
                     variables: getVariables(props, context) };
      this.iterator = null;
      this.forceUpdateHelper = this.forceUpdate.bind(this);
    }

    forceUpdate(variables = this.state.variables, props = this.props) {
      const additionalProps = { forceUpdate: this.forceUpdateHelper };
      const asyncBody = asyncFn(Object.assign(additionalProps, variables, props));

      this.setState(() => ({ body: React.createElement('noscript'), variables }));

      if (asyncBody instanceof Promise) {
        asyncBody.then(body => {
          this.setState(() => ({ body, variables }));
        });
      } else {
        this.iterator = asyncBody;
        const getNextBody = () => {
          asyncBody.next().then((body) => {
            if (body.done) {
              this.iterator = null;
            } else {
              this.setState(() => ({ body: body.value, variables }));
              return getNextBody();
            }
          });
        };

        getNextBody();
      }
    }

    componentDidMount() {
      return this.forceUpdate();
    }

    componentWillReceiveProps(nextProps) {
      if (shallowCompare(this, nextProps)) {
        if (this.iterator) {
          this.iterator.return();
          this.iterator = null;
        }

        this.forceUpdate(this.state.variables, nextProps);
      }
    }

    componentWillUnmount() {
      if (this.iterator) {
        this.iterator.return();
        this.iterator = null;
      }
    }

    render() {
      return this.state.body;
    }
  }
}

export default { create };
<commit_msg>use better version of handling unmounted component<commit_after>import React, { Component } from 'react';
import shallowCompare from 'react/lib/shallowCompare';

function create(asyncFn, getVariables = () => ({})) {
  const componentName = asyncFn.name || asyncFn.displayName;

  return class AsyncComponent extends Component {
    static get displayName() {
      return `Coroutine(${componentName})`;
    }

    constructor(props, context) {
      super(props, context);
      this.state = { body: React.createElement('noscript'),
                     variables: getVariables(props, context) };
      this.iterator = null;
      this.forceUpdateHelper = this.forceUpdate.bind(this);
      this.isComponentMounted = false;
    }

    forceUpdate(variables = this.state.variables, props = this.props) {
      const additionalProps = { forceUpdate: this.forceUpdateHelper };
      const asyncBody = asyncFn(Object.assign(additionalProps, variables, props));

      if (asyncBody instanceof Promise) {
        asyncBody.then(body => {
          this.setState(() => ({ body, variables }));
        });
      } else {
        this.iterator = asyncBody;
        const getNextBody = () => {
          asyncBody.next().then((body) => {
            if (body.done) {
              this.iterator = null;
            } else {
              this.setState(() => ({ body: body.value, variables }));
              return getNextBody();
            }
          });
        };

        getNextBody();
      }
    }

    componentDidMount() {
      this.isComponentMounted = true;
      return this.forceUpdate();
    }

    componentWillReceiveProps(nextProps) {
      if (shallowCompare(this, nextProps)) {
        if (this.iterator) {
          this.iterator.return();
          this.iterator = null;
        }

        if (this.isComponentMounted) {
          this.forceUpdate(this.state.variables, nextProps);
        }
      }
    }

    componentWillUnmount() {
      if (this.iterator) {
        this.iterator.return();
        this.iterator = null;
      }

      this.isComponentMounted = false;
    }

    render() {
      return this.state.body;
    }
  }
}

export default { create };
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import {Tabs, Tab} from 'react-bootstrap';

import EmbarkJS from 'Embark/EmbarkJS';
import Blockchain from './components/blockchain';
import Whisper from './components/whisper';
import Storage from './components/storage';
import ENS from './components/ens';

import './dapp.css';

class App extends React.Component {

  constructor(props) {
    super(props);

    this.handleSelect = this.handleSelect.bind(this);

    this.state = {
      error: false,
      activeKey: 1,
      whisperEnabled: false,
      storageEnabled: false,
      ensEnabled: false
    };
  }

  componentDidMount() {
    EmbarkJS.onReady((err) => {
      if (err) {
        // If err is not null then it means something went wrong connecting to ethereum
        // you can use this to ask the user to enable metamask for e.g
        return this.setState({error: err});
      }
      if (EmbarkJS.isNewWeb3()) {
        EmbarkJS.Messages.Providers.whisper.getWhisperVersion((err, _version) => {
          if (err) {
            return console.log(err);
          }
          this.setState({whisperEnabled: true});
        });
      } else {
        if (EmbarkJS.Messages.providerName === 'whisper') {
          EmbarkJS.Messages.getWhisperVersion((err, _version) => {
            if (err) {
              return console.log(err);
            }
            this.setState({whisperEnabled: true});
          });
        }
      }

      EmbarkJS.Storage.isAvailable().then((result) => {
        this.setState({storageEnabled: result});
      }).catch(() => {
        this.setState({storageEnabled: false});
      });

      this.setState({
        ensEnabled: EmbarkJS.Names.isAvailable()
      });
    });
  }

  _renderStatus(title, available) {
    let className = available ? 'pull-right status-online' : 'pull-right status-offline';
    return <React.Fragment>
      {title}

    </React.Fragment>;
  }

  handleSelect(key) {
    this.setState({ activeKey: key });
  }

  render() {
    if (this.state.error) {
      return (

Something went wrong connecting to ethereum. Please make sure you have a node running or are using metamask to connect to the ethereum network:

{this.state.error}

);
    }
    return (

Embark - Usage Example

      <Tabs onSelect={this.handleSelect} activeKey={this.state.activeKey} id="uncontrolled-tab-example">
        <Tab eventKey={1} title="Blockchain">
          <Blockchain/>
        </Tab>
        <Tab eventKey={2} title={this._renderStatus('Decentralized Storage', this.state.storageEnabled)}>
          <Storage enabled={this.state.storageEnabled}/>
        </Tab>
        <Tab eventKey={3} title={this._renderStatus('P2P communication (Whisper)', this.state.whisperEnabled)}>
          <Whisper enabled={this.state.whisperEnabled}/>
        </Tab>
        <Tab eventKey={4} title={this._renderStatus('Naming (ENS)', this.state.ensEnabled)}>
          <ENS enabled={this.state.ensEnabled}/>
        </Tab>
      </Tabs>

);
  }
}

ReactDOM.render(<App></App>, document.getElementById('app'));
<commit_msg>use null instead of false<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import {Tabs, Tab} from 'react-bootstrap';

import EmbarkJS from 'Embark/EmbarkJS';
import Blockchain from './components/blockchain';
import Whisper from './components/whisper';
import Storage from './components/storage';
import ENS from './components/ens';

import './dapp.css';

class App extends React.Component {

  constructor(props) {
    super(props);

    this.handleSelect = this.handleSelect.bind(this);

    this.state = {
      error: null,
      activeKey: 1,
      whisperEnabled: false,
      storageEnabled: false,
      ensEnabled: false
    };
  }

  componentDidMount() {
    EmbarkJS.onReady((err) => {
      if (err) {
        // If err is not null then it means something went wrong connecting to ethereum
        // you can use this to ask the user to enable metamask for e.g
        return this.setState({error: err});
      }
      if (EmbarkJS.isNewWeb3()) {
        EmbarkJS.Messages.Providers.whisper.getWhisperVersion((err, _version) => {
          if (err) {
            return console.log(err);
          }
          this.setState({whisperEnabled: true});
        });
      } else {
        if (EmbarkJS.Messages.providerName === 'whisper') {
          EmbarkJS.Messages.getWhisperVersion((err, _version) => {
            if (err) {
              return console.log(err);
            }
            this.setState({whisperEnabled: true});
          });
        }
      }

      EmbarkJS.Storage.isAvailable().then((result) => {
        this.setState({storageEnabled: result});
      }).catch(() => {
        this.setState({storageEnabled: false});
      });

      this.setState({
        ensEnabled: EmbarkJS.Names.isAvailable()
      });
    });
  }

  _renderStatus(title, available) {
    let className = available ? 'pull-right status-online' : 'pull-right status-offline';
    return <React.Fragment>
      {title}

    </React.Fragment>;
  }

  handleSelect(key) {
    this.setState({ activeKey: key });
  }

  render() {
    if (this.state.error) {
      return (

Something went wrong connecting to ethereum. Please make sure you have a node running or are using metamask to connect to the ethereum network:

{this.state.error}

);
    }
    return (

Embark - Usage Example

      <Tabs onSelect={this.handleSelect} activeKey={this.state.activeKey} id="uncontrolled-tab-example">
        <Tab eventKey={1} title="Blockchain">
          <Blockchain/>
        </Tab>
        <Tab eventKey={2} title={this._renderStatus('Decentralized Storage', this.state.storageEnabled)}>
          <Storage enabled={this.state.storageEnabled}/>
        </Tab>
        <Tab eventKey={3} title={this._renderStatus('P2P communication (Whisper)', this.state.whisperEnabled)}>
          <Whisper enabled={this.state.whisperEnabled}/>
        </Tab>
        <Tab eventKey={4} title={this._renderStatus('Naming (ENS)', this.state.ensEnabled)}>
          <ENS enabled={this.state.ensEnabled}/>
        </Tab>
      </Tabs>

);
  }
}

ReactDOM.render(<App></App>, document.getElementById('app'));
<|endoftext|>


<commit_before>'use strict';

const express = require('express');
const bodyParser = require('body-parser');
const crypto = require('crypto');

module.exports = function(eventEmitter, githubToken) {
  var app = express();
  app.use(bodyParser.urlencoded({ extended: true }));

  app.post('/ghwebook', (req, res) => {
    const event     = req.headers['X-GitHub-Event'];
    const signature = req.headers['X-Hub-Signature'];

    verifySignature(signature, req.body.payload, (err, success) => {
      if (success) {
        eventEmitter.emit(event, req.body.payload);
      }
      res.status(200).send('ok');
    });
  });

  function verifySignature(originalSignature, payload, cb) {
    const hmac = crypto.createHmac('sha256', githubToken);

    hmac.on('readable', () => {
      const data = hmac.read();
      if (data) {
        const str  = data.toString('hex');
        const success = str === originalSignature;

        cb(null, success);
      }

    });

    hmac.write(payload);
    hmac.end();
  }

  return app;
};
<commit_msg>Adds a ping endpoint<commit_after>'use strict';

const express = require('express');
const bodyParser = require('body-parser');
const crypto = require('crypto');

module.exports = function(eventEmitter, githubToken) {
  var app = express();
  app.use(bodyParser.urlencoded({ extended: true }));

  app.get('/', (req, res) => {
    res.status(200).send('pong!');
  });

  app.post('/ghwebook', (req, res) => {
    const event     = req.headers['X-GitHub-Event'];
    const signature = req.headers['X-Hub-Signature'];

    verifySignature(signature, req.body.payload, (err, success) => {
      if (success) {
        eventEmitter.emit(event, req.body.payload);
      }
      res.status(200).send('ok');
    });
  });

  function verifySignature(originalSignature, payload, cb) {
    const hmac = crypto.createHmac('sha256', githubToken);

    hmac.on('readable', () => {
      const data = hmac.read();
      if (data) {
        const str  = data.toString('hex');
        const success = str === originalSignature;

        cb(null, success);
      }

    });

    hmac.write(payload);
    hmac.end();
  }

  return app;
};
<|endoftext|>


<commit_before>const { authenticate } = require('@feathersjs/authentication');
const { BadRequest, Forbidden } = require('@feathersjs/errors');
const logger = require('../../../logger');
const { ObjectId } = require('../../../helper/compare');
const {
	hasRole,
	hasRoleNoHook,
	hasPermissionNoHook,
	hasPermission,
} = require('../../../hooks');

const {
	getAge,
} = require('../../../utils');

const constants = require('../../../utils/constants');
const { CONSENT_WITHOUT_PARENTS_MIN_AGE_YEARS } = require('../../consent/config');

/**
 *
 * @param {object} hook - The hook of the server-request, containing req.params.query.roles as role-filter
 * @returns {Promise }
 */
const mapRoleFilterQuery = (hook) => {
	if (hook.params.query.roles) {
		const rolesFilter = hook.params.query.roles;
		hook.params.query.roles = {};
		hook.params.query.roles.$in = rolesFilter;
	}

	return Promise.resolve(hook);
};

const checkUnique = (hook) => {
	const userService = hook.service;
	const { email } = hook.data;
	if (email === undefined) {
		return Promise.reject(new BadRequest('Fehler beim Auslesen der E-Mail-Adresse bei der Nutzererstellung.'));
	}
	return userService.find({ query: { email: email.toLowerCase() } })
		.then((result) => {
			const { length } = result.data;
			if (length === undefined || length >= 2) {
				return Promise.reject(new BadRequest('Fehler beim Prüfen der Datenbankinformationen.'));
			}
			if (length === 0) {
				return Promise.resolve(hook);
			}

			const user = typeof result.data[0] === 'object' ? result.data[0] : {};
			const input = typeof hook.data === 'object' ? hook.data : {};
			const isLoggedIn = ((hook.params || {}).account && hook.params.account.userId);
			// eslint-disable-next-line no-underscore-dangle
			const { asTask } = hook.params._additional || {};

			if (isLoggedIn || asTask === undefined || asTask === 'student') {
				return Promise.reject(new BadRequest(`Die E-Mail Adresse ${email} ist bereits in Verwendung!`));
			} if (asTask === 'parent') {
				userService.update({ _id: user._id }, {
					$set: {
						children: (user.children || []).concat(input.children),
						firstName: input.firstName,
						lastName: input.lastName,
					},
				});
				return Promise.reject(new BadRequest(
					"parentCreatePatch... it's not a bug, it's a feature - and it really is this time!",
					user,
				));
				/* to stop the create process, the message are catch and resolve in regestration hook */
			}

			return Promise.resolve(hook);
		});
};

const checkUniqueAccount = (hook) => {
	const accountService = hook.app.service('/accounts');
	const { email } = hook.data;
	return accountService.find({ query: { username: email.toLowerCase() } })
		.then((result) => {
			if (result.length > 0) {
				return Promise.reject(
					new BadRequest(`Ein Account mit dieser E-Mail Adresse ${email} existiert bereits!`),
				);
			}
			return Promise.resolve(hook);
		});
};

const updateAccountUsername = async (context) => {
	let { params: { account } } = context;
	const {
		data: { email },
		app,
	} = context;

	if (!email) {
		return context;
	}

	if (!context.id) {
		throw new BadRequest('Id is required for email changes');
	}

	if (!account || !ObjectId.equal(context.id, account.userId)) {
		account = (await app.service('/accounts')
			.find({ query: { userId: context.id } }))[0];

		if (!account) return context;
	}

	if (email && account.systemId) {
		delete context.data.email;
		return context;
	}

	await app.service('/accounts')
		// set account in params to context.parmas.account to reference the current user
		.patch(account._id, { username: email }, { account: context.params.account })
		.catch((err) => {
			throw new BadRequest('Can not update account username.', err);
		});
	return context;
};

const removeStudentFromClasses = (hook) => {
	const classesService = hook.app.service('/classes');
	const userId = hook.id;
	if (userId === undefined) {
		throw new BadRequest(
			'Der Nutzer wurde gelöscht, konnte aber eventuell nicht aus allen Klassen/Kursen entfernt werden.',
		);
	}

	const query = { userIds: userId };

	return classesService.find({ query })
		.then((classes) => Promise.all(
			classes.data.map((myClass) => {
				myClass.userIds.splice(myClass.userIds.indexOf(userId), 1);
				return classesService.patch(myClass._id, myClass);
			}),
		).then(() => hook).catch((err) => {
			throw new Forbidden(
				'Der Nutzer wurde gelöscht, konnte aber eventuell nicht aus allen Klassen/Kursen entfernt werden.', err,
			);
		}));
};

const removeStudentFromCourses = async (hook) => {
	const coursesService = hook.app.service('/courses');
	const userId = hook.id;
	if (userId === undefined) {
		throw new BadRequest(
			'Der Nutzer wurde gelöscht, konnte aber eventuell nicht aus allen Klassen/Kursen entfernt werden.',
		);
	}

	try {
		const usersCourses = await coursesService.find({ query: { userIds: userId } });
		await Promise.all(usersCourses.data.map(
			(course) => hook.app.service('courseModel').patch(course._id, { $pull: { userIds: userId } }),
		));
	} catch (err) {
		throw new Forbidden(
			'Der Nutzer wurde gelöscht, konnte aber eventuell nicht aus allen Klassen/Kursen entfernt werden.', err,
		);
	}
};

const sanitizeData = (hook) => {
	if ('email' in hook.data) {
		if (!constants.expressions.email.test(hook.data.email)) {
			return Promise.reject(new BadRequest('Bitte gib eine valide E-Mail Adresse an!'));
		}
	}
	const idRegExp = RegExp('^[0-9a-fA-F]{24}$');
	if ('schoolId' in hook.data) {
		if (!idRegExp.test(hook.data.schoolId)) {
			return Promise.reject(new BadRequest('invalid Id'));
		}
	}
	if ('classId' in hook.data) {
		if (!idRegExp.test(hook.data.classId)) {
			return Promise.reject(new BadRequest('invalid Id'));
		}
	}
	return Promise.resolve(hook);
};

const checkJwt = () => function checkJwtfnc(hook) {
	if (((hook.params || {}).headers || {}).authorization !== undefined) {
		return (authenticate('jwt')).call(this, hook);
	}
	return Promise.resolve(hook);
};

const pinIsVerified = (hook) => {
	if ((hook.params || {}).account && hook.params.account.userId) {
		return (hasPermission(['STUDENT_CREATE', 'TEACHER_CREATE', 'ADMIN_CREATE'])).call(this, hook);
	}
	// eslint-disable-next-line no-underscore-dangle
	const email = (hook.params._additional || {}).parentEmail || hook.data.email;
	return hook.app.service('/registrationPins').find({ query: { email, verified: true } })
		.then((pins) => {
			if (pins.data.length === 1 && pins.data[0].pin) {
				const age = getAge(hook.data.birthday);

				if (!((hook.data.roles || []).includes('student') && age < CONSENT_WITHOUT_PARENTS_MIN_AGE_YEARS)) {
					hook.app.service('/registrationPins').remove(pins.data[0]._id);
				}

				return Promise.resolve(hook);
			}
			return Promise.reject(new BadRequest('Der Pin wurde noch nicht bei der Registrierung eingetragen.'));
		});
};

const securePatching = (hook) => Promise.all([
	hasRole(hook, hook.params.account.userId, 'superhero'),
	hasRole(hook, hook.params.account.userId, 'administrator'),
	hasRole(hook, hook.params.account.userId, 'teacher'),
	hasRole(hook, hook.params.account.userId, 'demoStudent'),
	hasRole(hook, hook.params.account.userId, 'demoTeacher'),
	hasRole(hook, hook.id, 'student'),
])
	.then(([isSuperHero, isAdmin, isTeacher, isDemoStudent, isDemoTeacher, targetIsStudent]) => {
		if (isDemoStudent || isDemoTeacher) {
			return Promise.reject(new Forbidden('Diese Funktion ist im Demomodus nicht verfügbar!'));
		}
		if (!isSuperHero) {
			delete hook.data.schoolId;
			delete (hook.data.$push || {}).schoolId;
		}
		if (!(isSuperHero || isAdmin)) {
			delete hook.data.roles;
			delete (hook.data.$push || {}).roles;
		}
		if (hook.params.account.userId.toString() !== hook.id) {
			if (!(isSuperHero || isAdmin || (isTeacher && targetIsStudent))) {
				return Promise.reject(new BadRequest('You have not the permissions to change other users'));
			}
		}
		return Promise.resolve(hook);
	});

/**
 *
 * @param user {object} - the user the display name has to be generated
 * @param app {object} - the global feathers-app
 * @returns {string} - a display name of the given user
 */
const getDisplayName = (user, app) => app.service('/roles').find({
	// load protected roles
	query: {	// TODO: cache these
		name: ['teacher', 'admin'],
	},
}).then((protectedRoles) => {
	const protectedRoleIds = (protectedRoles.data || []).map((role) => role._id);
	const isProtectedUser = protectedRoleIds.find((role) => (user.roles || []).includes(role));

	user.age = getAge(user.birthday);

	if (isProtectedUser) {
		return user.lastName ? user.lastName : user._id;
	}
	return user.lastName ? `${user.firstName} ${user.lastName}` : user._id;
});

/**
 *
 * @param hook {object} - the hook of the server-request
 * @returns {object} - the hook with the decorated user
 */
const decorateUser = (hook) => getDisplayName(hook.result, hook.app)
	.then((displayName) => {
		hook.result = (hook.result.constructor.name === 'model') ? hook.result.toObject() : hook.result;
		hook.result.displayName = displayName;
	})
	.then(() => Promise.resolve(hook));

/**
 *
 * @param user {object} - a user
 * @returns {object} - a user with avatar info
 */
const setAvatarData = (user) => {
	if (user.firstName && user.lastName) {
		user.avatarInitials = user.firstName.charAt(0) + user.lastName.charAt(0);
	} else {
		user.avatarInitials = '?';
	}
	// css readable value like "#ff0000" needed
	const colors = ['#4a4e4d', '#0e9aa7', '#3da4ab', '#f6cd61', '#fe8a71'];
	if (user.customAvatarBackgroundColor) {
		user.avatarBackgroundColor = user.customAvatarBackgroundColor;
	} else {
		// choose colors based on initials
		const index = (user.avatarInitials.charCodeAt(0) + user.avatarInitials.charCodeAt(1)) % colors.length;
		user.avatarBackgroundColor = colors[index];
	}
	return user;
};

/**
 *
 * @param hook {object} - the hook of the server-request
 * @returns {object} - the hook with the decorated user avatar
 */
const decorateAvatar = (hook) => {
	if (hook.result.total) {
		hook.result = (hook.result.constructor.name === 'model') ? hook.result.toObject() : hook.result;
		(hook.result.data || []).forEach((user) => setAvatarData(user));
	} else {
		// run and find with only one user
		hook.result = setAvatarData(hook.result);
	}

	return Promise.resolve(hook);
};

/**
 *
 * @param hook {object} - the hook of the server-request
 * @returns {object} - the hook with the decorated users
 */
const decorateUsers = (hook) => {
	hook.result = (hook.result.constructor.name === 'model') ? hook.result.toObject() : hook.result;
	const userPromises = (hook.result.data || []).map((user) => getDisplayName(user, hook.app).then((displayName) => {
		user.displayName = displayName;
		return user;
	}));

	return Promise.all(userPromises).then((users) => {
		hook.result.data = users;
		return Promise.resolve(hook);
	});
};

const handleClassId = (hook) => {
	if (!('classId' in hook.data)) {
		return Promise.resolve(hook);
	}
	return hook.app.service('/classes').patch(hook.data.classId, {
		$push: { userIds: hook.result._id },
	}).then((res) => Promise.resolve(hook));
};

const pushRemoveEvent = (hook) => {
	hook.app.emit('users:after:remove', hook);
	return hook;
};

const enforceRoleHierarchyOnDelete = async (hook) => {
	try {
		const userIsSuperhero = await hasRoleNoHook(hook, hook.params.account.userId, 'superhero');
		if (userIsSuperhero) return hook;

		const [targetIsStudent, targetIsTeacher, targetIsAdmin] = await Promise.all([
			hasRoleNoHook(hook, hook.id, 'student'),
			hasRoleNoHook(hook, hook.id, 'teacher'),
			hasRoleNoHook(hook, hook.id, 'administrator'),
		]);
		let permissionChecks = [true];
		if (targetIsStudent) {
			permissionChecks.push(hasPermissionNoHook(hook, hook.params.account.userId, 'STUDENT_DELETE'));
		}
		if (targetIsTeacher) {
			permissionChecks.push(hasPermissionNoHook(hook, hook.params.account.userId, 'TEACHER_DELETE'));
		}
		if (targetIsAdmin) {
			permissionChecks.push(hasRoleNoHook(hook, hook.params.account.userId, 'superhero'));
		}
		permissionChecks = await Promise.all(permissionChecks);

		if (!permissionChecks.reduce((accumulator, val) => accumulator && val)) {
			throw new Forbidden('you dont have permission to delete this user!', { hook });
		}

		return hook;
	} catch (error) {
		logger.error(error);
		throw new Forbidden('you dont have permission to delete this user!');
	}
};

module.exports = {
	mapRoleFilterQuery,
	checkUnique,
	checkJwt,
	checkUniqueAccount,
	updateAccountUsername,
	removeStudentFromClasses,
	removeStudentFromCourses,
	sanitizeData,
	pinIsVerified,
	securePatching,
	decorateUser,
	decorateAvatar,
	decorateUsers,
	handleClassId,
	pushRemoveEvent,
	enforceRoleHierarchyOnDelete,
};
<commit_msg>fix securePatching<commit_after>const { authenticate } = require('@feathersjs/authentication');
const { BadRequest, Forbidden } = require('@feathersjs/errors');
const logger = require('../../../logger');
const { ObjectId } = require('../../../helper/compare');
const {
	hasRole,
	hasRoleNoHook,
	hasPermissionNoHook,
	hasPermission,
} = require('../../../hooks');

const {
	getAge,
} = require('../../../utils');

const constants = require('../../../utils/constants');
const { CONSENT_WITHOUT_PARENTS_MIN_AGE_YEARS } = require('../../consent/config');

/**
 *
 * @param {object} hook - The hook of the server-request, containing req.params.query.roles as role-filter
 * @returns {Promise }
 */
const mapRoleFilterQuery = (hook) => {
	if (hook.params.query.roles) {
		const rolesFilter = hook.params.query.roles;
		hook.params.query.roles = {};
		hook.params.query.roles.$in = rolesFilter;
	}

	return Promise.resolve(hook);
};

const checkUnique = (hook) => {
	const userService = hook.service;
	const { email } = hook.data;
	if (email === undefined) {
		return Promise.reject(new BadRequest('Fehler beim Auslesen der E-Mail-Adresse bei der Nutzererstellung.'));
	}
	return userService.find({ query: { email: email.toLowerCase() } })
		.then((result) => {
			const { length } = result.data;
			if (length === undefined || length >= 2) {
				return Promise.reject(new BadRequest('Fehler beim Prüfen der Datenbankinformationen.'));
			}
			if (length === 0) {
				return Promise.resolve(hook);
			}

			const user = typeof result.data[0] === 'object' ? result.data[0] : {};
			const input = typeof hook.data === 'object' ? hook.data : {};
			const isLoggedIn = ((hook.params || {}).account && hook.params.account.userId);
			// eslint-disable-next-line no-underscore-dangle
			const { asTask } = hook.params._additional || {};

			if (isLoggedIn || asTask === undefined || asTask === 'student') {
				return Promise.reject(new BadRequest(`Die E-Mail Adresse ${email} ist bereits in Verwendung!`));
			} if (asTask === 'parent') {
				userService.update({ _id: user._id }, {
					$set: {
						children: (user.children || []).concat(input.children),
						firstName: input.firstName,
						lastName: input.lastName,
					},
				});
				return Promise.reject(new BadRequest(
					"parentCreatePatch... it's not a bug, it's a feature - and it really is this time!",
					user,
				));
				/* to stop the create process, the message are catch and resolve in regestration hook */
			}

			return Promise.resolve(hook);
		});
};

const checkUniqueAccount = (hook) => {
	const accountService = hook.app.service('/accounts');
	const { email } = hook.data;
	return accountService.find({ query: { username: email.toLowerCase() } })
		.then((result) => {
			if (result.length > 0) {
				return Promise.reject(
					new BadRequest(`Ein Account mit dieser E-Mail Adresse ${email} existiert bereits!`),
				);
			}
			return Promise.resolve(hook);
		});
};

const updateAccountUsername = async (context) => {
	let { params: { account } } = context;
	const {
		data: { email },
		app,
	} = context;

	if (!email) {
		return context;
	}

	if (!context.id) {
		throw new BadRequest('Id is required for email changes');
	}

	if (!account || !ObjectId.equal(context.id, account.userId)) {
		account = (await app.service('/accounts')
			.find({ query: { userId: context.id } }))[0];

		if (!account) return context;
	}

	if (email && account.systemId) {
		delete context.data.email;
		return context;
	}

	await app.service('/accounts')
		// set account in params to context.parmas.account to reference the current user
		.patch(account._id, { username: email }, { account: context.params.account })
		.catch((err) => {
			throw new BadRequest('Can not update account username.', err);
		});
	return context;
};

const removeStudentFromClasses = (hook) => {
	const classesService = hook.app.service('/classes');
	const userId = hook.id;
	if (userId === undefined) {
		throw new BadRequest(
			'Der Nutzer wurde gelöscht, konnte aber eventuell nicht aus allen Klassen/Kursen entfernt werden.',
		);
	}

	const query = { userIds: userId };

	return classesService.find({ query })
		.then((classes) => Promise.all(
			classes.data.map((myClass) => {
				myClass.userIds.splice(myClass.userIds.indexOf(userId), 1);
				return classesService.patch(myClass._id, myClass);
			}),
		).then(() => hook).catch((err) => {
			throw new Forbidden(
				'Der Nutzer wurde gelöscht, konnte aber eventuell nicht aus allen Klassen/Kursen entfernt werden.', err,
			);
		}));
};

const removeStudentFromCourses = async (hook) => {
	const coursesService = hook.app.service('/courses');
	const userId = hook.id;
	if (userId === undefined) {
		throw new BadRequest(
			'Der Nutzer wurde gelöscht, konnte aber eventuell nicht aus allen Klassen/Kursen entfernt werden.',
		);
	}

	try {
		const usersCourses = await coursesService.find({ query: { userIds: userId } });
		await Promise.all(usersCourses.data.map(
			(course) => hook.app.service('courseModel').patch(course._id, { $pull: { userIds: userId } }),
		));
	} catch (err) {
		throw new Forbidden(
			'Der Nutzer wurde gelöscht, konnte aber eventuell nicht aus allen Klassen/Kursen entfernt werden.', err,
		);
	}
};

const sanitizeData = (hook) => {
	if ('email' in hook.data) {
		if (!constants.expressions.email.test(hook.data.email)) {
			return Promise.reject(new BadRequest('Bitte gib eine valide E-Mail Adresse an!'));
		}
	}
	const idRegExp = RegExp('^[0-9a-fA-F]{24}$');
	if ('schoolId' in hook.data) {
		if (!idRegExp.test(hook.data.schoolId)) {
			return Promise.reject(new BadRequest('invalid Id'));
		}
	}
	if ('classId' in hook.data) {
		if (!idRegExp.test(hook.data.classId)) {
			return Promise.reject(new BadRequest('invalid Id'));
		}
	}
	return Promise.resolve(hook);
};

const checkJwt = () => function checkJwtfnc(hook) {
	if (((hook.params || {}).headers || {}).authorization !== undefined) {
		return (authenticate('jwt')).call(this, hook);
	}
	return Promise.resolve(hook);
};

const pinIsVerified = (hook) => {
	if ((hook.params || {}).account && hook.params.account.userId) {
		return (hasPermission(['STUDENT_CREATE', 'TEACHER_CREATE', 'ADMIN_CREATE'])).call(this, hook);
	}
	// eslint-disable-next-line no-underscore-dangle
	const email = (hook.params._additional || {}).parentEmail || hook.data.email;
	return hook.app.service('/registrationPins').find({ query: { email, verified: true } })
		.then((pins) => {
			if (pins.data.length === 1 && pins.data[0].pin) {
				const age = getAge(hook.data.birthday);

				if (!((hook.data.roles || []).includes('student') && age < CONSENT_WITHOUT_PARENTS_MIN_AGE_YEARS)) {
					hook.app.service('/registrationPins').remove(pins.data[0]._id);
				}

				return Promise.resolve(hook);
			}
			return Promise.reject(new BadRequest('Der Pin wurde noch nicht bei der Registrierung eingetragen.'));
		});
};

const securePatching = (hook) => Promise.all([
	hasRole(hook, hook.params.account.userId, 'superhero'),
	hasRole(hook, hook.params.account.userId, 'administrator'),
	hasRole(hook, hook.params.account.userId, 'teacher'),
	hasRole(hook, hook.params.account.userId, 'demoStudent'),
	hasRole(hook, hook.params.account.userId, 'demoTeacher'),
	hasRole(hook, hook.id, 'student'),
])
	.then(([isSuperHero, isAdmin, isTeacher, isDemoStudent, isDemoTeacher, targetIsStudent]) => {
		if (isDemoStudent || isDemoTeacher) {
			return Promise.reject(new Forbidden('Diese Funktion ist im Demomodus nicht verfügbar!'));
		}
		if (!isSuperHero) {
			delete hook.data.schoolId;
			delete (hook.data.$push || {}).schoolId;
		}
		if (!(isSuperHero || isAdmin)) {
			delete hook.data.roles;
			delete (hook.data.$push || {}).roles;
		}
		if (!ObjectId.equal(hook.id, hook.params.account.userId)) {
			if (!(isSuperHero || isAdmin || (isTeacher && targetIsStudent))) {
				return Promise.reject(new BadRequest('You have not the permissions to change other users'));
			}
		}
		return Promise.resolve(hook);
	});

/**
 *
 * @param user {object} - the user the display name has to be generated
 * @param app {object} - the global feathers-app
 * @returns {string} - a display name of the given user
 */
const getDisplayName = (user, app) => app.service('/roles').find({
	// load protected roles
	query: {	// TODO: cache these
		name: ['teacher', 'admin'],
	},
}).then((protectedRoles) => {
	const protectedRoleIds = (protectedRoles.data || []).map((role) => role._id);
	const isProtectedUser = protectedRoleIds.find((role) => (user.roles || []).includes(role));

	user.age = getAge(user.birthday);

	if (isProtectedUser) {
		return user.lastName ? user.lastName : user._id;
	}
	return user.lastName ? `${user.firstName} ${user.lastName}` : user._id;
});

/**
 *
 * @param hook {object} - the hook of the server-request
 * @returns {object} - the hook with the decorated user
 */
const decorateUser = (hook) => getDisplayName(hook.result, hook.app)
	.then((displayName) => {
		hook.result = (hook.result.constructor.name === 'model') ? hook.result.toObject() : hook.result;
		hook.result.displayName = displayName;
	})
	.then(() => Promise.resolve(hook));

/**
 *
 * @param user {object} - a user
 * @returns {object} - a user with avatar info
 */
const setAvatarData = (user) => {
	if (user.firstName && user.lastName) {
		user.avatarInitials = user.firstName.charAt(0) + user.lastName.charAt(0);
	} else {
		user.avatarInitials = '?';
	}
	// css readable value like "#ff0000" needed
	const colors = ['#4a4e4d', '#0e9aa7', '#3da4ab', '#f6cd61', '#fe8a71'];
	if (user.customAvatarBackgroundColor) {
		user.avatarBackgroundColor = user.customAvatarBackgroundColor;
	} else {
		// choose colors based on initials
		const index = (user.avatarInitials.charCodeAt(0) + user.avatarInitials.charCodeAt(1)) % colors.length;
		user.avatarBackgroundColor = colors[index];
	}
	return user;
};

/**
 *
 * @param hook {object} - the hook of the server-request
 * @returns {object} - the hook with the decorated user avatar
 */
const decorateAvatar = (hook) => {
	if (hook.result.total) {
		hook.result = (hook.result.constructor.name === 'model') ? hook.result.toObject() : hook.result;
		(hook.result.data || []).forEach((user) => setAvatarData(user));
	} else {
		// run and find with only one user
		hook.result = setAvatarData(hook.result);
	}

	return Promise.resolve(hook);
};

/**
 *
 * @param hook {object} - the hook of the server-request
 * @returns {object} - the hook with the decorated users
 */
const decorateUsers = (hook) => {
	hook.result = (hook.result.constructor.name === 'model') ? hook.result.toObject() : hook.result;
	const userPromises = (hook.result.data || []).map((user) => getDisplayName(user, hook.app).then((displayName) => {
		user.displayName = displayName;
		return user;
	}));

	return Promise.all(userPromises).then((users) => {
		hook.result.data = users;
		return Promise.resolve(hook);
	});
};

const handleClassId = (hook) => {
	if (!('classId' in hook.data)) {
		return Promise.resolve(hook);
	}
	return hook.app.service('/classes').patch(hook.data.classId, {
		$push: { userIds: hook.result._id },
	}).then((res) => Promise.resolve(hook));
};

const pushRemoveEvent = (hook) => {
	hook.app.emit('users:after:remove', hook);
	return hook;
};

const enforceRoleHierarchyOnDelete = async (hook) => {
	try {
		const userIsSuperhero = await hasRoleNoHook(hook, hook.params.account.userId, 'superhero');
		if (userIsSuperhero) return hook;

		const [targetIsStudent, targetIsTeacher, targetIsAdmin] = await Promise.all([
			hasRoleNoHook(hook, hook.id, 'student'),
			hasRoleNoHook(hook, hook.id, 'teacher'),
			hasRoleNoHook(hook, hook.id, 'administrator'),
		]);
		let permissionChecks = [true];
		if (targetIsStudent) {
			permissionChecks.push(hasPermissionNoHook(hook, hook.params.account.userId, 'STUDENT_DELETE'));
		}
		if (targetIsTeacher) {
			permissionChecks.push(hasPermissionNoHook(hook, hook.params.account.userId, 'TEACHER_DELETE'));
		}
		if (targetIsAdmin) {
			permissionChecks.push(hasRoleNoHook(hook, hook.params.account.userId, 'superhero'));
		}
		permissionChecks = await Promise.all(permissionChecks);

		if (!permissionChecks.reduce((accumulator, val) => accumulator && val)) {
			throw new Forbidden('you dont have permission to delete this user!', { hook });
		}

		return hook;
	} catch (error) {
		logger.error(error);
		throw new Forbidden('you dont have permission to delete this user!');
	}
};

module.exports = {
	mapRoleFilterQuery,
	checkUnique,
	checkJwt,
	checkUniqueAccount,
	updateAccountUsername,
	removeStudentFromClasses,
	removeStudentFromCourses,
	sanitizeData,
	pinIsVerified,
	securePatching,
	decorateUser,
	decorateAvatar,
	decorateUsers,
	handleClassId,
	pushRemoveEvent,
	enforceRoleHierarchyOnDelete,
};
<|endoftext|>