<commit_before>import React, {Component, PropTypes} from "react";
import {Dimensions, StyleSheet, TouchableOpacity, View} from "react-native";

import * as colors from "../lib/colors";
import CircleProgress from "../components/circle-progress";
import Icon from "../components/iwapp-icons";
import Text from "../components/text-lato";

const styles = StyleSheet.create({
    progressQuestionnaire: {
        flexDirection: "column",
        alignItems: "stretch"
    },
    iconQuestionnaireWrp: {
        position: "absolute",
        left: 0,
        right: 0,
        top: 0,
        bottom: 0,
        alignItems: "center",
        justifyContent: "center",
        backgroundColor: colors.transparent
    },
    titQuestionnaire: {
        fontSize: 11,
        textAlign: "center",
        color: colors.textGrey
    },
    percQuestionnaire: {
        fontSize: 9,
        textAlign: "center",
        color: colors.textGrey
    }
});

export default class QuestionnaireProgress extends Component {

    static propTypes = {
        questionnaire: PropTypes.shape({
            key: PropTypes.string,
            color: PropTypes.string,
            value: PropTypes.number,
            icon: PropTypes.string,
            name: PropTypes.string,
            onPress: PropTypes.func
        }).isRequired
    }

    render () {
        const {questionnaire} = this.props;
        const {width, height} = Dimensions.get("window");
        return (
            <View style={[styles.progressQuestionnaire, {
                height: width * .4,
                marginHorizontal: width * .02,
                marginVertical: height * .015
            }]}
            >
                <CircleProgress
                    percentage={questionnaire.value}
                    size={90}
                    strokeColor={questionnaire.color}
                    strokeWidth={2}
                />
                <TouchableOpacity
                    onPress={questionnaire.onPress}
                    style={[styles.iconQuestionnaireWrp, {width: width * .25, height: height * .12}]}
                    transparent={true}
                >
                    <Icon
                        color={questionnaire.color}
                        name={questionnaire.icon}
                        size={width * .14}
                        style={{alignItems: "center"}}
                    />
                </TouchableOpacity>
                <View>
                    <View style={{width: width * .25}}>
                        <Text style={styles.titQuestionnaire}>
                            {questionnaire.name}
                        </Text>
                    </View>
                    <View style={{width: width * .25}}>
                        <Text style={styles.percQuestionnaire}>
                            {questionnaire.text}
                        </Text>
                    </View>
                </View>
            </View>
        );
    }

}
<commit_msg>Resized icon in questionnaire progress bar.<commit_after>import React, {Component, PropTypes} from "react";
import {Dimensions, StyleSheet, TouchableOpacity, View} from "react-native";

import * as colors from "../lib/colors";
import CircleProgress from "../components/circle-progress";
import Icon from "../components/iwapp-icons";
import Text from "../components/text-lato";

const styles = StyleSheet.create({
    progressQuestionnaire: {
        flexDirection: "column",
        alignItems: "stretch"
    },
    iconQuestionnaireWrp: {
        position: "absolute",
        left: 0,
        right: 0,
        top: 0,
        bottom: 0,
        alignItems: "center",
        justifyContent: "center",
        backgroundColor: colors.transparent
    },
    titQuestionnaire: {
        fontSize: 11,
        textAlign: "center",
        color: colors.textGrey
    },
    percQuestionnaire: {
        fontSize: 9,
        textAlign: "center",
        color: colors.textGrey
    }
});

export default class QuestionnaireProgress extends Component {

    static propTypes = {
        questionnaire: PropTypes.shape({
            key: PropTypes.string,
            color: PropTypes.string,
            value: PropTypes.number,
            icon: PropTypes.string,
            name: PropTypes.string,
            onPress: PropTypes.func
        }).isRequired
    }

    render () {
        const {questionnaire} = this.props;
        const {width, height} = Dimensions.get("window");
        return (
            <View style={[styles.progressQuestionnaire, {
                height: width * .4,
                marginHorizontal: width * .02,
                marginVertical: height * .015
            }]}
            >
                <CircleProgress
                    percentage={questionnaire.value}
                    size={height * .13}
                    strokeColor={questionnaire.color}
                    strokeWidth={2}
                />
                <TouchableOpacity
                    onPress={questionnaire.onPress}
                    style={[styles.iconQuestionnaireWrp, {width: width * .25, height: height * .125}]}
                    transparent={true}
                >
                    <Icon
                        color={questionnaire.color}
                        name={questionnaire.icon}
                        size={width * .14}
                        style={{alignItems: "center"}}
                    />
                </TouchableOpacity>
                <View>
                    <View style={{width: width * .25}}>
                        <Text style={styles.titQuestionnaire}>
                            {questionnaire.name}
                        </Text>
                    </View>
                    <View style={{width: width * .25}}>
                        <Text style={styles.percQuestionnaire}>
                            {questionnaire.text}
                        </Text>
                    </View>
                </View>
            </View>
        );
    }

}
<|endoftext|>


<commit_before>'use strict';

angular.module('containerDetails', ['ngRoute'])

    .config(['$routeProvider', function ($routeProvider) {
        $routeProvider.when('/containerDetails/:containerId', {
            templateUrl: 'app/containerDetails/containerDetails.html',
            controller: 'ContainerDetailsCtrl'
        });
    }])

    .controller('ContainerDetailsCtrl', ['$rootScope', '$scope', '$location', '$routeParams', 'Helpers', 'Docker', function ($rootScope, $scope, $location, $routeParams, Helpers, Docker) {
        if (Helpers.isEmpty($rootScope.hostUrl)) {
            $location.path('/hosts');
        } else {
            $scope.activeTab = 'top';
            $scope.newDiffPageSize = 20;
            //
            var diffSettings = [];
            diffSettings["pageIndex"] = 1;
            diffSettings["maxPageIndex"] = 0;
            diffSettings["startIndex"] = 0;
            diffSettings["endIndex"] = 0;
            diffSettings["pageSize"] = $scope.newDiffPageSize;
            $scope.diffSettings = diffSettings;
            //
            var logSettings = [];
            logSettings["showStdOut"] = true;
            logSettings["stdOutTail"] = 1000;
            logSettings["stdOutTailAll"] = false;
            logSettings["stdOutTimestamp"] = true;
            logSettings["showStdErr"] = true;
            logSettings["stdErrTail"] = 1000;
            logSettings["stdErrTailAll"] = false;
            logSettings["stdErrTimestamp"] = true;
            $scope.logSettings = logSettings;
            // dirt hack to emulate destroy function :)
            var term = {};

            $scope.containerDetailsLoading = true;
            $scope.containerDetailsLoadingMessage = 'Loading container data';
            $scope.containerTopLoading = false;
            $scope.containerLogsLoading = false;
            $scope.containerDiffLoading = false;

            var containerDetails = Docker.containers().get({containerId: $routeParams.containerId}, function () {
                $scope.containerDetailsLoading = false;
                $scope.containerDetails = containerDetails;
                $scope.showProcesses($routeParams.containerId);

                // TODO check output format with multiple port situation
                var portAssignments = "";
                for (var key in containerDetails.NetworkSettings.Ports) {
                    portAssignments = portAssignments + key + ':';
                    var portArray = containerDetails.NetworkSettings.Ports[key];
                    portArray.forEach(function (port) {
                        portAssignments += port["HostPort"];
                    });
                    portAssignments += ', ';
                }
                $scope.portAssignments = portAssignments.substring(0, portAssignments.length - 2);
            });
            //

            $scope.showLogs = function (containerId) {
                $scope.containerLogsLoading = true;
                $scope.containerLogsLoadingMessage = 'Loading log information';
                if (!Helpers.isEmpty(term)) {
                    term.destroy();
                }
                $scope.activeTab = 'logs';
                var logParams = {};
                logParams.stderr = 0;
                logParams.stdout = 1;
                logParams.timestamps = logSettings["stdOutTimestamp"];
                logParams.tail = logSettings["stdOutTailAll"] == true ? true : logSettings["stdOutTail"];

                Docker.containerLogs(containerId, logParams, function (data, status, headers, config) {
                    $scope.stdOutLog = data;
                    logParams.stderr = 1;
                    logParams.stdout = 0;
                    logParams.timestamps = logSettings["stdErrTimestamp"];
                    logParams.tail = logSettings["stdErrTailAll"] == true ? true : logSettings["stdErrTail"];
                    Docker.containerLogs(containerId, logParams, function (data, status, headers, config) {
                        $scope.containerLogsLoading = false;
                        data = data.substring(8);
                        $scope.stdErrLog = data;
                    });
                });

                $scope.switchStdOutPanel = function () {
                    logSettings["showStdOut"] = !logSettings["showStdOut"];
                };
                $scope.switchStdErrPanel = function () {
                    logSettings["showStdErr"] = !logSettings["showStdErr"];
                };

                $scope.refreshStdOutLogs = function () {
                    logParams.stderr = 0;
                    logParams.stdout = 1;
                    logParams.timestamps = logSettings["stdOutTimestamp"];
                    logParams.tail = logSettings["stdOutTailAll"] == true ? true : logSettings["stdOutTail"];
                    Docker.containerLogs(containerId, logParams, function (data, status, headers, config) {
                        $scope.stdOutLog = data;
                    });
                };

                $scope.switchShowStdOutTimestamp = function () {
                    logSettings["stdOutTimestamp"] = !logSettings["stdOutTimestamp"];
                    $scope.refreshStdOutLogs();
                };

                $scope.switchStdOutTailAll = function () {
                    logSettings["stdOutTailAll"] = !logSettings["stdOutTailAll"];
                    $scope.refreshStdOutLogs();
                };

                $scope.refreshStdErrLogs = function () {
                    logParams.stderr = 1;
                    logParams.stdout = 0;
                    logParams.timestamps = logSettings["stdErrTimestamp"];
                    logParams.tail = logSettings["stdErrTailAll"] == true ? true : logSettings["stdErrTail"];
                    Docker.containerLogs(containerId, logParams, function (data, status, headers, config) {
                        $scope.stdErrLog = data;
                    });
                };

                $scope.switchShowStdErrTimestamp = function () {
                    logSettings["stdErrTimestamp"] = !logSettings["stdErrTimestamp"];
                    $scope.refreshStdErrLogs();
                };

                $scope.switchStdErrTailAll = function () {
                    logSettings["stdErrTailAll"] = !logSettings["stdErrTailAll"];
                    $scope.refreshStdErrLogs();
                };
            };

            $scope.showProcesses = function (containerId) {
                $scope.containerTopLoading = true;
                $scope.containerTopLoadingMessage = 'Loading process information';
                if (!Helpers.isEmpty(term)) {
                    term.destroy();
                }
                //
                $scope.activeTab = 'top';
                if (containerDetails.State.Running) {
                    var containerProcesses = Docker.containers().top({containerId: containerId}, function () {
                        $scope.containerTopLoading = false;
                        $scope.containerProcesses = containerProcesses;
                    });
                }
            };

            $scope.showDiff = function (containerId) {
                if (!Helpers.isEmpty(term)) {
                    term.destroy();
                }
                //
                var containerDiffs = {};
                $scope.containerDiffLoading = true;
                $scope.containerDiffLoadingMessage = 'Loading container diff information. Be patient! It could take some time.';
                //
                var refreshDiffs = function () {
                    containerDiffs = Docker.containers().diff({containerId: containerId}, function () {
                        $scope.containerDiffLoading = false;
                        updateDiffPagedList();
                    });
                };
                //
                $scope.activeTab = 'diff';
                refreshDiffs();
                //
                var updateDiffPagedList = function () {
                    var diffPageSize = $scope.newDiffPageSize;
                    diffSettings["pageSize"] = diffPageSize;
                    diffSettings["maxPageIndex"] = Math.trunc(containerDiffs.length / diffPageSize) + 1;
                    diffSettings["pageIndex"] = Math.min(diffSettings["maxPageIndex"], diffSettings["pageIndex"]);
                    diffSettings["count"] = containerDiffs.length;
                    var subDiffs = [];
                    var startIndex = containerDiffs.length > 0 ? (diffSettings["pageIndex"] - 1) * diffPageSize + 1 : 0;
                    var endIndex = Math.min((diffSettings["pageIndex"]) * diffPageSize, containerDiffs.length);

                    diffSettings["startIndex"] = startIndex;
                    diffSettings["endIndex"] = endIndex;

                    for (var i = startIndex - 1; i < endIndex; i++) {
                        subDiffs.push(containerDiffs[i]);
                    }
                    $scope.containerDiffs = subDiffs;
                };

                $scope.refreshDiffs = function () {
                    refreshDiffs();
                };

                $scope.changeDiffPaging = function () {
                    if ($scope.newDiffPageSize != diffSettings["pageSize"]) {
                        updateDiffPagedList();
                    }
                };
                $scope.firstDiffPage = function () {
                    diffSettings["pageIndex"] = 1;
                    updateDiffPagedList();
                };
                $scope.prevDiffPage = function () {
                    if (diffSettings["pageIndex"] > 1) {
                        diffSettings["pageIndex"]--;
                        updateDiffPagedList();
                    }
                };
                $scope.nextDiffPage = function () {
                    if (diffSettings["pageIndex"] < diffSettings["maxPageIndex"]) {
                        diffSettings["pageIndex"]++;
                        updateDiffPagedList();
                    }
                };
                $scope.lastDiffPage = function () {
                    diffSettings["pageIndex"] = diffSettings["maxPageIndex"];
                    updateDiffPagedList();
                }
            };

            $scope.showTty = function (containerId) {
                $scope.activeTab = 'tty';
                var termContainer = document.getElementById('terminal');
                term = new Terminal();
                var url = $rootScope.hostUrl.replace('http:', 'ws:').replace('https:', 'wss:') + '/containers/' + containerId + '/attach/ws?logs=0&stream=1&stdout=1&stderr=1&stdin=1';
                var socket = new WebSocket(url);
                term.open(termContainer);
                term.resize(80, 25);
                term.on('data', function (data) {
                    socket.send(data);
                });
                socket.onmessage = function (e) {
                    term.write(e.data);
                }
            };

            $scope.showJson = function (containerId) {
                if (!Helpers.isEmpty(term)) {
                    term.destroy();
                }
                //
                $scope.activeTab = 'json';
            };

            $scope.$on("$destroy", function () {
                if (!Helpers.isEmpty(term)) {
                    term.destroy();
                }
            });
        }
    }])
;<commit_msg>TODO portmap<commit_after>'use strict';

angular.module('containerDetails', ['ngRoute'])

    .config(['$routeProvider', function ($routeProvider) {
        $routeProvider.when('/containerDetails/:containerId', {
            templateUrl: 'app/containerDetails/containerDetails.html',
            controller: 'ContainerDetailsCtrl'
        });
    }])

    .controller('ContainerDetailsCtrl', ['$rootScope', '$scope', '$location', '$routeParams', 'Helpers', 'Docker', function ($rootScope, $scope, $location, $routeParams, Helpers, Docker) {
        if (Helpers.isEmpty($rootScope.hostUrl)) {
            $location.path('/hosts');
        } else {
            $scope.activeTab = 'top';
            $scope.newDiffPageSize = 20;
            //
            var diffSettings = [];
            diffSettings["pageIndex"] = 1;
            diffSettings["maxPageIndex"] = 0;
            diffSettings["startIndex"] = 0;
            diffSettings["endIndex"] = 0;
            diffSettings["pageSize"] = $scope.newDiffPageSize;
            $scope.diffSettings = diffSettings;
            //
            var logSettings = [];
            logSettings["showStdOut"] = true;
            logSettings["stdOutTail"] = 1000;
            logSettings["stdOutTailAll"] = false;
            logSettings["stdOutTimestamp"] = true;
            logSettings["showStdErr"] = true;
            logSettings["stdErrTail"] = 1000;
            logSettings["stdErrTailAll"] = false;
            logSettings["stdErrTimestamp"] = true;
            $scope.logSettings = logSettings;
            // dirt hack to emulate destroy function :)
            var term = {};

            $scope.containerDetailsLoading = true;
            $scope.containerDetailsLoadingMessage = 'Loading container data';
            $scope.containerTopLoading = false;
            $scope.containerLogsLoading = false;
            $scope.containerDiffLoading = false;

            var containerDetails = Docker.containers().get({containerId: $routeParams.containerId}, function () {
                $scope.containerDetailsLoading = false;
                $scope.containerDetails = containerDetails;
                $scope.showProcesses($routeParams.containerId);

                // TODO check output format with multiple port situation
                var portAssignments = "";
                for (var key in containerDetails.NetworkSettings.Ports) {
                    portAssignments = portAssignments + key + ':';
                    var portArray = containerDetails.NetworkSettings.PortMapping.Ports[key];
                    portArray.forEach(function (port) {
                        portAssignments += port["HostPort"];
                    });
                    portAssignments += ', ';
                }
                $scope.portAssignments = portAssignments.substring(0, portAssignments.length - 2);
            });
            //

            $scope.showLogs = function (containerId) {
                $scope.containerLogsLoading = true;
                $scope.containerLogsLoadingMessage = 'Loading log information';
                if (!Helpers.isEmpty(term)) {
                    term.destroy();
                }
                $scope.activeTab = 'logs';
                var logParams = {};
                logParams.stderr = 0;
                logParams.stdout = 1;
                logParams.timestamps = logSettings["stdOutTimestamp"];
                logParams.tail = logSettings["stdOutTailAll"] == true ? true : logSettings["stdOutTail"];

                Docker.containerLogs(containerId, logParams, function (data, status, headers, config) {
                    $scope.stdOutLog = data;
                    logParams.stderr = 1;
                    logParams.stdout = 0;
                    logParams.timestamps = logSettings["stdErrTimestamp"];
                    logParams.tail = logSettings["stdErrTailAll"] == true ? true : logSettings["stdErrTail"];
                    Docker.containerLogs(containerId, logParams, function (data, status, headers, config) {
                        $scope.containerLogsLoading = false;
                        data = data.substring(8);
                        $scope.stdErrLog = data;
                    });
                });

                $scope.switchStdOutPanel = function () {
                    logSettings["showStdOut"] = !logSettings["showStdOut"];
                };
                $scope.switchStdErrPanel = function () {
                    logSettings["showStdErr"] = !logSettings["showStdErr"];
                };

                $scope.refreshStdOutLogs = function () {
                    logParams.stderr = 0;
                    logParams.stdout = 1;
                    logParams.timestamps = logSettings["stdOutTimestamp"];
                    logParams.tail = logSettings["stdOutTailAll"] == true ? true : logSettings["stdOutTail"];
                    Docker.containerLogs(containerId, logParams, function (data, status, headers, config) {
                        $scope.stdOutLog = data;
                    });
                };

                $scope.switchShowStdOutTimestamp = function () {
                    logSettings["stdOutTimestamp"] = !logSettings["stdOutTimestamp"];
                    $scope.refreshStdOutLogs();
                };

                $scope.switchStdOutTailAll = function () {
                    logSettings["stdOutTailAll"] = !logSettings["stdOutTailAll"];
                    $scope.refreshStdOutLogs();
                };

                $scope.refreshStdErrLogs = function () {
                    logParams.stderr = 1;
                    logParams.stdout = 0;
                    logParams.timestamps = logSettings["stdErrTimestamp"];
                    logParams.tail = logSettings["stdErrTailAll"] == true ? true : logSettings["stdErrTail"];
                    Docker.containerLogs(containerId, logParams, function (data, status, headers, config) {
                        $scope.stdErrLog = data;
                    });
                };

                $scope.switchShowStdErrTimestamp = function () {
                    logSettings["stdErrTimestamp"] = !logSettings["stdErrTimestamp"];
                    $scope.refreshStdErrLogs();
                };

                $scope.switchStdErrTailAll = function () {
                    logSettings["stdErrTailAll"] = !logSettings["stdErrTailAll"];
                    $scope.refreshStdErrLogs();
                };
            };

            $scope.showProcesses = function (containerId) {
                $scope.containerTopLoading = true;
                $scope.containerTopLoadingMessage = 'Loading process information';
                if (!Helpers.isEmpty(term)) {
                    term.destroy();
                }
                //
                $scope.activeTab = 'top';
                if (containerDetails.State.Running) {
                    var containerProcesses = Docker.containers().top({containerId: containerId}, function () {
                        $scope.containerTopLoading = false;
                        $scope.containerProcesses = containerProcesses;
                    });
                }
            };

            $scope.showDiff = function (containerId) {
                if (!Helpers.isEmpty(term)) {
                    term.destroy();
                }
                //
                var containerDiffs = {};
                $scope.containerDiffLoading = true;
                $scope.containerDiffLoadingMessage = 'Loading container diff information. Be patient! It could take some time.';
                //
                var refreshDiffs = function () {
                    containerDiffs = Docker.containers().diff({containerId: containerId}, function () {
                        $scope.containerDiffLoading = false;
                        updateDiffPagedList();
                    });
                };
                //
                $scope.activeTab = 'diff';
                refreshDiffs();
                //
                var updateDiffPagedList = function () {
                    var diffPageSize = $scope.newDiffPageSize;
                    diffSettings["pageSize"] = diffPageSize;
                    diffSettings["maxPageIndex"] = Math.trunc(containerDiffs.length / diffPageSize) + 1;
                    diffSettings["pageIndex"] = Math.min(diffSettings["maxPageIndex"], diffSettings["pageIndex"]);
                    diffSettings["count"] = containerDiffs.length;
                    var subDiffs = [];
                    var startIndex = containerDiffs.length > 0 ? (diffSettings["pageIndex"] - 1) * diffPageSize + 1 : 0;
                    var endIndex = Math.min((diffSettings["pageIndex"]) * diffPageSize, containerDiffs.length);

                    diffSettings["startIndex"] = startIndex;
                    diffSettings["endIndex"] = endIndex;

                    for (var i = startIndex - 1; i < endIndex; i++) {
                        subDiffs.push(containerDiffs[i]);
                    }
                    $scope.containerDiffs = subDiffs;
                };

                $scope.refreshDiffs = function () {
                    refreshDiffs();
                };

                $scope.changeDiffPaging = function () {
                    if ($scope.newDiffPageSize != diffSettings["pageSize"]) {
                        updateDiffPagedList();
                    }
                };
                $scope.firstDiffPage = function () {
                    diffSettings["pageIndex"] = 1;
                    updateDiffPagedList();
                };
                $scope.prevDiffPage = function () {
                    if (diffSettings["pageIndex"] > 1) {
                        diffSettings["pageIndex"]--;
                        updateDiffPagedList();
                    }
                };
                $scope.nextDiffPage = function () {
                    if (diffSettings["pageIndex"] < diffSettings["maxPageIndex"]) {
                        diffSettings["pageIndex"]++;
                        updateDiffPagedList();
                    }
                };
                $scope.lastDiffPage = function () {
                    diffSettings["pageIndex"] = diffSettings["maxPageIndex"];
                    updateDiffPagedList();
                }
            };

            $scope.showTty = function (containerId) {
                $scope.activeTab = 'tty';
                var termContainer = document.getElementById('terminal');
                term = new Terminal();
                var url = $rootScope.hostUrl.replace('http:', 'ws:').replace('https:', 'wss:') + '/containers/' + containerId + '/attach/ws?logs=0&stream=1&stdout=1&stderr=1&stdin=1';
                var socket = new WebSocket(url);
                term.open(termContainer);
                term.resize(80, 25);
                term.on('data', function (data) {
                    socket.send(data);
                });
                socket.onmessage = function (e) {
                    term.write(e.data);
                }
            };

            $scope.showJson = function (containerId) {
                if (!Helpers.isEmpty(term)) {
                    term.destroy();
                }
                //
                $scope.activeTab = 'json';
            };

            $scope.$on("$destroy", function () {
                if (!Helpers.isEmpty(term)) {
                    term.destroy();
                }
            });
        }
    }])
;<|endoftext|>


<commit_before>const TextBasedChannel = require('./interfaces/TextBasedChannel');
const Role = require('./Role');
const Permissions = require('../util/Permissions');
const Collection = require('../util/Collection');
const Presence = require('./Presence').Presence;
const util = require('util');

/**
 * Represents a member of a guild on Discord.
 * @implements {TextBasedChannel}
 */
class GuildMember {
  constructor(guild, data) {
    /**
     * The client that instantiated this GuildMember
     * @name GuildMember#client
     * @type {Client}
     * @readonly
     */
    Object.defineProperty(this, 'client', { value: guild.client });

    /**
     * The guild that this member is part of
     * @type {Guild}
     */
    this.guild = guild;

    /**
     * The user that this guild member instance Represents
     * @type {User}
     */
    this.user = {};

    this._roles = [];
    if (data) this.setup(data);

    /**
     * The ID of the last message sent by the member in their guild, if one was sent
     * @type {?Snowflake}
     */
    this.lastMessageID = null;

    /**
     * The Message object of the last message sent by the member in their guild, if one was sent
     * @type {?Message}
     */
    this.lastMessage = null;
  }

  setup(data) {
    /**
     * Whether this member is deafened server-wide
     * @type {boolean}
     */
    this.serverDeaf = data.deaf;

    /**
     * Whether this member is muted server-wide
     * @type {boolean}
     */
    this.serverMute = data.mute;

    /**
     * Whether this member is self-muted
     * @type {boolean}
     */
    this.selfMute = data.self_mute;

    /**
     * Whether this member is self-deafened
     * @type {boolean}
     */
    this.selfDeaf = data.self_deaf;

    /**
     * The voice session ID of this member, if any
     * @type {?Snowflake}
     */
    this.voiceSessionID = data.session_id;

    /**
     * The voice channel ID of this member, if any
     * @type {?Snowflake}
     */
    this.voiceChannelID = data.channel_id;

    /**
     * Whether this member is speaking
     * @type {boolean}
     */
    this.speaking = false;

    /**
     * The nickname of this guild member, if they have one
     * @type {?string}
     */
    this.nickname = data.nick || null;

    /**
     * The timestamp the member joined the guild at
     * @type {number}
     */
    this.joinedTimestamp = new Date(data.joined_at).getTime();

    this.user = data.user;
    this._roles = data.roles;
  }

  /**
   * The time the member joined the guild
   * @type {Date}
   * @readonly
   */
  get joinedAt() {
    return new Date(this.joinedTimestamp);
  }

  /**
   * The presence of this guild member
   * @type {Presence}
   * @readonly
   */
  get presence() {
    return this.frozenPresence || this.guild.presences.get(this.id) || new Presence();
  }

  /**
   * A list of roles that are applied to this GuildMember, mapped by the role ID
   * @type {Collection<Snowflake, Role>}
   * @readonly
   */
  get roles() {
    const list = new Collection();
    const everyoneRole = this.guild.roles.get(this.guild.id);

    if (everyoneRole) list.set(everyoneRole.id, everyoneRole);

    for (const roleID of this._roles) {
      const role = this.guild.roles.get(roleID);
      if (role) list.set(role.id, role);
    }

    return list;
  }

  /**
   * The role of the member with the highest position
   * @type {Role}
   * @readonly
   */
  get highestRole() {
    return this.roles.reduce((prev, role) => !prev || role.comparePositionTo(prev) > 0 ? role : prev);
  }

  /**
   * The role of the member used to set their color
   * @type {?Role}
   * @readonly
   */
  get colorRole() {
    const coloredRoles = this.roles.filter(role => role.color);
    if (!coloredRoles.size) return null;
    return coloredRoles.reduce((prev, role) => !prev || role.comparePositionTo(prev) > 0 ? role : prev);
  }

  /**
   * The displayed color of the member in base 10
   * @type {number}
   * @readonly
   */
  get displayColor() {
    const role = this.colorRole;
    return (role && role.color) || 0;
  }

  /**
   * The displayed color of the member in hexadecimal
   * @type {string}
   * @readonly
   */
  get displayHexColor() {
    const role = this.colorRole;
    return (role && role.hexColor) || '#000000';
  }

  /**
   * The role of the member used to hoist them in a separate category in the users list
   * @type {?Role}
   * @readonly
   */
  get hoistRole() {
    const hoistedRoles = this.roles.filter(role => role.hoist);
    if (!hoistedRoles.size) return null;
    return hoistedRoles.reduce((prev, role) => !prev || role.comparePositionTo(prev) > 0 ? role : prev);
  }

  /**
   * Whether this member is muted in any way
   * @type {boolean}
   * @readonly
   */
  get mute() {
    return this.selfMute || this.serverMute;
  }

  /**
   * Whether this member is deafened in any way
   * @type {boolean}
   * @readonly
   */
  get deaf() {
    return this.selfDeaf || this.serverDeaf;
  }

  /**
   * The voice channel this member is in, if any
   * @type {?VoiceChannel}
   * @readonly
   */
  get voiceChannel() {
    return this.guild.channels.get(this.voiceChannelID);
  }

  /**
   * The ID of this user
   * @type {Snowflake}
   * @readonly
   */
  get id() {
    return this.user.id;
  }

  /**
   * The nickname of the member, or their username if they don't have one
   * @type {string}
   * @readonly
   */
  get displayName() {
    return this.nickname || this.user.username;
  }

  /**
   * The overall set of permissions for the guild member, taking only roles into account
   * @type {Permissions}
   * @readonly
   */
  get permissions() {
    if (this.user.id === this.guild.ownerID) return new Permissions(this, Permissions.ALL);

    let permissions = 0;
    const roles = this.roles;
    for (const role of roles.values()) permissions |= role.permissions;

    return new Permissions(this, permissions);
  }

  /**
   * Whether the member is kickable by the client user
   * @type {boolean}
   * @readonly
   */
  get kickable() {
    if (this.user.id === this.guild.ownerID) return false;
    if (this.user.id === this.client.user.id) return false;
    const clientMember = this.guild.member(this.client.user);
    if (!clientMember.hasPermission(Permissions.FLAGS.KICK_MEMBERS)) return false;
    return clientMember.highestRole.comparePositionTo(this.highestRole) > 0;
  }

  /**
   * Whether the member is bannable by the client user
   * @type {boolean}
   * @readonly
   */
  get bannable() {
    if (this.user.id === this.guild.ownerID) return false;
    if (this.user.id === this.client.user.id) return false;
    const clientMember = this.guild.member(this.client.user);
    if (!clientMember.hasPermission(Permissions.FLAGS.BAN_MEMBERS)) return false;
    return clientMember.highestRole.comparePositionTo(this.highestRole) > 0;
  }

  /**
   * Returns `channel.permissionsFor(guildMember)`. Returns permissions for a member in a guild channel,
   * taking into account roles and permission overwrites.
   * @param {ChannelResolvable} channel The guild channel to use as context
   * @returns {?Permissions}
   */
  permissionsIn(channel) {
    channel = this.client.resolver.resolveChannel(channel);
    if (!channel || !channel.guild) throw new Error('Could not resolve channel to a guild channel.');
    return channel.permissionsFor(this);
  }

  /**
   * Checks if any of the member's roles have a permission.
   * @param {PermissionResolvable|PermissionResolvable[]} permission Permission(s) to check for
   * @param {boolean} [explicit=false] Whether to require the role to explicitly have the exact permission
   * **(deprecated)**
   * @param {boolean} [checkAdmin] Whether to allow the administrator permission to override
   * (takes priority over `explicit`)
   * @param {boolean} [checkOwner] Whether to allow being the guild's owner to override
   * (takes priority over `explicit`)
   * @returns {boolean}
   */
  hasPermission(permission, explicit = false, checkAdmin, checkOwner) {
    if (typeof checkAdmin === 'undefined') checkAdmin = !explicit;
    if (typeof checkOwner === 'undefined') checkOwner = !explicit;
    if (checkOwner && this.user.id === this.guild.ownerID) return true;
    return this.roles.some(r => r.hasPermission(permission, undefined, checkAdmin));
  }

  /**
   * Checks whether the roles of the member allows them to perform specific actions.
   * @param {PermissionResolvable[]} permissions The permissions to check for
   * @param {boolean} [explicit=false] Whether to require the member to explicitly have the exact permissions
   * @returns {boolean}
   * @deprecated
   */
  hasPermissions(permissions, explicit = false) {
    if (!explicit && this.user.id === this.guild.ownerID) return true;
    return permissions.every(p => this.hasPermission(p, explicit));
  }

  /**
   * Checks whether the roles of the member allows them to perform specific actions, and lists any missing permissions.
   * @param {PermissionResolvable[]} permissions The permissions to check for
   * @param {boolean} [explicit=false] Whether to require the member to explicitly have the exact permissions
   * @returns {PermissionResolvable[]}
   */
  missingPermissions(permissions, explicit = false) {
    return permissions.filter(p => !this.hasPermission(p, explicit));
  }

  /**
   * The data for editing a guild member.
   * @typedef {Object} GuildMemberEditData
   * @property {string} [nick] The nickname to set for the member
   * @property {Collection<Snowflake, Role>|Role[]|Snowflake[]} [roles] The roles or role IDs to apply
   * @property {boolean} [mute] Whether or not the member should be muted
   * @property {boolean} [deaf] Whether or not the member should be deafened
   * @property {ChannelResolvable} [channel] Channel to move member to (if they are connected to voice)
   */

  /**
   * Edit a guild member.
   * @param {GuildMemberEditData} data The data to edit the member with
   * @returns {Promise<GuildMember>}
   */
  edit(data) {
    return this.client.rest.methods.updateGuildMember(this, data);
  }

  /**
   * Mute/unmute a user.
   * @param {boolean} mute Whether or not the member should be muted
   * @returns {Promise<GuildMember>}
   */
  setMute(mute) {
    return this.edit({ mute });
  }

  /**
   * Deafen/undeafen a user.
   * @param {boolean} deaf Whether or not the member should be deafened
   * @returns {Promise<GuildMember>}
   */
  setDeaf(deaf) {
    return this.edit({ deaf });
  }

  /**
   * Moves the guild member to the given channel.
   * @param {ChannelResolvable} channel The channel to move the member to
   * @returns {Promise<GuildMember>}
   */
  setVoiceChannel(channel) {
    return this.edit({ channel });
  }

  /**
   * Sets the roles applied to the member.
   * @param {Collection<Snowflake, Role>|Role[]|Snowflake[]} roles The roles or role IDs to apply
   * @returns {Promise<GuildMember>}
   */
  setRoles(roles) {
    return this.edit({ roles });
  }

  /**
   * Adds a single role to the member.
   * @param {Role|Snowflake} role The role or ID of the role to add
   * @returns {Promise<GuildMember>}
   */
  addRole(role) {
    if (!(role instanceof Role)) role = this.guild.roles.get(role);
    if (!role) throw new TypeError('Supplied parameter was neither a Role nor a Snowflake.');
    return this.client.rest.methods.addMemberRole(this, role);
  }

  /**
   * Adds multiple roles to the member.
   * @param {Collection<Snowflake, Role>|Role[]|Snowflake[]} roles The roles or role IDs to add
   * @returns {Promise<GuildMember>}
   */
  addRoles(roles) {
    let allRoles;
    if (roles instanceof Collection) {
      allRoles = this._roles.slice();
      for (const role of roles.values()) allRoles.push(role.id);
    } else {
      allRoles = this._roles.concat(roles);
    }
    return this.edit({ roles: allRoles });
  }

  /**
   * Removes a single role from the member.
   * @param {Role|Snowflake} role The role or ID of the role to remove
   * @returns {Promise<GuildMember>}
   */
  removeRole(role) {
    if (!(role instanceof Role)) role = this.guild.roles.get(role);
    if (!role) throw new TypeError('Supplied parameter was neither a Role nor a Snowflake.');
    return this.client.rest.methods.removeMemberRole(this, role);
  }

  /**
   * Removes multiple roles from the member.
   * @param {Collection<Snowflake, Role>|Role[]|Snowflake[]} roles The roles or role IDs to remove
   * @returns {Promise<GuildMember>}
   */
  removeRoles(roles) {
    const allRoles = this._roles.slice();
    if (roles instanceof Collection) {
      for (const role of roles.values()) {
        const index = allRoles.indexOf(role.id);
        if (index >= 0) allRoles.splice(index, 1);
      }
    } else {
      for (const role of roles) {
        const index = allRoles.indexOf(role instanceof Role ? role.id : role);
        if (index >= 0) allRoles.splice(index, 1);
      }
    }
    return this.edit({ roles: allRoles });
  }

  /**
   * Set the nickname for the guild member.
   * @param {string} nick The nickname for the guild member
   * @returns {Promise<GuildMember>}
   */
  setNickname(nick) {
    return this.edit({ nick });
  }

  /**
   * Creates a DM channel between the client and the member.
   * @returns {Promise<DMChannel>}
   */
  createDM() {
    return this.user.createDM();
  }

  /**
   * Deletes any DMs with this guild member.
   * @returns {Promise<DMChannel>}
   */
  deleteDM() {
    return this.user.deleteDM();
  }

  /**
   * Kick this member from the guild.
   * @param {string} [reason] Reason for kicking user
   * @returns {Promise<GuildMember>}
   */
  kick(reason) {
    return this.client.rest.methods.kickGuildMember(this.guild, this, reason);
  }

  /**
   * Ban this guild member
   * @param {Object} [options] Ban options.
   * @param {number} [options.days=0] Number of days of messages to delete
   * @param {string} [options.reason] Reason for banning
   * @returns {Promise<GuildMember>}
   * @example
   * // ban a guild member
   * guildMember.ban(7);
   */
  ban(options) {
    return this.guild.ban(this, options);
  }

  /**
   * When concatenated with a string, this automatically concatenates the user's mention instead of the Member object.
   * @returns {string}
   * @example
   * // Logs: Hello from <@123456789>!
   * console.log(`Hello from ${member}!`);
   */
  toString() {
    return `<@${this.nickname ? '!' : ''}${this.user.id}>`;
  }

  // These are here only for documentation purposes - they are implemented by TextBasedChannel
  /* eslint-disable no-empty-function */
  send() {}
  sendMessage() {}
  sendEmbed() {}
  sendFile() {}
  sendCode() {}
}

TextBasedChannel.applyToClass(GuildMember);

GuildMember.prototype.hasPermissions = util.deprecate(GuildMember.prototype.hasPermissions,
  'GuildMember#hasPermissions is deprecated - use GuildMember#hasPermission, it now takes an array');

module.exports = GuildMember;
<commit_msg>Force deaf/mute properties to be booleans even when null data is received<commit_after>const TextBasedChannel = require('./interfaces/TextBasedChannel');
const Role = require('./Role');
const Permissions = require('../util/Permissions');
const Collection = require('../util/Collection');
const Presence = require('./Presence').Presence;
const util = require('util');

/**
 * Represents a member of a guild on Discord.
 * @implements {TextBasedChannel}
 */
class GuildMember {
  constructor(guild, data) {
    /**
     * The client that instantiated this GuildMember
     * @name GuildMember#client
     * @type {Client}
     * @readonly
     */
    Object.defineProperty(this, 'client', { value: guild.client });

    /**
     * The guild that this member is part of
     * @type {Guild}
     */
    this.guild = guild;

    /**
     * The user that this guild member instance Represents
     * @type {User}
     */
    this.user = {};

    this._roles = [];
    if (data) this.setup(data);

    /**
     * The ID of the last message sent by the member in their guild, if one was sent
     * @type {?Snowflake}
     */
    this.lastMessageID = null;

    /**
     * The Message object of the last message sent by the member in their guild, if one was sent
     * @type {?Message}
     */
    this.lastMessage = null;
  }

  setup(data) {
    /**
     * Whether this member is deafened server-wide
     * @type {boolean}
     */
    this.serverDeaf = Boolean(data.deaf);

    /**
     * Whether this member is muted server-wide
     * @type {boolean}
     */
    this.serverMute = Boolean(data.mute);

    /**
     * Whether this member is self-muted
     * @type {boolean}
     */
    this.selfMute = Boolean(data.self_mute);

    /**
     * Whether this member is self-deafened
     * @type {boolean}
     */
    this.selfDeaf = Boolean(data.self_deaf);

    /**
     * The voice session ID of this member, if any
     * @type {?Snowflake}
     */
    this.voiceSessionID = data.session_id;

    /**
     * The voice channel ID of this member, if any
     * @type {?Snowflake}
     */
    this.voiceChannelID = data.channel_id;

    /**
     * Whether this member is speaking
     * @type {boolean}
     */
    this.speaking = false;

    /**
     * The nickname of this guild member, if they have one
     * @type {?string}
     */
    this.nickname = data.nick || null;

    /**
     * The timestamp the member joined the guild at
     * @type {number}
     */
    this.joinedTimestamp = new Date(data.joined_at).getTime();

    this.user = data.user;
    this._roles = data.roles;
  }

  /**
   * The time the member joined the guild
   * @type {Date}
   * @readonly
   */
  get joinedAt() {
    return new Date(this.joinedTimestamp);
  }

  /**
   * The presence of this guild member
   * @type {Presence}
   * @readonly
   */
  get presence() {
    return this.frozenPresence || this.guild.presences.get(this.id) || new Presence();
  }

  /**
   * A list of roles that are applied to this GuildMember, mapped by the role ID
   * @type {Collection<Snowflake, Role>}
   * @readonly
   */
  get roles() {
    const list = new Collection();
    const everyoneRole = this.guild.roles.get(this.guild.id);

    if (everyoneRole) list.set(everyoneRole.id, everyoneRole);

    for (const roleID of this._roles) {
      const role = this.guild.roles.get(roleID);
      if (role) list.set(role.id, role);
    }

    return list;
  }

  /**
   * The role of the member with the highest position
   * @type {Role}
   * @readonly
   */
  get highestRole() {
    return this.roles.reduce((prev, role) => !prev || role.comparePositionTo(prev) > 0 ? role : prev);
  }

  /**
   * The role of the member used to set their color
   * @type {?Role}
   * @readonly
   */
  get colorRole() {
    const coloredRoles = this.roles.filter(role => role.color);
    if (!coloredRoles.size) return null;
    return coloredRoles.reduce((prev, role) => !prev || role.comparePositionTo(prev) > 0 ? role : prev);
  }

  /**
   * The displayed color of the member in base 10
   * @type {number}
   * @readonly
   */
  get displayColor() {
    const role = this.colorRole;
    return (role && role.color) || 0;
  }

  /**
   * The displayed color of the member in hexadecimal
   * @type {string}
   * @readonly
   */
  get displayHexColor() {
    const role = this.colorRole;
    return (role && role.hexColor) || '#000000';
  }

  /**
   * The role of the member used to hoist them in a separate category in the users list
   * @type {?Role}
   * @readonly
   */
  get hoistRole() {
    const hoistedRoles = this.roles.filter(role => role.hoist);
    if (!hoistedRoles.size) return null;
    return hoistedRoles.reduce((prev, role) => !prev || role.comparePositionTo(prev) > 0 ? role : prev);
  }

  /**
   * Whether this member is muted in any way
   * @type {boolean}
   * @readonly
   */
  get mute() {
    return this.selfMute || this.serverMute;
  }

  /**
   * Whether this member is deafened in any way
   * @type {boolean}
   * @readonly
   */
  get deaf() {
    return this.selfDeaf || this.serverDeaf;
  }

  /**
   * The voice channel this member is in, if any
   * @type {?VoiceChannel}
   * @readonly
   */
  get voiceChannel() {
    return this.guild.channels.get(this.voiceChannelID);
  }

  /**
   * The ID of this user
   * @type {Snowflake}
   * @readonly
   */
  get id() {
    return this.user.id;
  }

  /**
   * The nickname of the member, or their username if they don't have one
   * @type {string}
   * @readonly
   */
  get displayName() {
    return this.nickname || this.user.username;
  }

  /**
   * The overall set of permissions for the guild member, taking only roles into account
   * @type {Permissions}
   * @readonly
   */
  get permissions() {
    if (this.user.id === this.guild.ownerID) return new Permissions(this, Permissions.ALL);

    let permissions = 0;
    const roles = this.roles;
    for (const role of roles.values()) permissions |= role.permissions;

    return new Permissions(this, permissions);
  }

  /**
   * Whether the member is kickable by the client user
   * @type {boolean}
   * @readonly
   */
  get kickable() {
    if (this.user.id === this.guild.ownerID) return false;
    if (this.user.id === this.client.user.id) return false;
    const clientMember = this.guild.member(this.client.user);
    if (!clientMember.hasPermission(Permissions.FLAGS.KICK_MEMBERS)) return false;
    return clientMember.highestRole.comparePositionTo(this.highestRole) > 0;
  }

  /**
   * Whether the member is bannable by the client user
   * @type {boolean}
   * @readonly
   */
  get bannable() {
    if (this.user.id === this.guild.ownerID) return false;
    if (this.user.id === this.client.user.id) return false;
    const clientMember = this.guild.member(this.client.user);
    if (!clientMember.hasPermission(Permissions.FLAGS.BAN_MEMBERS)) return false;
    return clientMember.highestRole.comparePositionTo(this.highestRole) > 0;
  }

  /**
   * Returns `channel.permissionsFor(guildMember)`. Returns permissions for a member in a guild channel,
   * taking into account roles and permission overwrites.
   * @param {ChannelResolvable} channel The guild channel to use as context
   * @returns {?Permissions}
   */
  permissionsIn(channel) {
    channel = this.client.resolver.resolveChannel(channel);
    if (!channel || !channel.guild) throw new Error('Could not resolve channel to a guild channel.');
    return channel.permissionsFor(this);
  }

  /**
   * Checks if any of the member's roles have a permission.
   * @param {PermissionResolvable|PermissionResolvable[]} permission Permission(s) to check for
   * @param {boolean} [explicit=false] Whether to require the role to explicitly have the exact permission
   * **(deprecated)**
   * @param {boolean} [checkAdmin] Whether to allow the administrator permission to override
   * (takes priority over `explicit`)
   * @param {boolean} [checkOwner] Whether to allow being the guild's owner to override
   * (takes priority over `explicit`)
   * @returns {boolean}
   */
  hasPermission(permission, explicit = false, checkAdmin, checkOwner) {
    if (typeof checkAdmin === 'undefined') checkAdmin = !explicit;
    if (typeof checkOwner === 'undefined') checkOwner = !explicit;
    if (checkOwner && this.user.id === this.guild.ownerID) return true;
    return this.roles.some(r => r.hasPermission(permission, undefined, checkAdmin));
  }

  /**
   * Checks whether the roles of the member allows them to perform specific actions.
   * @param {PermissionResolvable[]} permissions The permissions to check for
   * @param {boolean} [explicit=false] Whether to require the member to explicitly have the exact permissions
   * @returns {boolean}
   * @deprecated
   */
  hasPermissions(permissions, explicit = false) {
    if (!explicit && this.user.id === this.guild.ownerID) return true;
    return permissions.every(p => this.hasPermission(p, explicit));
  }

  /**
   * Checks whether the roles of the member allows them to perform specific actions, and lists any missing permissions.
   * @param {PermissionResolvable[]} permissions The permissions to check for
   * @param {boolean} [explicit=false] Whether to require the member to explicitly have the exact permissions
   * @returns {PermissionResolvable[]}
   */
  missingPermissions(permissions, explicit = false) {
    return permissions.filter(p => !this.hasPermission(p, explicit));
  }

  /**
   * The data for editing a guild member.
   * @typedef {Object} GuildMemberEditData
   * @property {string} [nick] The nickname to set for the member
   * @property {Collection<Snowflake, Role>|Role[]|Snowflake[]} [roles] The roles or role IDs to apply
   * @property {boolean} [mute] Whether or not the member should be muted
   * @property {boolean} [deaf] Whether or not the member should be deafened
   * @property {ChannelResolvable} [channel] Channel to move member to (if they are connected to voice)
   */

  /**
   * Edit a guild member.
   * @param {GuildMemberEditData} data The data to edit the member with
   * @returns {Promise<GuildMember>}
   */
  edit(data) {
    return this.client.rest.methods.updateGuildMember(this, data);
  }

  /**
   * Mute/unmute a user.
   * @param {boolean} mute Whether or not the member should be muted
   * @returns {Promise<GuildMember>}
   */
  setMute(mute) {
    return this.edit({ mute });
  }

  /**
   * Deafen/undeafen a user.
   * @param {boolean} deaf Whether or not the member should be deafened
   * @returns {Promise<GuildMember>}
   */
  setDeaf(deaf) {
    return this.edit({ deaf });
  }

  /**
   * Moves the guild member to the given channel.
   * @param {ChannelResolvable} channel The channel to move the member to
   * @returns {Promise<GuildMember>}
   */
  setVoiceChannel(channel) {
    return this.edit({ channel });
  }

  /**
   * Sets the roles applied to the member.
   * @param {Collection<Snowflake, Role>|Role[]|Snowflake[]} roles The roles or role IDs to apply
   * @returns {Promise<GuildMember>}
   */
  setRoles(roles) {
    return this.edit({ roles });
  }

  /**
   * Adds a single role to the member.
   * @param {Role|Snowflake} role The role or ID of the role to add
   * @returns {Promise<GuildMember>}
   */
  addRole(role) {
    if (!(role instanceof Role)) role = this.guild.roles.get(role);
    if (!role) throw new TypeError('Supplied parameter was neither a Role nor a Snowflake.');
    return this.client.rest.methods.addMemberRole(this, role);
  }

  /**
   * Adds multiple roles to the member.
   * @param {Collection<Snowflake, Role>|Role[]|Snowflake[]} roles The roles or role IDs to add
   * @returns {Promise<GuildMember>}
   */
  addRoles(roles) {
    let allRoles;
    if (roles instanceof Collection) {
      allRoles = this._roles.slice();
      for (const role of roles.values()) allRoles.push(role.id);
    } else {
      allRoles = this._roles.concat(roles);
    }
    return this.edit({ roles: allRoles });
  }

  /**
   * Removes a single role from the member.
   * @param {Role|Snowflake} role The role or ID of the role to remove
   * @returns {Promise<GuildMember>}
   */
  removeRole(role) {
    if (!(role instanceof Role)) role = this.guild.roles.get(role);
    if (!role) throw new TypeError('Supplied parameter was neither a Role nor a Snowflake.');
    return this.client.rest.methods.removeMemberRole(this, role);
  }

  /**
   * Removes multiple roles from the member.
   * @param {Collection<Snowflake, Role>|Role[]|Snowflake[]} roles The roles or role IDs to remove
   * @returns {Promise<GuildMember>}
   */
  removeRoles(roles) {
    const allRoles = this._roles.slice();
    if (roles instanceof Collection) {
      for (const role of roles.values()) {
        const index = allRoles.indexOf(role.id);
        if (index >= 0) allRoles.splice(index, 1);
      }
    } else {
      for (const role of roles) {
        const index = allRoles.indexOf(role instanceof Role ? role.id : role);
        if (index >= 0) allRoles.splice(index, 1);
      }
    }
    return this.edit({ roles: allRoles });
  }

  /**
   * Set the nickname for the guild member.
   * @param {string} nick The nickname for the guild member
   * @returns {Promise<GuildMember>}
   */
  setNickname(nick) {
    return this.edit({ nick });
  }

  /**
   * Creates a DM channel between the client and the member.
   * @returns {Promise<DMChannel>}
   */
  createDM() {
    return this.user.createDM();
  }

  /**
   * Deletes any DMs with this guild member.
   * @returns {Promise<DMChannel>}
   */
  deleteDM() {
    return this.user.deleteDM();
  }

  /**
   * Kick this member from the guild.
   * @param {string} [reason] Reason for kicking user
   * @returns {Promise<GuildMember>}
   */
  kick(reason) {
    return this.client.rest.methods.kickGuildMember(this.guild, this, reason);
  }

  /**
   * Ban this guild member
   * @param {Object} [options] Ban options.
   * @param {number} [options.days=0] Number of days of messages to delete
   * @param {string} [options.reason] Reason for banning
   * @returns {Promise<GuildMember>}
   * @example
   * // ban a guild member
   * guildMember.ban(7);
   */
  ban(options) {
    return this.guild.ban(this, options);
  }

  /**
   * When concatenated with a string, this automatically concatenates the user's mention instead of the Member object.
   * @returns {string}
   * @example
   * // Logs: Hello from <@123456789>!
   * console.log(`Hello from ${member}!`);
   */
  toString() {
    return `<@${this.nickname ? '!' : ''}${this.user.id}>`;
  }

  // These are here only for documentation purposes - they are implemented by TextBasedChannel
  /* eslint-disable no-empty-function */
  send() {}
  sendMessage() {}
  sendEmbed() {}
  sendFile() {}
  sendCode() {}
}

TextBasedChannel.applyToClass(GuildMember);

GuildMember.prototype.hasPermissions = util.deprecate(GuildMember.prototype.hasPermissions,
  'GuildMember#hasPermissions is deprecated - use GuildMember#hasPermission, it now takes an array');

module.exports = GuildMember;
<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

/**
 * All utility functions for processing synced records after being synced. These changes
 * should be detected by SyncQueue and sent back to the server on next sync.
 */
import { Client as BugsnagClient } from 'bugsnag-react-native';

import { getNextNumber, NUMBER_SEQUENCE_KEYS } from '../database/utilities';

import { SETTINGS_KEYS, STORE_CUSTOM_DATA_KEYS } from '../settings';

const { LAST_POST_PROCESSING_FAILED } = SETTINGS_KEYS;
const { REQUISITION_SERIAL_NUMBER, SUPPLIER_INVOICE_NUMBER } = NUMBER_SEQUENCE_KEYS;

export class PostSyncProcessor {
  constructor(database, settings) {
    this.database = database;
    this.settings = settings;
    this.recordQueue = new Map(); // Map of [recordId, recordType].
    this.functionQueue = [];
    this.database.addListener(this.onDatabaseEvent);
  }

  setUser = user => {
    this.user = user;
  };

  /**
   * Respond to a database change event relating to incoming sync records. Adds records
   * to end of |this.recordQueue| and removes any earlier duplicates.
   *
   * @param   {string}  changeType  The type of database change, e.g. CREATE, UPDATE, DELETE.
   * @param   {string}  recordType  The type of record changed (from database schema).
   * @param   {object}  record      The record changed.
   * @param   {string}  causedBy    The cause of this database event, either 'sync' or undefined.
   * @return  {none}
   */
  onDatabaseEvent = (changeType, recordType, record, causedBy) => {
    // Exit if not a change caused by incoming sync.
    if (causedBy !== 'sync' || recordType === 'SyncOut') return;

    if (this.recordQueue.has(record.id)) {
      // Check if already in queue, remove old.
      this.recordQueue.delete(record.id);
    }

    // Add new entry at end of Map.
    this.recordQueue.set(record.id, recordType);
  };

  /**
   * Check if last sync of the app failed.
   *
   * @return  {boolean}  True if the last call of synchronise failed.
   */
  lastPostSyncProcessingFailed = () => {
    const lastPostSyncProcessingFailed = this.settings.get(LAST_POST_PROCESSING_FAILED);
    return lastPostSyncProcessingFailed && lastPostSyncProcessingFailed === 'true';
  };

  /**
   * Attempts to enqueue functions for every record in specified tables of local database,
   * ensuring data is correct. Tables manually added as to not iterate over table function
   * generators.
   */
  processAnyUnprocessedRecords = () => {
    this.settings.set(LAST_POST_PROCESSING_FAILED, 'true');
    this.functionQueue = [];
    this.recordQueue.clear(); // Reset the |recordQueue| to avoid unnessary runs.

    this.database
      .objects('Requisition')
      .forEach(record => this.enqueueFunctionsForRecordType('Requisition', record));

    this.database
      .objects('Transaction')
      .forEach(record => this.enqueueFunctionsForRecordType('Transaction', record));

    this.processFunctionQueue();
    this.settings.set(LAST_POST_PROCESSING_FAILED, 'false');
  };

  /**
   * Processes this stores custom data. Certain fields (See: Settings/index.js)
   * in settings are customizable from the server, providing altered behavior.
   * After syncing, recheck and save all of these fields
   */
  processCustomData = () => {
    const customData = this.settings.get(SETTINGS_KEYS.THIS_STORE_CUSTOM_DATA);

    if (customData) {
      try {
        const parsedCustomData = JSON.parse(customData);

        this.database.write(() => {
          Object.values(STORE_CUSTOM_DATA_KEYS).forEach(value => {
            if (parsedCustomData[value]) {
              this.database.update('Setting', {
                key: value,
                value: parsedCustomData[value].data ?? '',
              });
            }
          });
        });
      } catch (error) {
        BugsnagClient.notify(error, report => {
          report.context = 'CustomDataProcessing Error';
          report.metadata = { error: { message: 'Error processing', customData } };
        });
      }
    }
  };

  /**
   * Iterates through records added through listening to sync, adding needed functions
   * to |functionQueue|. Runs the |functionQueue| to make the changes.
   */
  processRecordQueue = () => {
    this.settings.set(LAST_POST_PROCESSING_FAILED, 'true');
    this.recordQueue.forEach((recordType, recordId) => {
      // Use local database record, not what comes in sync. Ensures that records are
      // integrated information (definitely after sync is done).
      if (recordType === 'Setting') return;
      const internalRecord = this.database.objects(recordType).filtered('id == $0', recordId)[0];
      this.enqueueFunctionsForRecordType(recordType, internalRecord);
    });

    this.processFunctionQueue();
    this.processCustomData();
    this.recordQueue.clear(); // Reset the |recordQueue| to avoid unnecessary runs.

    this.settings.set(LAST_POST_PROCESSING_FAILED, 'false');
  };

  /**
   * Runs all the post sync functions in |this.functionQueue| and clears queue.
   */
  processFunctionQueue = () => {
    this.database.write(() => {
      this.functionQueue.forEach(func => {
        func();
      });
    });
    this.functionQueue = [];
  };

  /**
   * Delegates records to the correct utility function and adds generated functions
   * to |this.functionQueue|.
   *
   * @param   {string}  recordType  The type of record changed (from database schema).
   * @param   {object}  record      The changed record.
   * @return  {none}
   */
  enqueueFunctionsForRecordType = (recordType, record) => {
    if (!record) return;
    switch (recordType) {
      case 'Requisition':
        this.functionQueue = this.functionQueue.concat(
          this.generateFunctionsForRequisition(record)
        );
        break;
      case 'Transaction':
        this.functionQueue = this.functionQueue.concat(
          this.generateFunctionsForTransaction(record)
        );
        break;
      default:
        break;
    }
  };

  /**
   * Builds an array of post sync functions based on conditions, appends a database write call
   * to apply all post sync functions (if there are any).
   *
   * @param   {object}  record     The record changed.
   * @return  {array}              An array of functions to be called for the given record.
   */
  generateFunctionsForRequisition = record => {
    const funcs = [];

    // Allocate serial number to requisitions with serial number of -1. These serial numbers
    // have been generated by the server, and represent records coming from another store as
    // supplier.
    if (record.serialNumber === '-1') {
      funcs.push(() => {
        record.serialNumber = getNextNumber(this.database, REQUISITION_SERIAL_NUMBER);
      });
    }

    if (!record.isRequest && !record.isFinalised) {
      funcs.push(() => {
        record.createCustomerInvoice(this.database, this.user);
      });
    }

    // If any changes, add database update for record.
    if (funcs.length > 0) {
      funcs.push(() => {
        this.database.save('Requisition', record);
      });
    }

    return funcs;
  };

  /**
   * Builds an array of post sync functions based on conditions, appends a database write call
   * to apply all the post sync functions if there are any.
   *
   * @param   {object}  record     The changed record.
   * @return  {array}              An array of functions to be called for the changed record.
   */
  generateFunctionsForTransaction = record => {
    const funcs = [];

    // Allocate serial number to requisitions with serial number of -1. These serial numbers
    // have been generated by the server, and represent records coming from another store as
    // supplier.
    if (record.serialNumber === '-1' && record.type === 'supplier_invoice') {
      funcs.push(() => {
        record.serialNumber = getNextNumber(this.database, SUPPLIER_INVOICE_NUMBER);
      });
    }

    // If any changes, add database update for record.
    if (funcs.length > 0) {
      funcs.push(() => {
        this.database.save('Transaction', record);
      });
    }

    return funcs;
  };
}

export default PostSyncProcessor;
<commit_msg>Add update PostSyncProcessor with bugsnag<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

/**
 * All utility functions for processing synced records after being synced. These changes
 * should be detected by SyncQueue and sent back to the server on next sync.
 */
import { Client } from 'bugsnag-react-native';

import { getNextNumber, NUMBER_SEQUENCE_KEYS } from '../database/utilities';

import { SETTINGS_KEYS, STORE_CUSTOM_DATA_KEYS } from '../settings';

const { LAST_POST_PROCESSING_FAILED } = SETTINGS_KEYS;
const { REQUISITION_SERIAL_NUMBER, SUPPLIER_INVOICE_NUMBER } = NUMBER_SEQUENCE_KEYS;

const BugsnagClient = new Client();

export class PostSyncProcessor {
  constructor(database, settings) {
    this.database = database;
    this.settings = settings;
    this.recordQueue = new Map(); // Map of [recordId, recordType].
    this.functionQueue = [];
    this.database.addListener(this.onDatabaseEvent);
  }

  setUser = user => {
    this.user = user;
  };

  /**
   * Respond to a database change event relating to incoming sync records. Adds records
   * to end of |this.recordQueue| and removes any earlier duplicates.
   *
   * @param   {string}  changeType  The type of database change, e.g. CREATE, UPDATE, DELETE.
   * @param   {string}  recordType  The type of record changed (from database schema).
   * @param   {object}  record      The record changed.
   * @param   {string}  causedBy    The cause of this database event, either 'sync' or undefined.
   * @return  {none}
   */
  onDatabaseEvent = (changeType, recordType, record, causedBy) => {
    // Exit if not a change caused by incoming sync.
    if (causedBy !== 'sync' || recordType === 'SyncOut') return;

    if (this.recordQueue.has(record.id)) {
      // Check if already in queue, remove old.
      this.recordQueue.delete(record.id);
    }

    // Add new entry at end of Map.
    this.recordQueue.set(record.id, recordType);
  };

  /**
   * Check if last sync of the app failed.
   *
   * @return  {boolean}  True if the last call of synchronise failed.
   */
  lastPostSyncProcessingFailed = () => {
    const lastPostSyncProcessingFailed = this.settings.get(LAST_POST_PROCESSING_FAILED);
    return lastPostSyncProcessingFailed && lastPostSyncProcessingFailed === 'true';
  };

  /**
   * Attempts to enqueue functions for every record in specified tables of local database,
   * ensuring data is correct. Tables manually added as to not iterate over table function
   * generators.
   */
  processAnyUnprocessedRecords = () => {
    this.settings.set(LAST_POST_PROCESSING_FAILED, 'true');
    this.functionQueue = [];
    this.recordQueue.clear(); // Reset the |recordQueue| to avoid unnessary runs.

    this.database
      .objects('Requisition')
      .forEach(record => this.enqueueFunctionsForRecordType('Requisition', record));

    this.database
      .objects('Transaction')
      .forEach(record => this.enqueueFunctionsForRecordType('Transaction', record));

    this.processFunctionQueue();
    this.settings.set(LAST_POST_PROCESSING_FAILED, 'false');
  };

  /**
   * Processes this stores custom data. Certain fields (See: Settings/index.js)
   * in settings are customizable from the server, providing altered behavior.
   * After syncing, recheck and save all of these fields.
   */
  processCustomData = () => {
    // This stores custom data, stored as a JSON stringified object
    const customData = this.settings.get(SETTINGS_KEYS.THIS_STORE_CUSTOM_DATA);

    try {
      // If there is no custom data, use an empty object and clear all
      // customizable settings (set to an empty string)
      const parsedCustomData = JSON.parse(customData || '{}');

      // Iterate over custom data keys - setting to either what is in the
      // current custom data, or set to an empty string if it is either not
      // present, or has been deleted.
      this.database.write(() => {
        Object.values(STORE_CUSTOM_DATA_KEYS).forEach(value => {
          this.database.update('Setting', {
            key: value,
            value: parsedCustomData[value]?.data ?? '',
          });
        });
      });
    } catch (error) {
      BugsnagClient.notify(error, report => {
        report.context = 'CustomDataProcessing Error';
        report.metadata = { error: { message: 'Method: processCustomData', customData } };
      });
    }
  };

  /**
   * Iterates through records added through listening to sync, adding needed functions
   * to |functionQueue|. Runs the |functionQueue| to make the changes.
   */
  processRecordQueue = () => {
    this.settings.set(LAST_POST_PROCESSING_FAILED, 'true');
    this.recordQueue.forEach((recordType, recordId) => {
      // Use local database record, not what comes in sync. Ensures that records are
      // integrated information (definitely after sync is done).
      if (recordType === 'Setting') return;
      const internalRecord = this.database.objects(recordType).filtered('id == $0', recordId)[0];
      this.enqueueFunctionsForRecordType(recordType, internalRecord);
    });
    this.processFunctionQueue();
    this.processCustomData();
    this.recordQueue.clear(); // Reset the |recordQueue| to avoid unnecessary runs.

    this.settings.set(LAST_POST_PROCESSING_FAILED, 'false');
  };

  /**
   * Runs all the post sync functions in |this.functionQueue| and clears queue.
   */
  processFunctionQueue = () => {
    this.database.write(() => {
      this.functionQueue.forEach(func => {
        func();
      });
    });
    this.functionQueue = [];
  };

  /**
   * Delegates records to the correct utility function and adds generated functions
   * to |this.functionQueue|.
   *
   * @param   {string}  recordType  The type of record changed (from database schema).
   * @param   {object}  record      The changed record.
   * @return  {none}
   */
  enqueueFunctionsForRecordType = (recordType, record) => {
    if (!record) return;
    switch (recordType) {
      case 'Requisition':
        this.functionQueue = this.functionQueue.concat(
          this.generateFunctionsForRequisition(record)
        );
        break;
      case 'Transaction':
        this.functionQueue = this.functionQueue.concat(
          this.generateFunctionsForTransaction(record)
        );
        break;
      default:
        break;
    }
  };

  /**
   * Builds an array of post sync functions based on conditions, appends a database write call
   * to apply all post sync functions (if there are any).
   *
   * @param   {object}  record     The record changed.
   * @return  {array}              An array of functions to be called for the given record.
   */
  generateFunctionsForRequisition = record => {
    const funcs = [];

    // Allocate serial number to requisitions with serial number of -1. These serial numbers
    // have been generated by the server, and represent records coming from another store as
    // supplier.
    if (record.serialNumber === '-1') {
      funcs.push(() => {
        record.serialNumber = getNextNumber(this.database, REQUISITION_SERIAL_NUMBER);
      });
    }

    if (!record.isRequest && !record.isFinalised) {
      funcs.push(() => {
        record.createCustomerInvoice(this.database, this.user);
      });
    }

    // If any changes, add database update for record.
    if (funcs.length > 0) {
      funcs.push(() => {
        this.database.save('Requisition', record);
      });
    }

    return funcs;
  };

  /**
   * Builds an array of post sync functions based on conditions, appends a database write call
   * to apply all the post sync functions if there are any.
   *
   * @param   {object}  record     The changed record.
   * @return  {array}              An array of functions to be called for the changed record.
   */
  generateFunctionsForTransaction = record => {
    const funcs = [];

    // Allocate serial number to requisitions with serial number of -1. These serial numbers
    // have been generated by the server, and represent records coming from another store as
    // supplier.
    if (record.serialNumber === '-1' && record.type === 'supplier_invoice') {
      funcs.push(() => {
        record.serialNumber = getNextNumber(this.database, SUPPLIER_INVOICE_NUMBER);
      });
    }

    // If any changes, add database update for record.
    if (funcs.length > 0) {
      funcs.push(() => {
        this.database.save('Transaction', record);
      });
    }

    return funcs;
  };
}

export default PostSyncProcessor;
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	crypto = require('crypto'),
	Wg = mongoose.model('Wg'),
	User = mongoose.model('User'),
	Shoppinglist = mongoose.model('Shoppinglist'),
	Payment = mongoose.model('Payment'),
	Xtasklist = mongoose.model('Xtasklist'),
	Todo = mongoose.model('Todo'),
	_ = require('lodash'),
	randomWord = require('random-word');

/**
 * Create a Wg
 */
exports.create = function(req, res) {
	var wg = new Wg(req.body);
	wg.users.push(req.user._id);
	wg.created_from = req.user.username;

	User.update({ _id: req.user.id }, { $set: { wg_id: wg._id }}, function(error, doc){
		if(error) console.log(error);
		console.log('user update, added wgID');
	});

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

exports.join = function(req, res) {
	var wg, user;

	wg = Wg.findOne({ 'passphrase': req.body.pass }, function(err, wg) {
		if(err){
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		}
		if(wg === null){
			return res.status(400).send({
				message: 'No flat with this passphrase found!'
			});
		}

		Wg.update({ _id: wg._id },{ $push: { users: req.user._id }}, function(){});

		user = User.update({ _id: req.user._id }, { $set: { wg_id: wg._id }}, function(error, user){
			if(err){
				return res.status(400).send({
					message: errorHandler.getErrorMessage(err)
				});
			}
		});
		return res.json(wg);
	});
};

/**
 * Show the current Wg
 */
exports.read = function(req, res) {
	res.jsonp(req.wg);
};

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

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

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

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

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

			Shoppinglist.remove({wg_id: mongoose.Types.ObjectId(wg._id)});
			Payment.remove({wg_id: mongoose.Types.ObjectId(wg._id)});
			Xtasklist.remove({wg_id: mongoose.Types.ObjectId(wg._id)});
			Todo.remove({wg_id: mongoose.Types.ObjectId(wg._id)});
			User.update({ _id: req.user._id }, { $set: { wg_id: null }}, function(error, user){
				if(error){
					console.log('error');
					return res.status(400).send({
						message: errorHandler.getErrorMessage(error)
					});
				}
				res.jsonp(user);
			});
		}
	});
};

exports.removeUser = function(req, res) {
	var wg = req.body;

	var newPass = randomWord() + "-" + randomWord();

	Xtasklist.find({wg_id: mongoose.Types.ObjectId(wg._id)}, function(err, tasks){
		var task = tasks;
		task.forEach(function(elem){
			if(elem.users.hasOwnProperty(req.user._id)){
				var turnUser = elem.users[req.user._id].turn;
				var isNext = elem.users[req.user._id].isNext;
				var isCurrent = (elem.crtUser.toString() == req.user._id.toString());

				var changeNext = false;
				var newNextTurn = 0;
				var crtIsNext = false;
				var usersNext = [];

				for(var user in elem.users){
					usersNext.push({id: elem.users[user]._id, turn: elem.users[user].turn});
					if(elem.users[user].turn > turnUser) elem.users[user].turn = Math.max(1, elem.users[user].turn-1);
					if(elem.users[user].turn == turnUser) elem.users[user].isNext = isNext;
					if((turnUser-1) === 1) crtIsNext = true;
					if(isCurrent && elem.users[user].isNext){
						newNextTurn = elem.users[user].turn+1;
						changeNext = true;
						elem.crtUser = elem.users[user]._id;
					}
				}
				if(changeNext || crtIsNext){
					usersNext.forEach(function(u){
						if(crtIsNext){
							elem.users[u.id].isNext = true;
						}
						if(changeNext && u.isNext === newNextTurn){
							elem.users[u.id].isNext = true;
						}
					});
				}
				console.log('delete KEY');
				delete elem.users[req.user._id];

				Xtasklist.update({ _id: elem._id },{ $set: { users: elem.users }}, function(err){
					if(err) console.log('error while update task');
					console.log('update task success');
				});
			}
		});

		Wg.update({ _id: wg._id },{ $set: { users: wg.users, passphrase: newPass }}, function(error,wg){
			console.log('update wg.users');
			if(error){ console.log('error'); }
		});
	});

	User.update({ _id: req.user._id }, { $set: { wg_id: null, balance: 0 }}, function(error, user){
		if(error){
			console.log('error');
			return res.status(400).send({
			message: errorHandler.getErrorMessage(error)
			});
		}
		res.jsonp(user);
	});
};

/**
 * List of Wgs
 */
// exports.list = function(req, res) {
// 	var wg = Wg.where({ _id: req.user.wg_id });
// 	console.log('wg', wg);
// 		if (!wg) {
// 			return res.status(400).send({
// 				message: errorHandler.getErrorMessage(err)
// 			});
// 		} else {
// 			res.jsonp(wg);
// 		}
// };

/**
 * Wg middleware
 */
exports.wgByID = function(req, res, next, id) {
	Wg.findById(id).populate('user', 'displayName').exec(function(err, wg) {
		if (err) return next(err);
		if (! wg) return next(new Error('Failed to load flat-share ' + id));
		req.wg = wg;
		next();
	});
};

exports.wgByUser = function(req, res, next) {
	if(!req.user.wg_id){
		next();
	} else {
		Wg.findById(req.user.wg_id).exec(function(err, wg) {
			if(err) return next(err);
			if(!wg) return next(new Error('Failed to load'));
			req.wg = wg;
			next();
		});
	}
};

exports.wgByPass = function(req, res, next, pass) {
	Wg.findOne({'passphrase': pass}, function(err, wg) {
		if(err) return next(err);
		if(!wg) return next(new Error('Failed to find flat-share with that passphrase'));
		req.wg = wg;
		next();
	});
};

function isUserInWg(myArray, searchTerm) {
    for(var i = 0; i < myArray.length; i++) {
        if (myArray[i].toString() === searchTerm) return true;
    }
    return false;
}

/**
 * Wg authorization middleware
 */
exports.hasAuthorization = function(req, res, next) {
	// if (req.wg.user.id !== req.user.id) {
	// 	return res.status(403).send('User is not authorized');
	// }

	if(!isUserInWg(req.wg.users, req.user.id)){
		return res.status(403).send('User is not authorized');
	}
	next();
};

exports.isAllowedToLeave = function(req, res, next) {
	if(req.user.wg_id.toString() !== req.body._id.toString()) {
		return res.status(403).send('User is not authorized');
	}
	if(req.user.balance !== 0) {
		return res.status(403).send('User is has debts/credit.');
	}
	next();
};

exports.loggon = function(req, res) {
	User.update({ _id: req.body.user._id }, { $set: { socket_id: req.body.socketID }}, function(error, user){
		if(error){
			return res.status(400).send({
				message: errorHandler.getErrorMessage(error)
			});
		}
	});
};
<commit_msg>refactor(wgs): change string quotes to preferred single-quotes<commit_after>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
	errorHandler = require('./errors.server.controller'),
	crypto = require('crypto'),
	Wg = mongoose.model('Wg'),
	User = mongoose.model('User'),
	Shoppinglist = mongoose.model('Shoppinglist'),
	Payment = mongoose.model('Payment'),
	Xtasklist = mongoose.model('Xtasklist'),
	Todo = mongoose.model('Todo'),
	_ = require('lodash'),
	randomWord = require('random-word');

/**
 * Create a Wg
 */
exports.create = function(req, res) {
	var wg = new Wg(req.body);
	wg.users.push(req.user._id);
	wg.created_from = req.user.username;

	User.update({ _id: req.user.id }, { $set: { wg_id: wg._id }}, function(error, doc){
		if(error) console.log(error);
		console.log('user update, added wgID');
	});

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

exports.join = function(req, res) {
	var wg, user;

	wg = Wg.findOne({ 'passphrase': req.body.pass }, function(err, wg) {
		if(err){
			return res.status(400).send({
				message: errorHandler.getErrorMessage(err)
			});
		}
		if(wg === null){
			return res.status(400).send({
				message: 'No flat with this passphrase found!'
			});
		}

		Wg.update({ _id: wg._id },{ $push: { users: req.user._id }}, function(){});

		user = User.update({ _id: req.user._id }, { $set: { wg_id: wg._id }}, function(error, user){
			if(err){
				return res.status(400).send({
					message: errorHandler.getErrorMessage(err)
				});
			}
		});
		return res.json(wg);
	});
};

/**
 * Show the current Wg
 */
exports.read = function(req, res) {
	res.jsonp(req.wg);
};

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

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

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

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

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

			Shoppinglist.remove({wg_id: mongoose.Types.ObjectId(wg._id)});
			Payment.remove({wg_id: mongoose.Types.ObjectId(wg._id)});
			Xtasklist.remove({wg_id: mongoose.Types.ObjectId(wg._id)});
			Todo.remove({wg_id: mongoose.Types.ObjectId(wg._id)});
			User.update({ _id: req.user._id }, { $set: { wg_id: null }}, function(error, user){
				if(error){
					console.log('error');
					return res.status(400).send({
						message: errorHandler.getErrorMessage(error)
					});
				}
				res.jsonp(user);
			});
		}
	});
};

exports.removeUser = function(req, res) {
	var wg = req.body;

	var newPass = randomWord() + '-' + randomWord();

	Xtasklist.find({wg_id: mongoose.Types.ObjectId(wg._id)}, function(err, tasks){
		var task = tasks;
		task.forEach(function(elem){
			if(elem.users.hasOwnProperty(req.user._id)){
				var turnUser = elem.users[req.user._id].turn;
				var isNext = elem.users[req.user._id].isNext;
				var isCurrent = (elem.crtUser.toString() == req.user._id.toString());

				var changeNext = false;
				var newNextTurn = 0;
				var crtIsNext = false;
				var usersNext = [];

				for(var user in elem.users){
					usersNext.push({id: elem.users[user]._id, turn: elem.users[user].turn});
					if(elem.users[user].turn > turnUser) elem.users[user].turn = Math.max(1, elem.users[user].turn-1);
					if(elem.users[user].turn == turnUser) elem.users[user].isNext = isNext;
					if((turnUser-1) === 1) crtIsNext = true;
					if(isCurrent && elem.users[user].isNext){
						newNextTurn = elem.users[user].turn+1;
						changeNext = true;
						elem.crtUser = elem.users[user]._id;
					}
				}
				if(changeNext || crtIsNext){
					usersNext.forEach(function(u){
						if(crtIsNext){
							elem.users[u.id].isNext = true;
						}
						if(changeNext && u.isNext === newNextTurn){
							elem.users[u.id].isNext = true;
						}
					});
				}
				console.log('delete KEY');
				delete elem.users[req.user._id];

				Xtasklist.update({ _id: elem._id },{ $set: { users: elem.users }}, function(err){
					if(err) console.log('error while update task');
					console.log('update task success');
				});
			}
		});

		Wg.update({ _id: wg._id },{ $set: { users: wg.users, passphrase: newPass }}, function(error,wg){
			console.log('update wg.users');
			if(error){ console.log('error'); }
		});
	});

	User.update({ _id: req.user._id }, { $set: { wg_id: null, balance: 0 }}, function(error, user){
		if(error){
			console.log('error');
			return res.status(400).send({
			message: errorHandler.getErrorMessage(error)
			});
		}
		res.jsonp(user);
	});
};

/**
 * List of Wgs
 */
// exports.list = function(req, res) {
// 	var wg = Wg.where({ _id: req.user.wg_id });
// 	console.log('wg', wg);
// 		if (!wg) {
// 			return res.status(400).send({
// 				message: errorHandler.getErrorMessage(err)
// 			});
// 		} else {
// 			res.jsonp(wg);
// 		}
// };

/**
 * Wg middleware
 */
exports.wgByID = function(req, res, next, id) {
	Wg.findById(id).populate('user', 'displayName').exec(function(err, wg) {
		if (err) return next(err);
		if (! wg) return next(new Error('Failed to load flat-share ' + id));
		req.wg = wg;
		next();
	});
};

exports.wgByUser = function(req, res, next) {
	if(!req.user.wg_id){
		next();
	} else {
		Wg.findById(req.user.wg_id).exec(function(err, wg) {
			if(err) return next(err);
			if(!wg) return next(new Error('Failed to load'));
			req.wg = wg;
			next();
		});
	}
};

exports.wgByPass = function(req, res, next, pass) {
	Wg.findOne({'passphrase': pass}, function(err, wg) {
		if(err) return next(err);
		if(!wg) return next(new Error('Failed to find flat-share with that passphrase'));
		req.wg = wg;
		next();
	});
};

function isUserInWg(myArray, searchTerm) {
    for(var i = 0; i < myArray.length; i++) {
        if (myArray[i].toString() === searchTerm) return true;
    }
    return false;
}

/**
 * Wg authorization middleware
 */
exports.hasAuthorization = function(req, res, next) {
	// if (req.wg.user.id !== req.user.id) {
	// 	return res.status(403).send('User is not authorized');
	// }

	if(!isUserInWg(req.wg.users, req.user.id)){
		return res.status(403).send('User is not authorized');
	}
	next();
};

exports.isAllowedToLeave = function(req, res, next) {
	if(req.user.wg_id.toString() !== req.body._id.toString()) {
		return res.status(403).send('User is not authorized');
	}
	if(req.user.balance !== 0) {
		return res.status(403).send('User is has debts/credit.');
	}
	next();
};

exports.loggon = function(req, res) {
	User.update({ _id: req.body.user._id }, { $set: { socket_id: req.body.socketID }}, function(error, user){
		if(error){
			return res.status(400).send({
				message: errorHandler.getErrorMessage(error)
			});
		}
	});
};
<|endoftext|>


<commit_before>'use strict'

var net = require('net'),
	http = require('http'),
	https = require('https'),
	url = require('url'),
	path = require('path'),
	fs = require('fs'),

	Buffer = require('buffer').Buffer,
	WebSocketServer = require('ws').Server,

	webServer, wsServer,
	source_host, source_port, target_host, target_port,
	web_path = null,
	argv = null;

// Handle new WebSocket client
var new_client = function(client) {
	var clientAddr = client._socket.remoteAddress, log;
	if(client.upgradeReq && client.upgradeReq.url) console.log(client.upgradeReq.url);
	log = function (msg) {
		console.log(' ' + clientAddr + ': '+ msg);
	};
	log('WebSocket connection');
	log('Version ' + client.protocolVersion + ', subprotocol: ' + client.protocol);

	var target = net.createConnection(target_port,target_host, function() {
		log('connected to target');
	});
	target.on('data', function(data) {
		//log("sending message: " + data);
		try {
			client.send(data);
		} catch(e) {
			log("Client closed, cleaning up target");
			target.end();
		}
	});
	target.on('end', function() {
		log('target disconnected');
		client.close();
	});
	target.on('error', function() {
		log('target connection error');
		target.end();
		client.close();
	});

	client.on('message', function(msg) {
		//log('got message: ' + msg);
		target.write(msg);
	});
	client.on('close', function(code, reason) {
		log('WebSocket client disconnected: ' + code + ' [' + reason + ']');
		target.end();
	});
	client.on('error', function(a) {
		log('WebSocket client error: ' + a);
		target.end();
	});
};

// Send an HTTP error response
var http_error = function (response, code, msg) {
	response.writeHead(code, {"Content-Type": "text/plain"});
	response.write(msg + "\n");
	response.end();
	return;
}

// Process an HTTP static file request
var http_request = function (request, response) {
//    console.log("pathname: " + url.parse(req.url).pathname);
//    res.writeHead(200, {'Content-Type': 'text/plain'});
//    res.end('okay');

	if (! argv.web) {
		return http_error(response, 403, "403 Permission Denied");
	}

	var uri = url.parse(request.url).pathname
		, filename = path.join(argv.web, uri);

	fs.exists(filename, function(exists) {
		if(!exists) {
			return http_error(response, 404, "404 Not Found");
		}

		if (fs.statSync(filename).isDirectory()) {
			filename += '/index.html';
		}

		fs.readFile(filename, "binary", function(err, file) {
			if(err) {
				return http_error(response, 500, err);
			}

			response.writeHead(200);
			response.write(file, "binary");
			response.end();
		});
	});
};

function initWsServer(_argv) {
	var argv = _argv;
	var source_arg = argv.source;
	var target_arg = argv.target;
	// parse source and target arguments into parts
	try {
		var idx;
		idx = source_arg.indexOf(":");
		if (idx >= 0) {
			source_host = source_arg.slice(0, idx);
			source_port = parseInt(source_arg.slice(idx+1), 10);
		} else {
			source_host = "";
			source_port = parseInt(source_arg, 10);
		}

		idx = target_arg.indexOf(":");
		if (idx < 0) {
			throw("target must be host:port");
		}
		target_host = target_arg.slice(0, idx);
		target_port = parseInt(target_arg.slice(idx+1), 10);

		if (isNaN(source_port) || isNaN(target_port)) {
			throw("illegal port");
		}
	} catch(e) {
		console.error("websockify.js [--web web_dir] [--cert cert.pem [--key key.pem]] [source_addr:]source_port target_addr:target_port");
		process.exit(2);
	}

	console.log("WebSocket settings: ");
	console.log("    - proxying from " + source_host + ":" + source_port +
				" to " + target_host + ":" + target_port);
	if (argv.web) {
		console.log("    - Web server active. Serving: " + argv.web);
	}

	if (argv.cert) {
		argv.key = argv.key || argv.cert;
		var cert = fs.readFileSync(argv.cert),
			key = fs.readFileSync(argv.key);
		console.log("    - Running in encrypted HTTPS (wss://) mode using: " + argv.cert + ", " + argv.key);
		webServer = https.createServer({cert: cert, key: key}, http_request);
	} else {
		console.log("    - Running in unencrypted HTTP (ws://) mode");
		webServer = http.createServer(http_request);
	}
	webServer.listen(source_port, function() {
		wsServer = new WebSocketServer({server: webServer});
		wsServer.on('connection', new_client);
	});
}

module.exports = initWsServer;
<commit_msg>Fix bug caused by ws upgrade<commit_after>'use strict'

var net = require('net'),
	http = require('http'),
	https = require('https'),
	url = require('url'),
	path = require('path'),
	fs = require('fs'),

	Buffer = require('buffer').Buffer,
	WebSocketServer = require('ws').Server,

	webServer, wsServer,
	source_host, source_port, target_host, target_port,
	web_path = null,
	argv = null;

// Handle new WebSocket client
var new_client = function(client, req) {
	var clientAddr = client._socket.remoteAddress, log;
	console.log(req ? req.url : client.upgradeReq.url);
	log = function (msg) {
		console.log(' ' + clientAddr + ': '+ msg);
	};
	log('WebSocket connection');
	log('Version ' + client.protocolVersion + ', subprotocol: ' + client.protocol);

	var target = net.createConnection(target_port,target_host, function() {
		log('connected to target');
	});
	target.on('data', function(data) {
		//log("sending message: " + data);
		try {
			client.send(data);
		} catch(e) {
			log("Client closed, cleaning up target");
			target.end();
		}
	});
	target.on('end', function() {
		log('target disconnected');
		client.close();
	});
	target.on('error', function() {
		log('target connection error');
		target.end();
		client.close();
	});

	client.on('message', function(msg) {
		//log('got message: ' + msg);
		target.write(msg);
	});
	client.on('close', function(code, reason) {
		log('WebSocket client disconnected: ' + code + ' [' + reason + ']');
		target.end();
	});
	client.on('error', function(a) {
		log('WebSocket client error: ' + a);
		target.end();
	});
};

// Send an HTTP error response
var http_error = function (response, code, msg) {
	response.writeHead(code, {"Content-Type": "text/plain"});
	response.write(msg + "\n");
	response.end();
	return;
}

// Process an HTTP static file request
var http_request = function (request, response) {
//    console.log("pathname: " + url.parse(req.url).pathname);
//    res.writeHead(200, {'Content-Type': 'text/plain'});
//    res.end('okay');

	if (! argv.web) {
		return http_error(response, 403, "403 Permission Denied");
	}

	var uri = url.parse(request.url).pathname
		, filename = path.join(argv.web, uri);

	fs.exists(filename, function(exists) {
		if(!exists) {
			return http_error(response, 404, "404 Not Found");
		}

		if (fs.statSync(filename).isDirectory()) {
			filename += '/index.html';
		}

		fs.readFile(filename, "binary", function(err, file) {
			if(err) {
				return http_error(response, 500, err);
			}

			response.writeHead(200);
			response.write(file, "binary");
			response.end();
		});
	});
};

function initWsServer(_argv) {
	var argv = _argv;
	var source_arg = argv.source;
	var target_arg = argv.target;
	// parse source and target arguments into parts
	try {
		var idx;
		idx = source_arg.indexOf(":");
		if (idx >= 0) {
			source_host = source_arg.slice(0, idx);
			source_port = parseInt(source_arg.slice(idx+1), 10);
		} else {
			source_host = "";
			source_port = parseInt(source_arg, 10);
		}

		idx = target_arg.indexOf(":");
		if (idx < 0) {
			throw("target must be host:port");
		}
		target_host = target_arg.slice(0, idx);
		target_port = parseInt(target_arg.slice(idx+1), 10);

		if (isNaN(source_port) || isNaN(target_port)) {
			throw("illegal port");
		}
	} catch(e) {
		console.error("websockify.js [--web web_dir] [--cert cert.pem [--key key.pem]] [source_addr:]source_port target_addr:target_port");
		process.exit(2);
	}

	console.log("WebSocket settings: ");
	console.log("    - proxying from " + source_host + ":" + source_port +
				" to " + target_host + ":" + target_port);
	if (argv.web) {
		console.log("    - Web server active. Serving: " + argv.web);
	}

	if (argv.cert) {
		argv.key = argv.key || argv.cert;
		var cert = fs.readFileSync(argv.cert),
			key = fs.readFileSync(argv.key);
		console.log("    - Running in encrypted HTTPS (wss://) mode using: " + argv.cert + ", " + argv.key);
		webServer = https.createServer({cert: cert, key: key}, http_request);
	} else {
		console.log("    - Running in unencrypted HTTP (ws://) mode");
		webServer = http.createServer(http_request);
	}
	webServer.listen(source_port, function() {
		wsServer = new WebSocketServer({server: webServer});
		wsServer.on('connection', new_client);
	});
}

module.exports = initWsServer;<|endoftext|>