<commit_before>// Amara, universalsubtitles.org
//
// Copyright (C) 2013 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

var angular = angular || null;

(function() {

    var root = this;
    var $ = root.AmarajQuery;

    function AppController($scope, $timeout, LockService, VideoPlayer) {
        var minutesIdle = 0;
        var secondsUntilClosing = 120;
        var regainLockTimer;

        $scope.scrollingSynced = true;
        $scope.timelineShown = true;
        $scope.subtitlesHeight = 431;

        $scope.toggleScrollingSynced = function() {
            $scope.scrollingSynced = !$scope.scrollingSynced;
        }

        $scope.toggleTimelineShown = function() {
            $scope.timelineShown = !$scope.timelineShown
            if ($scope.timelineShown) {
                $scope.subtitlesHeight = 431;
            } else {
                $scope.subtitlesHeight = 366;
            }
        }

        function releaseLock() {
            LockService.releaseLock($scope.videoId, $scope.languageCode);
        }

        function regainLock() {
            LockService.regainLock($scope.videoId, $scope.languageCode)
                .then(function onSuccess(response) {
                if (response.data.ok) {
                    minutesIdle = 0;
                    $scope.$root.$emit('hide-modal');
                    startRegainLockTimer();
                    startUserIdleTimer();
                } else {
                    window.alert("Sorry, could not restart your session.");
                    window.location = '/videos/' + $scope.videoId + "/";
                }
            }, function onError() {
                window.alert("Sorry, could not restart your session.");
                window.location = '/videos/' + $scope.videoId + "/";
            });
        }

        function startUserIdleTimer() {
            var userIdleTimeout = function() {

                minutesIdle++;

                if (minutesIdle >= USER_IDLE_MINUTES) {
                    showIdleModal();
                    $timeout.cancel(regainLockTimer);
                } else {
                    $timeout(userIdleTimeout, 60 * 1000);
                }
            };

            $timeout(userIdleTimeout, 60 * 1000);
        }
        function startRegainLockTimer() {
            var regainLockTimeout = function() {
                regainLock();
                regainLockTimer = $timeout(regainLockTimeout, 15 * 1000);
            };

            regainLockTimer = $timeout(regainLockTimeout, 15 * 1000);

        }
        function showIdleModal() {

            var heading = "Warning: you've been idle for more than " + USER_IDLE_MINUTES + " minutes. " +
                "To ensure no work is lost we will close your session in ";

            var closeSessionTimeout;

            var closeSession = function() {

                secondsUntilClosing--;

                if (secondsUntilClosing <= 0) {

                    releaseLock();

                    $scope.$root.$emit("show-modal", {
                        heading: 'Your session has ended. You can try to resume, close the editor, or download your subtitles',
                        buttons: [
                            {'text': 'Try to resume work', 'class': 'yes', 'fn': function() {
                                // TODO: Remove this duplication from below.
                                if (closeSessionTimeout) {
                                    $timeout.cancel(closeSessionTimeout);
                                }

                                regainLock();
                            }},
                            {'text': 'Download subtitles', 'class': 'no', 'fn': function() {
                                $scope.$root.$emit('show-modal-download');
                            }},
                            {'text': 'Close editor', 'class': 'no', 'fn': function() {
                                window.location = '/videos/' + $scope.videoId + "/";
                            }}
                        ]
                    });

                } else {

                    $scope.$root.$emit('change-modal-heading', heading + secondsUntilClosing + " seconds.");
                    closeSessionTimeout = $timeout(closeSession, 1000);

                }
            };

            $scope.$root.$emit("show-modal", {
                heading: heading + secondsUntilClosing + " seconds.",
                buttons: [
                    {'text': 'Try to resume work', 'class': 'yes', 'fn': function() {
                        if (closeSessionTimeout) {
                            $timeout.cancel(closeSessionTimeout);
                        }

                        regainLock();
                    }}
                ]
            });

            closeSessionTimeout = $timeout(closeSession, 1000);
        }

        $scope.handleAppKeyDown = function(evt) {
            // Reset the lock timer.
            minutesIdle = 0;

            // Shortcuts that should work while editing a subtitle
            if (evt.keyCode === 32 && evt.shiftKey) {
                // Space with shift, toggle play / pause.
                evt.preventDefault();
                evt.stopPropagation();
                VideoPlayer.togglePlay();
            }
            else if(evt.target.type == 'textarea') {
                return;
            }
            // Shortcuts that should be disabled while editing a subtitle
            else if(evt.keyCode == 40) {
                // Down arrow, set the start time of the first
                // unsynced sub
                $scope.$root.$emit("sync-next-start-time");
                evt.preventDefault();
                evt.stopPropagation();
            } else if(evt.keyCode == 38) {
                // Up arrow, set the end time of the first
                // unsynced sub
                $scope.$root.$emit("sync-next-end-time");
                evt.preventDefault();
                evt.stopPropagation();
            }
        };

        $scope.handleAppMouseMove = function(evt) {
            // Reset the lock timer.
            minutesIdle = 0;
        };

        startUserIdleTimer();
        startRegainLockTimer();

        window.onunload = function() {
            releaseLock();
        }
    };

    root.AppController = AppController;

}).call(this);
<commit_msg>Fixes for locking code.<commit_after>// Amara, universalsubtitles.org
//
// Copyright (C) 2013 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

var angular = angular || null;

(function() {

    var root = this;
    var $ = root.AmarajQuery;

    function AppController($scope, $timeout, LockService, VideoPlayer) {
        var minutesIdle = 0;
        var secondsUntilClosing = 120;
        var regainLockTimer;

        $scope.scrollingSynced = true;
        $scope.timelineShown = true;
        $scope.subtitlesHeight = 431;

        $scope.toggleScrollingSynced = function() {
            $scope.scrollingSynced = !$scope.scrollingSynced;
        }

        $scope.toggleTimelineShown = function() {
            $scope.timelineShown = !$scope.timelineShown
            if ($scope.timelineShown) {
                $scope.subtitlesHeight = 431;
            } else {
                $scope.subtitlesHeight = 366;
            }
        }

        function releaseLock() {
            LockService.releaseLock($scope.videoId, $scope.languageCode);
        }

        function regainLock() {
            return LockService.regainLock($scope.videoId,
                    $scope.languageCode);
        }

        function startUserIdleTimer() {
            var userIdleTimeout = function() {

                minutesIdle++;

                if (minutesIdle >= USER_IDLE_MINUTES) {
                    showIdleModal();
                    $timeout.cancel(regainLockTimer);
                } else {
                    $timeout(userIdleTimeout, 60 * 1000);
                }
            };

            $timeout(userIdleTimeout, 60 * 1000);
        }
        function startRegainLockTimer() {
            var regainLockTimeout = function() {
                regainLock();
                regainLockTimer = $timeout(regainLockTimeout, 15 * 1000);
            };

            regainLockTimer = $timeout(regainLockTimeout, 15 * 1000);

        }

        function regainLockAfterIdle() {
            regainLock().then(function onSuccess(response) {
                if (response.data.ok) {
                    minutesIdle = 0;
                    $scope.$root.$emit('hide-modal');
                    startRegainLockTimer();
                    startUserIdleTimer();
                } else {
                    window.alert("Sorry, could not restart your session.");
                    window.location = '/videos/' + $scope.videoId + "/";
                }
            }, function onError() {
                window.alert("Sorry, could not restart your session.");
                window.location = '/videos/' + $scope.videoId + "/";
            });
        }
        function showIdleModal() {

            var heading = "Warning: you've been idle for more than " + USER_IDLE_MINUTES + " minutes. " +
                "To ensure no work is lost we will close your session in ";

            var closeSessionTimeout;

            var closeSession = function() {

                secondsUntilClosing--;

                if (secondsUntilClosing <= 0) {

                    releaseLock();

                    $scope.$root.$emit("show-modal", {
                        heading: 'Your session has ended. You can try to resume, close the editor, or download your subtitles',
                        buttons: [
                            {'text': 'Try to resume work', 'class': 'yes', 'fn': function() {
                                // TODO: Remove this duplication from below.
                                if (closeSessionTimeout) {
                                    $timeout.cancel(closeSessionTimeout);
                                }

                                regainLockAfterIdle();
                            }},
                            {'text': 'Download subtitles', 'class': 'no', 'fn': function() {
                                $scope.$root.$emit('show-modal-download');
                            }},
                            {'text': 'Close editor', 'class': 'no', 'fn': function() {
                                window.location = '/videos/' + $scope.videoId + "/";
                            }}
                        ]
                    });

                } else {

                    $scope.$root.$emit('change-modal-heading', heading + secondsUntilClosing + " seconds.");
                    closeSessionTimeout = $timeout(closeSession, 1000);

                }
            };

            $scope.$root.$emit("show-modal", {
                heading: heading + secondsUntilClosing + " seconds.",
                buttons: [
                    {'text': 'Try to resume work', 'class': 'yes', 'fn': function() {
                        if (closeSessionTimeout) {
                            $timeout.cancel(closeSessionTimeout);
                        }

                        regainLockAfterIdle();
                    }}
                ]
            });

            closeSessionTimeout = $timeout(closeSession, 1000);
        }

        $scope.handleAppKeyDown = function(evt) {
            // Reset the lock timer.
            minutesIdle = 0;

            // Shortcuts that should work while editing a subtitle
            if (evt.keyCode === 32 && evt.shiftKey) {
                // Space with shift, toggle play / pause.
                evt.preventDefault();
                evt.stopPropagation();
                VideoPlayer.togglePlay();
            }
            else if(evt.target.type == 'textarea') {
                return;
            }
            // Shortcuts that should be disabled while editing a subtitle
            else if(evt.keyCode == 40) {
                // Down arrow, set the start time of the first
                // unsynced sub
                $scope.$root.$emit("sync-next-start-time");
                evt.preventDefault();
                evt.stopPropagation();
            } else if(evt.keyCode == 38) {
                // Up arrow, set the end time of the first
                // unsynced sub
                $scope.$root.$emit("sync-next-end-time");
                evt.preventDefault();
                evt.stopPropagation();
            }
        };

        $scope.handleAppMouseMove = function(evt) {
            // Reset the lock timer.
            minutesIdle = 0;
        };

        startUserIdleTimer();
        startRegainLockTimer();

        window.onunload = function() {
            releaseLock();
        }
    };

    root.AppController = AppController;

}).call(this);
<|endoftext|>


<commit_before>var util = require('util'),
    EventEmitter = require('events').EventEmitter,
    net = require('net'),
    Parser = require('./Parser'),
    Message = require('./Message'),
    packageInfo = require('../package.json'),
    proxyParser = require('./proxyParser')

/**
 * Handles relp client communication
 * Will emit new messages and connection closes for the consumer to act on
 * If a connection closes, non ack'd messages should be dropped since they will be retransmitted
 *
 * @param {Object} options Configuration options for the server
 * @param {Number} options.port The port to listen for new connections
 * @param {String} [options.host] The host to listen for new connections on
 * @param {Server} [options.server] Configure your own server and provide it here
 *      You must call listen on the provided server object
 * @param {String} [options.connectionEvent='connection'] Change the event name for new connections.
 *      This is especially useful for TLS support since a TLS server emits 'secureConnection` events when
 *      a socket is ready
 * @param {Boolean} [options.proxyProtocol=false] True to expect PROXY protocol, false if not
 * @param {Object} [options.parser=Parser] A custom parser, if you must
 * @param {Object} [options.proxyParser=proxyParser] A custom PROXY protocol parser, if you must
 * @param {Object} [options.logger] A logger to use in place of console
 *
 * @constructor
 */
var Server = function (options) {
    Server.super_.call(this)

    var self = this

    self.options = options || {}
    self.logger = self.options.logger || console
    self.sockets = {}
    self._nextSocketId = 1

    self.paused = false
    self.server = self.options.server || net.createServer()

    self.server.on(self.options.connectionEvent || 'connection', function (socket) {
        self._setupConnection(socket)
    })

    self.server.on('error', function (error) {
        self.logger.error('node-relp', 'RELP Server Error', { error: error.stack || error })
    })

    if (!self.options.server) {
        self.server.listen(self.options.port, self.options.host)
    }
}

Server.RELP_VERSION = '0'
Server.RELP_SOFTWARE = 'node-' + packageInfo.name + ',' + packageInfo.version + ',' + packageInfo.repository.url
Server.RELP_COMMANDS = ['open', 'close', 'syslog']

util.inherits(Server, EventEmitter)
module.exports = Server

/**
 * Acknowledges the receipt of a relp message to the relp client
 * Messages with a transaction id below 1 will not be ack'd
 * Messages that have already been ack'd will not be re-ack'd
 *
 * @param {Message} message A relp message to ack
 * @param {String} [data='200 OK'] Data to send
 * @param {boolean} [close=false] Whether or not to close the socket after sending
 *
 * @returns {boolean} True if the message was ack'd, false if not
 */
Server.prototype.ack = function (message, data, close) {
    var self = this,
        socket = self.sockets[message.socketId],
        actualData = data || '200 OK'

    if (!socket || message.transactionId < 1 || message.acked) {
        return false
    }

    var ackMessage = new Message(message.transactionId, 'rsp', actualData),
        buffer = socket.parser.serialize(ackMessage)

    if (close) {
        socket.end(buffer)
    } else {
        socket.write(buffer)
    }

    message.acked = true
    return true
}

/**
 * Negatively acknowledges the receipt of a relp message to the relp client
 * This should result in receiving the message again from the client
 *
 * @param {Message} message A relp message to ack
 * @param {String} [error='Error'] Error message to send
 * @param {boolean} [close=false] Whether or not to close the socket after sending
 *
 * @returns {boolean} True if the message was nack'd, false if not
 */
Server.prototype.nack = function (message, error, close) {
    var useError = error || 'Error'
    return this.ack(message, '500 ' + useError, close)
}

/**
 * Pauses all existing and future connections
 * You must resume before seeing anymore messages
 *
 * @return {boolean} True on success, false if already paused
 */
Server.prototype.pause = function () {
    if (this.paused) {
        return false
    }

    for (var socketId in this.sockets) {
        this.sockets[socketId].pause()
    }

    this.paused = true
    return true
}

/**
 * Resumes a previously paused server
 * Expect a flood of messages!
 *
 * @returns {boolean} True on success, false if not already paused
 */
Server.prototype.resume = function () {
    if (!this.paused) {
        return false
    }

    for (var socketId in this.sockets) {
        this.sockets[socketId].resume()
    }

    this.paused = false
    return true
}

/**
 * Closes each open connection and finally the server
 */
Server.prototype.close = function () {
    this.server.close()

    for (var socketId in this.sockets) {
        this.sockets[socketId].end()
    }
}

/**
 * Dispatches a message to the proper handler
 *
 * @param {Message} message The message to handle
 *
 * @private
 */
Server.prototype._handleMessage = function (message) {
    var self = this,
        socket = this.sockets[message.socketId]

    switch (message.command) {
        case 'syslog':
            //Are we getting messages before the handshake?
            if (socket.relpHandshakeComplete === false) {
                //TODO: emit?
                self.logger.error(
		    'node-relp',
                    'Connection #' + message.socketId + ' (' + self.getAddress(socket) + ') sent a syslog message before the handshake, disconnecting'
                )

                return socket.destroy()
            }

            this.emit('message', message)
            break

        case 'open':
            this._handleOpen(message)
            break

        case 'close':
            this._handleClose(message)
            break

        default:
            //TODO: emit?
            self.logger.error('node-relp', 'Connection #' + message.socketId + ' (' + self.getAddress(socket) + ') sent an unknown command: ' + message.command)
            return socket.destroy()
    }
}

/**
 * Handles the open message
 * This is what starts a relp session
 *
 * @param {Message} message The open message to work from
 *
 * @private
 */
Server.prototype._handleOpen = function (message) {
    var self = this
      , rawOffers = message.body.split('\n')
      , offers = {}
      , body

    rawOffers.forEach(function (rawOffer) {
        var parts = rawOffer.split('=')
        offers[parts[0]] = parts[1]
    })

    if (offers['relp_version'] !== Server.RELP_VERSION) {
        body = '500 Insufficient version\n'
            + Server.RELP_VERSION + ' required, ' + offers['relp_version'] + ' provided'
        return self.ack(message, body, true)
    }

    if (!offers['commands']) {
        body = '500 No commands provided'
        return self.ack(message, body, true)
    }

    offers['commands'].split(',').some(function (command) {
        if (Server.RELP_COMMANDS.indexOf(command.trim().toLowerCase()) === -1) {
            body = '500 Invalid command\n'
                + command + ' is not supported'

            return self.ack(message, body, true)
        }
    })

    body = '200 OK '
        + 'relp_version=' + Server.RELP_VERSION + '\n'
        + 'relp_software=' + Server.RELP_SOFTWARE + '\n'
        + 'commands=' + offers['commands']

    self.ack(message, body)
    self.sockets[message.socketId].relpHandshakeComplete = true
}

/**
 * Handles a close message
 * Acks the message and closes the socket
 *
 * @param {Message} message The close message to work from
 *
 * @private
 */
Server.prototype._handleClose = function (message) {
    this.ack(message, '', true)
}

/**
 * Sets up a connection to be managed
 * Handles new messages, socket closes/opens, etc
 *
 * @param {Socket} socket The socket to handle events for
 *
 * @private
 */
Server.prototype._setupConnection = function (socket) {
    if (this.paused) {
        socket.pause()
    }

    var self = this,
        useParser = self.options.parser || Parser,
        useProxyParser = self.options.proxyParser || proxyParser

    socket.socketId = self._nextSocketId++
    self.sockets[socket.socketId] = socket

    socket.parser = new useParser()

    socket.proxyHandshakeComplete = false
    socket.relpHandshakeComplete = false

    socket.parser.on('error', function (error) {
        self.logger.error('node-relp', 'Connection #' + socket.socketId + ' (' + self.getAddress(socket) + ') had a parse error, disconnecting.', { error: error.stack || error })
        socket.destroy()
    })

    socket.parser.on('message', function (message) {
        message.socketId = socket.socketId
        message.remoteAddress = socket.remoteAddress
        message.proxyDetails = socket.proxyDetails
        self._handleMessage(message)
    })

    socket.on('data', function (data) {
        if (self.options.proxyProtocol && socket.proxyHandshakeComplete === false) {
            var result = useProxyParser.consume(data)
            socket.proxyHandshakeComplete = true
            socket.proxyDetails = result.proxy
            data = result.buffer
        }

        socket.parser.consume(data)
    })

    socket.on('error', function (error) {
        self.logger.error('node-relp', 'Connection #' + socket.socketId + ' (' + self.getAddress(socket) + ') had an error.', { error: error.stack || error })
        socket.destroy()
    })

    socket.on('close', function () {
        /**
         * Notifies consumers that a client connection has closed and is about to be unreferenced
         *
         * @event Server#connectionClosed
         * @type {Socket} The socket that closed
         * @property {Number} socketId The id of the socket that was closed, any non ack'd message with this
         *      socketId should stop processing to avoid duplicates
         */
        self.emit('connectionClosed', socket)

        delete self.sockets[socket.socketId]
    })

    /**
     * Notifies consumers that a new client connection has opened
     *
     * @event Server#connectionOpened
     * @type {Socket} The socket that opened
     * @property {Number} socketId the id of the socket that just opened, all messages received on this connection
     *      will contain this socketId
     */
    self.emit('connectionOpened', socket)
}

/**
 * Gets the address to log for a socket
 *
 * @param {net.Socket} socket
 */
Server.prototype.getAddress = function (socket) {
    var str = socket.remoteAddress
    if (socket.hasOwnProperty('proxyDetails')) {
        str = socket.proxyDetails.clientIp + ', ' + str
    }
    return str
}
<commit_msg>Don't allow new connections in if we are overloaded as is<commit_after>var util = require('util'),
    EventEmitter = require('events').EventEmitter,
    net = require('net'),
    Parser = require('./Parser'),
    Message = require('./Message'),
    packageInfo = require('../package.json'),
    proxyParser = require('./proxyParser')

/**
 * Handles relp client communication
 * Will emit new messages and connection closes for the consumer to act on
 * If a connection closes, non ack'd messages should be dropped since they will be retransmitted
 *
 * @param {Object} options Configuration options for the server
 * @param {Number} options.port The port to listen for new connections
 * @param {String} [options.host] The host to listen for new connections on
 * @param {Server} [options.server] Configure your own server and provide it here
 *      You must call listen on the provided server object
 * @param {String} [options.connectionEvent='connection'] Change the event name for new connections.
 *      This is especially useful for TLS support since a TLS server emits 'secureConnection` events when
 *      a socket is ready
 * @param {Boolean} [options.proxyProtocol=false] True to expect PROXY protocol, false if not
 * @param {Object} [options.parser=Parser] A custom parser, if you must
 * @param {Object} [options.proxyParser=proxyParser] A custom PROXY protocol parser, if you must
 * @param {Object} [options.logger] A logger to use in place of console
 *
 * @constructor
 */
var Server = function (options) {
    Server.super_.call(this)

    var self = this

    self.options = options || {}
    self.logger = self.options.logger || console
    self.sockets = {}
    self._nextSocketId = 1

    self.paused = false
    self.server = self.options.server || net.createServer()

    self.server.on(self.options.connectionEvent || 'connection', function (socket) {
        self._setupConnection(socket)
    })

    self.server.on('error', function (error) {
        self.logger.error('node-relp', 'RELP Server Error', { error: error.stack || error })
    })

    if (!self.options.server) {
        self.server.listen(self.options.port, self.options.host)
    }
}

Server.RELP_VERSION = '0'
Server.RELP_SOFTWARE = 'node-' + packageInfo.name + ',' + packageInfo.version + ',' + packageInfo.repository.url
Server.RELP_COMMANDS = ['open', 'close', 'syslog']

util.inherits(Server, EventEmitter)
module.exports = Server

/**
 * Acknowledges the receipt of a relp message to the relp client
 * Messages with a transaction id below 1 will not be ack'd
 * Messages that have already been ack'd will not be re-ack'd
 *
 * @param {Message} message A relp message to ack
 * @param {String} [data='200 OK'] Data to send
 * @param {boolean} [close=false] Whether or not to close the socket after sending
 *
 * @returns {boolean} True if the message was ack'd, false if not
 */
Server.prototype.ack = function (message, data, close) {
    var self = this,
        socket = self.sockets[message.socketId],
        actualData = data || '200 OK'

    if (!socket || message.transactionId < 1 || message.acked) {
        return false
    }

    var ackMessage = new Message(message.transactionId, 'rsp', actualData),
        buffer = socket.parser.serialize(ackMessage)

    if (close) {
        socket.end(buffer)
    } else {
        socket.write(buffer)
    }

    message.acked = true
    return true
}

/**
 * Negatively acknowledges the receipt of a relp message to the relp client
 * This should result in receiving the message again from the client
 *
 * @param {Message} message A relp message to ack
 * @param {String} [error='Error'] Error message to send
 * @param {boolean} [close=false] Whether or not to close the socket after sending
 *
 * @returns {boolean} True if the message was nack'd, false if not
 */
Server.prototype.nack = function (message, error, close) {
    var useError = error || 'Error'
    return this.ack(message, '500 ' + useError, close)
}

/**
 * Pauses all existing and future connections
 * You must resume before seeing anymore messages
 *
 * @return {boolean} True on success, false if already paused
 */
Server.prototype.pause = function () {
    if (this.paused) {
        return false
    }

    for (var socketId in this.sockets) {
        this.sockets[socketId].pause()
    }

    this.paused = true
    return true
}

/**
 * Resumes a previously paused server
 * Expect a flood of messages!
 *
 * @returns {boolean} True on success, false if not already paused
 */
Server.prototype.resume = function () {
    if (!this.paused) {
        return false
    }

    for (var socketId in this.sockets) {
        this.sockets[socketId].resume()
    }

    this.paused = false
    return true
}

/**
 * Closes each open connection and finally the server
 */
Server.prototype.close = function () {
    this.server.close()

    for (var socketId in this.sockets) {
        this.sockets[socketId].end()
    }
}

/**
 * Dispatches a message to the proper handler
 *
 * @param {Message} message The message to handle
 *
 * @private
 */
Server.prototype._handleMessage = function (message) {
    var self = this,
        socket = this.sockets[message.socketId]

    switch (message.command) {
        case 'syslog':
            //Are we getting messages before the handshake?
            if (socket.relpHandshakeComplete === false) {
                //TODO: emit?
                self.logger.error(
		    'node-relp',
                    'Connection #' + message.socketId + ' (' + self.getAddress(socket) + ') sent a syslog message before the handshake, disconnecting'
                )

                return socket.destroy()
            }

            this.emit('message', message)
            break

        case 'open':
            this._handleOpen(message)
            break

        case 'close':
            this._handleClose(message)
            break

        default:
            //TODO: emit?
            self.logger.error('node-relp', 'Connection #' + message.socketId + ' (' + self.getAddress(socket) + ') sent an unknown command: ' + message.command)
            return socket.destroy()
    }
}

/**
 * Handles the open message
 * This is what starts a relp session
 *
 * @param {Message} message The open message to work from
 *
 * @private
 */
Server.prototype._handleOpen = function (message) {
    var self = this
      , rawOffers = message.body.split('\n')
      , offers = {}
      , body

    rawOffers.forEach(function (rawOffer) {
        var parts = rawOffer.split('=')
        offers[parts[0]] = parts[1]
    })

    if (offers['relp_version'] !== Server.RELP_VERSION) {
        body = '500 Insufficient version\n'
            + Server.RELP_VERSION + ' required, ' + offers['relp_version'] + ' provided'
        return self.ack(message, body, true)
    }

    if (!offers['commands']) {
        body = '500 No commands provided'
        return self.ack(message, body, true)
    }

    offers['commands'].split(',').some(function (command) {
        if (Server.RELP_COMMANDS.indexOf(command.trim().toLowerCase()) === -1) {
            body = '500 Invalid command\n'
                + command + ' is not supported'

            return self.ack(message, body, true)
        }
    })

    body = '200 OK '
        + 'relp_version=' + Server.RELP_VERSION + '\n'
        + 'relp_software=' + Server.RELP_SOFTWARE + '\n'
        + 'commands=' + offers['commands']

    self.ack(message, body)
    self.sockets[message.socketId].relpHandshakeComplete = true
}

/**
 * Handles a close message
 * Acks the message and closes the socket
 *
 * @param {Message} message The close message to work from
 *
 * @private
 */
Server.prototype._handleClose = function (message) {
    this.ack(message, '', true)
}

/**
 * Sets up a connection to be managed
 * Handles new messages, socket closes/opens, etc
 *
 * @param {Socket} socket The socket to handle events for
 *
 * @private
 */
Server.prototype._setupConnection = function (socket) {
    if (this.paused) {
        socket.destroy()
        return
    }

    var self = this,
        useParser = self.options.parser || Parser,
        useProxyParser = self.options.proxyParser || proxyParser

    socket.socketId = self._nextSocketId++
    self.sockets[socket.socketId] = socket

    socket.parser = new useParser()

    socket.proxyHandshakeComplete = false
    socket.relpHandshakeComplete = false

    socket.parser.on('error', function (error) {
        self.logger.error('node-relp', 'Connection #' + socket.socketId + ' (' + self.getAddress(socket) + ') had a parse error, disconnecting.', { error: error.stack || error })
        socket.destroy()
    })

    socket.parser.on('message', function (message) {
        message.socketId = socket.socketId
        message.remoteAddress = socket.remoteAddress
        message.proxyDetails = socket.proxyDetails
        self._handleMessage(message)
    })

    socket.on('data', function (data) {
        if (self.options.proxyProtocol && socket.proxyHandshakeComplete === false) {
            var result = useProxyParser.consume(data)
            socket.proxyHandshakeComplete = true
            socket.proxyDetails = result.proxy
            data = result.buffer
        }

        socket.parser.consume(data)
    })

    socket.on('error', function (error) {
        self.logger.error('node-relp', 'Connection #' + socket.socketId + ' (' + self.getAddress(socket) + ') had an error.', { error: error.stack || error })
        socket.destroy()
    })

    socket.on('close', function () {
        /**
         * Notifies consumers that a client connection has closed and is about to be unreferenced
         *
         * @event Server#connectionClosed
         * @type {Socket} The socket that closed
         * @property {Number} socketId The id of the socket that was closed, any non ack'd message with this
         *      socketId should stop processing to avoid duplicates
         */
        self.emit('connectionClosed', socket)

        delete self.sockets[socket.socketId]
    })

    /**
     * Notifies consumers that a new client connection has opened
     *
     * @event Server#connectionOpened
     * @type {Socket} The socket that opened
     * @property {Number} socketId the id of the socket that just opened, all messages received on this connection
     *      will contain this socketId
     */
    self.emit('connectionOpened', socket)
}

/**
 * Gets the address to log for a socket
 *
 * @param {net.Socket} socket
 */
Server.prototype.getAddress = function (socket) {
    var str = socket.remoteAddress
    if (socket.hasOwnProperty('proxyDetails')) {
        str = socket.proxyDetails.clientIp + ', ' + str
    }
    return str
}
<|endoftext|>


<commit_before>const vocab = require('solid-namespace')

/**
 * ACL Group Listing
 * @see https://github.com/solid/web-access-control-spec#groups-of-agents
 * @class GroupListing
 */
class GroupListing {
  /**
   * @constructor
   * @param [options={}] {Object} Options hashmap
   * @param [options.uri] {string|NamedNode} Group URI as appears in ACL file
   *   (e.g. `https://example.com/groups#management`)
   * @param [options.uid] {string} Value of `vcard:hasUID` object
   * @param [options.members={}] {Object} Hashmap of group members, by webId
   * @param [options.listing] {string|NamedNode} Group listing document URI
   * @param [options.rdf] {RDF} RDF library
   * @param [options.graph] {Graph} Parsed graph of the group listing document
   */
  constructor (options = {}) {
    this.uri = options.uri
    this.uid = options.uid
    this.members = options.members || {}
    this.listing = options.listing
    this.rdf = options.rdf
    this.graph = options.graph
    if (this.rdf && this.graph) {
      this.initFromGraph(this.graph, this.rdf)
    }
  }

  /**
   * Factory function, returns a group listing, loaded and initialized
   * with the graph from its uri. Will return null if parsing fails,
   * which can be used to deny access to the resource.
   * @static
   * @param uri {string}
   * @param fetchGraph {Function}
   * @param rdf {RDF}
   * @param options {Object} Options hashmap, passed through to fetchGraph()
   * @return {Promise<GroupListing>}
   */
  static loadFrom (uri, fetchGraph, rdf, options = {}) {
    let group = new GroupListing({ uri, rdf })
    return fetchGraph(uri, options)
      .then(graph => {
        return group.initFromGraph(uri, graph)
      })
      .catch(err => {
	// Returning null will result in deny, which is suitable in this case
        console.error(err)
        return null
      })
  }

  /**
   * Adds a member's web id uri to the listing
   * @param webId {string|NamedNode}
   * @return {GroupListing} Chainable
   */
  addMember (webId) {
    if (webId.value) {
      webId = webId.value
    }
    this.members[webId] = true
    return this
  }

  /**
   * Returns the number of members in this listing
   * @return {Number}
   */
  get count () {
    return Object.keys(this.members).length
  }

  /**
   * Tests if a webId uri is present in the members list
   * @param webId {string|NamedNode}
   * @return {Boolean}
   */
  hasMember (webId) {
    if (webId.value) {
      webId = webId.value
    }
    return this.members[webId]
  }

  /**
   * @method initFromGraph
   * @param [uri] {string|NamedNode} Group URI as appears in ACL file
   *   (e.g. `https://example.com/groups#management`)
   * @param [graph] {Graph} Parsed graph
   * @param [rdf] {RDF}
   * @throws {Error}
   * @return {GroupListing} Chainable
   */
  initFromGraph (uri = this.uri, graph = this.graph, rdf = this.rdf) {
    if (!uri) {
      throw new Error('Group URI required to init from graph')
    }
    if (!graph || !rdf) {
      throw new Error('initFromGraph() - graph or rdf library missing')
    }
    let ns = vocab(rdf)
    let group = rdf.namedNode(uri)
    let rdfType = graph.any(group, null, ns.vcard('Group'))
    if (!rdfType) {
      console.warn(`Possibly invalid group '${uri}', missing type vcard:Group`)
    }
    this.uid = graph.anyValue(group, ns.vcard('hasUID'))
    graph.match(group, ns.vcard('hasMember'))
      .forEach(memberMatch => {
        this.addMember(memberMatch.object)
      })
    return this
  }
}

module.exports = GroupListing
<commit_msg>Use a Set for members of a group<commit_after>const vocab = require('solid-namespace')

/**
 * ACL Group Listing
 * @see https://github.com/solid/web-access-control-spec#groups-of-agents
 * @class GroupListing
 */
class GroupListing {
  /**
   * @constructor
   * @param [options={}] {Object} Options hashmap
   * @param [options.uri] {string|NamedNode} Group URI as appears in ACL file
   *   (e.g. `https://example.com/groups#management`)
   * @param [options.uid] {string} Value of `vcard:hasUID` object
   * @param [options.members={}] {Set} Set of group members, by webId
   * @param [options.listing] {string|NamedNode} Group listing document URI
   * @param [options.rdf] {RDF} RDF library
   * @param [options.graph] {Graph} Parsed graph of the group listing document
   */
  constructor (options = {}) {
    this.uri = options.uri
    this.uid = options.uid
    this.members = options.members || new Set()
    this.listing = options.listing
    this.rdf = options.rdf
    this.graph = options.graph
    if (this.rdf && this.graph) {
      this.initFromGraph(this.graph, this.rdf)
    }
  }

  /**
   * Factory function, returns a group listing, loaded and initialized
   * with the graph from its uri. Will return null if parsing fails,
   * which can be used to deny access to the resource.
   * @static
   * @param uri {string}
   * @param fetchGraph {Function}
   * @param rdf {RDF}
   * @param options {Object} Options hashmap, passed through to fetchGraph()
   * @return {Promise<GroupListing>}
   */
  static loadFrom (uri, fetchGraph, rdf, options = {}) {
    let group = new GroupListing({ uri, rdf })
    return fetchGraph(uri, options)
      .then(graph => {
        return group.initFromGraph(uri, graph)
      })
      .catch(err => {
	// Returning null will result in deny, which is suitable in this case
        console.error(err)
        return null
      })
  }

  /**
   * Adds a member's web id uri to the listing
   * @param webId {string|NamedNode}
   * @return {GroupListing} Chainable
   */
  addMember (webId) {
    if (webId.value) {
      webId = webId.value
    }
    this.members.add(webId)
    return this
  }

  /**
   * Returns the number of members in this listing
   * @return {Number}
   */
  get count () {
    return this.members.size
  }

  /**
   * Tests if a webId uri is present in the members list
   * @param webId {string|NamedNode}
   * @return {Boolean}
   */
  hasMember (webId) {
    if (webId.value) {
      webId = webId.value
    }
    return this.members.has(webId)
  }

  /**
   * @method initFromGraph
   * @param [uri] {string|NamedNode} Group URI as appears in ACL file
   *   (e.g. `https://example.com/groups#management`)
   * @param [graph] {Graph} Parsed graph
   * @param [rdf] {RDF}
   * @throws {Error}
   * @return {GroupListing} Chainable
   */
  initFromGraph (uri = this.uri, graph = this.graph, rdf = this.rdf) {
    if (!uri) {
      throw new Error('Group URI required to init from graph')
    }
    if (!graph || !rdf) {
      throw new Error('initFromGraph() - graph or rdf library missing')
    }
    let ns = vocab(rdf)
    let group = rdf.namedNode(uri)
    let rdfType = graph.any(group, null, ns.vcard('Group'))
    if (!rdfType) {
      console.warn(`Possibly invalid group '${uri}', missing type vcard:Group`)
    }
    this.uid = graph.anyValue(group, ns.vcard('hasUID'))
    graph.match(group, ns.vcard('hasMember'))
      .forEach(memberMatch => {
        this.addMember(memberMatch.object)
      })
    return this
  }
}

module.exports = GroupListing
<|endoftext|>


<commit_before>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2014 pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.settings.translations");
pimcore.settings.translations = Class.create({

    filterField: null,
    preconfiguredFilter: "",

    initialize: function (filter) {

        this.filterField = new Ext.form.TextField({
            xtype: "textfield",
            width: 200,
            style: "margin: 0 10px 0 0;",
            enableKeyEvents: true,
            value: this.preconfiguredFilter,
            listeners: {
                "keydown" : function (field, key) {
                    if (key.getKey() == key.ENTER) {
                        var input = field;
                        this.store.baseParams.searchString = input.getValue();
                        this.store.load();
                    }
                }.bind(this)
            }
        });

        this.preconfiguredFilter = filter;
        this.filterField.setValue(filter);
        this.getAvailableLanguages();
    },

    getRowEditor: function () {

        var StateFullProvider = Ext.extend(Ext.state.Provider, {
            namespace: "default",

            constructor : function(config){
                StateFullProvider.superclass.constructor.call(this);
                Ext.apply(this, config);

                var data = localStorage.getItem(this.namespace);
                if (!data) {
                    this.state = {};
                } else {
                    data = JSON.parse(data);
                    if (data.state && data.user == pimcore.currentuser.id) {
                        this.state = data.state;
                    } else {
                        this.state = {};
                    }
                }
            },

            get : function(name, defaultValue){
                try {
                    if (typeof this.state[name] == "undefined") {
                        return defaultValue
                    } else {
                        return this.decodeValue(this.state[name])
                    }
                } catch (e) {
                    this.clear(name);
                    return defaultValue;
                }
            },
            set : function(name, value){
                try {
                    if (typeof value == "undefined" || value === null) {
                        this.clear(name);
                        return;
                    }
                    this.state[name] = this.encodeValue(value)

                    var data = {
                        state: this.state,
                        user: pimcore.currentuser.id
                    };
                    var json = JSON.stringify(data);

                    localStorage.setItem(this.namespace, json);
                } catch (e) {
                    this.clear(name);
                }

                this.fireEvent("statechange", this, name, value);
            }
        });

        var stateId = "tr_" + this.translationType;
        var applyInitialSettings = false;
        var showInfo = false;
        var state = provider.get(stateId, null);
        var languages = this.languages;

        var maxCols = 7;   // include creation date / modification date / action column)
        var maxLanguages = maxCols - 3;

        if (state == null) {
            applyInitialSettings = true;
            if (languages.length > maxLanguages) {
                showInfo = true;
            }
        } else {
            if (state.columns) {
                for (var i = 0; i < state.columns.length; i++) {
                    var colState = state.columns[i];
                    if (colState.hidden) {
                        showInfo = true;
                        break;
                    }
                }
            }
        }

        var proxy = new Ext.data.HttpProxy({
            url: this.dataUrl,
            method: 'post'
        });

        var readerFields = [
            {name: 'key', allowBlank: false},
            {name: 'creationDate', allowBlank: true},
            {name: 'modificationDate', allowBlank: true}
        ];

        var typesColumns = [
            {header: t("key"), sortable: true, dataIndex: 'key', editable: false}

        ];

        for (var i = 0; i < languages.length; i++) {

            readerFields.push({name: languages[i]});
            var columnConfig = {header: pimcore.available_languages[languages[i]], sortable: false, dataIndex: languages[i],
                editor: new Ext.form.TextField({}), id: "translation_column_" + languages[i].toLowerCase()};
            if (applyInitialSettings) {
                var hidden = i >= maxLanguages;
                columnConfig.hidden = hidden;
            }

            typesColumns.push(columnConfig);
        }

        if (showInfo) {
            pimcore.helpers.showNotification(t("info"), t("there_are_more_columns"), null, null, 2000);
        }

        typesColumns.push({header: t("creationDate"), sortable: true, dataIndex: 'creationDate', editable: false,
            renderer: function(d) {
                var date = new Date(d * 1000);
                return date.format("Y-m-d H:i:s");
            }});
        typesColumns.push({header: t("modificationDate"), sortable: true, dataIndex: 'modificationDate', editable: false,
            renderer: function(d) {
                var date = new Date(d * 1000);
                return date.format("Y-m-d H:i:s");
            }});

        typesColumns.push({
            xtype: 'actioncolumn',
            width: 30,
            items: [{
                tooltip: t('delete'),
                icon: "/pimcore/static/img/icon/cross.png",
                handler: function (grid, rowIndex) {
                    grid.getStore().removeAt(rowIndex);
                }.bind(this)
            }]
        });

        var reader = new Ext.data.JsonReader({
            totalProperty: 'total',
            successProperty: 'success',
            root: 'data',
            idProperty: 'key'
        }, readerFields);

        var writer = new Ext.data.JsonWriter();

        var itemsPerPage = 20;
        this.store = new Ext.data.Store({
            id: 'translation_store',
            restful: false,
            proxy: proxy,
            reader: reader,
            writer: writer,
            remoteSort: true,
            baseParams: {
                limit: itemsPerPage,
                searchString: this.preconfiguredFilter
            },
            listeners: {
                write : function(store, action, result, response, rs) {
                }
            }
        });

        this.pagingtoolbar = new Ext.PagingToolbar({
            pageSize: itemsPerPage,
            store: this.store,
            displayInfo: true,
            displayMsg: '{0} - {1} / {2}',
            emptyMsg: t("no_items_found")
        });

        // add per-page selection
        this.pagingtoolbar.add("-");

        this.pagingtoolbar.add(new Ext.Toolbar.TextItem({
            text: t("items_per_page")
        }));
        this.pagingtoolbar.add(new Ext.form.ComboBox({
            store: [
                [10, "10"],
                [20, "20"],
                [40, "40"],
                [60, "60"],
                [80, "80"],
                [100, "100"]
            ],
            mode: "local",
            width: 50,
            value: 20,
            triggerAction: "all",
            listeners: {
                select: function (box, rec, index) {
                    this.pagingtoolbar.pageSize = intval(rec.data.field1);
                    this.pagingtoolbar.moveFirst();
                }.bind(this)
            }
        }));

        // filters
        this.gridFilters = new Ext.ux.grid.GridFilters({
            encode: true,
            local: false,
            filters: [{
                type: "date",
                dataIndex: "creationDate"
            },{
                type: "date",
                dataIndex: "modificationDate"
            }
            ]
        });

        this.grid = new Ext.grid.EditorGridPanel({
            frame: false,
            autoScroll: true,
            store: this.store,
            columnLines: true,
            stripeRows: true,
            plugins: [this.gridFilters],
            columns : typesColumns,
            trackMouseOver: true,
            bbar: this.pagingtoolbar,
            stateful: true,
            stateId: stateId,
            stateEvents: ['columnmove', 'columnresize', 'sortchange', 'groupchange'],
            sm: new Ext.grid.RowSelectionModel({singleSelect:true}),
            tbar: [
                {
                    text: t('add'),
                    handler: this.onAdd.bind(this),
                    iconCls: "pimcore_icon_add"
                },
                '-',{
                    text: this.getHint(),
                    xtype: "tbtext",
                    style: "margin: 0 10px 0 0;"
                },
                "->",
                {
                    text: t('cleanup'),
                    handler: this.cleanup.bind(this),
                    iconCls: "pimcore_icon_cleanup"
                },
                "-",
                {
                    text: t('merge_csv'),
                    handler: this.doMerge.bind(this),
                    iconCls: "pimcore_icon_merge"
                },
                "-",
                {
                    text: t('import_csv'),
                    handler: this.doImport.bind(this),
                    iconCls: "pimcore_icon_import"
                },
                '-',
                {
                    text: t('export_csv'),
                    handler: this.doExport.bind(this),
                    iconCls: "pimcore_icon_export"
                },'-',{
                    text: t("filter") + "/" + t("search"),
                    xtype: "tbtext",
                    style: "margin: 0 10px 0 0;"
                },this.filterField
            ],
            viewConfig: {
                forceFit: true
            }
        });

        this.store.load();

        return this.grid;
    },

    doImport:function(){
        pimcore.helpers.uploadDialog(this.importUrl, "Filedata", function() {
            this.store.reload();
        }.bind(this), function () {
            Ext.MessageBox.alert(t("error"), t("error"));
        });
    },

    doMerge:function(){
        pimcore.helpers.uploadDialog(this.mergeUrl, "Filedata", function(result) {
            var data = result.response.responseText;
            data = Ext.decode(data);

            var merger = new pimcore.settings.translation.translationmerger(this.translationType, data, this);
            this.refresh();
        }.bind(this), function () {
            Ext.MessageBox.alert(t("error"), t("error"));
        });
    },

    refresh: function() {
        this.store.reload();
    },

    doExport:function(){

        var filtersActive = this.filterField.getValue() || this.gridFilters.getFilterData().length;
        if(filtersActive) {
            Ext.MessageBox.confirm("", t("filter_active_message"), function (buttonValue) {
                if (buttonValue == "yes") {
                    var queryString = "searchString=" + this.filterField.getValue();
                    queryString += "&" + Ext.urlEncode(this.gridFilters.buildQuery(this.gridFilters.getFilterData()));
                    pimcore.helpers.download(Ext.urlAppend(this.exportUrl, queryString));
                } else {
                    pimcore.helpers.download(this.exportUrl);
                }
            }.bind(this));
        } else {
            pimcore.helpers.download(this.exportUrl);
        }
    },

    onAdd: function (btn, ev) {

        Ext.MessageBox.prompt("", t("please_enter_the_new_name"), function (button, value) {
            if(button == "ok") {
                var u = new this.grid.store.recordType();
                u.set("key", value);
                u.markDirty();

                this.grid.store.insert(0, u);
            }

        }.bind(this));
    },

    cleanup: function () {
        Ext.Ajax.request({
            url: this.cleanupUrl,
            success: function (response) {
                this.store.reload();
            }.bind(this)
        });
    }
});
<commit_msg>extjs3.4 - reverted accidental state provider change<commit_after>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2014 pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.settings.translations");
pimcore.settings.translations = Class.create({

    filterField: null,
    preconfiguredFilter: "",

    initialize: function (filter) {

        this.filterField = new Ext.form.TextField({
            xtype: "textfield",
            width: 200,
            style: "margin: 0 10px 0 0;",
            enableKeyEvents: true,
            value: this.preconfiguredFilter,
            listeners: {
                "keydown" : function (field, key) {
                    if (key.getKey() == key.ENTER) {
                        var input = field;
                        this.store.baseParams.searchString = input.getValue();
                        this.store.load();
                    }
                }.bind(this)
            }
        });

        this.preconfiguredFilter = filter;
        this.filterField.setValue(filter);
        this.getAvailableLanguages();
    },

    getRowEditor: function () {

        var StateFullProvider = Ext.extend(Ext.state.Provider, {
            namespace: "default",

            constructor : function(config){
                StateFullProvider.superclass.constructor.call(this);
                Ext.apply(this, config);

                var data = localStorage.getItem(this.namespace);
                if (!data) {
                    this.state = {};
                } else {
                    data = JSON.parse(data);
                    if (data.state && data.user == pimcore.currentuser.id) {
                        this.state = data.state;
                    } else {
                        this.state = {};
                    }
                }
            },

            get : function(name, defaultValue){
                try {
                    if (typeof this.state[name] == "undefined") {
                        return defaultValue
                    } else {
                        return this.decodeValue(this.state[name])
                    }
                } catch (e) {
                    this.clear(name);
                    return defaultValue;
                }
            },
            set : function(name, value){
                try {
                    if (typeof value == "undefined" || value === null) {
                        this.clear(name);
                        return;
                    }
                    this.state[name] = this.encodeValue(value)

                    var data = {
                        state: this.state,
                        user: pimcore.currentuser.id
                    };
                    var json = JSON.stringify(data);

                    localStorage.setItem(this.namespace, json);
                } catch (e) {
                    this.clear(name);
                }

                this.fireEvent("statechange", this, name, value);
            }
        });

        var provider = new StateFullProvider({
            namespace : "pimcore_ui_states"
        });

        Ext.state.Manager.setProvider(provider);

        var stateId = "tr_" + this.translationType;
        var applyInitialSettings = false;
        var showInfo = false;
        var state = provider.get(stateId, null);
        var languages = this.languages;

        var maxCols = 7;   // include creation date / modification date / action column)
        var maxLanguages = maxCols - 3;

        if (state == null) {
            applyInitialSettings = true;
            if (languages.length > maxLanguages) {
                showInfo = true;
            }
        } else {
            if (state.columns) {
                for (var i = 0; i < state.columns.length; i++) {
                    var colState = state.columns[i];
                    if (colState.hidden) {
                        showInfo = true;
                        break;
                    }
                }
            }
        }

        var proxy = new Ext.data.HttpProxy({
            url: this.dataUrl,
            method: 'post'
        });

        var readerFields = [
            {name: 'key', allowBlank: false},
            {name: 'creationDate', allowBlank: true},
            {name: 'modificationDate', allowBlank: true}
        ];

        var typesColumns = [
            {header: t("key"), sortable: true, dataIndex: 'key', editable: false}

        ];

        for (var i = 0; i < languages.length; i++) {

            readerFields.push({name: languages[i]});
            var columnConfig = {header: pimcore.available_languages[languages[i]], sortable: false, dataIndex: languages[i],
                editor: new Ext.form.TextField({}), id: "translation_column_" + languages[i].toLowerCase()};
            if (applyInitialSettings) {
                var hidden = i >= maxLanguages;
                columnConfig.hidden = hidden;
            }

            typesColumns.push(columnConfig);
        }

        if (showInfo) {
            pimcore.helpers.showNotification(t("info"), t("there_are_more_columns"), null, null, 2000);
        }

        typesColumns.push({header: t("creationDate"), sortable: true, dataIndex: 'creationDate', editable: false,
            renderer: function(d) {
                var date = new Date(d * 1000);
                return date.format("Y-m-d H:i:s");
            }});
        typesColumns.push({header: t("modificationDate"), sortable: true, dataIndex: 'modificationDate', editable: false,
            renderer: function(d) {
                var date = new Date(d * 1000);
                return date.format("Y-m-d H:i:s");
            }});

        typesColumns.push({
            xtype: 'actioncolumn',
            width: 30,
            items: [{
                tooltip: t('delete'),
                icon: "/pimcore/static/img/icon/cross.png",
                handler: function (grid, rowIndex) {
                    grid.getStore().removeAt(rowIndex);
                }.bind(this)
            }]
        });

        var reader = new Ext.data.JsonReader({
            totalProperty: 'total',
            successProperty: 'success',
            root: 'data',
            idProperty: 'key'
        }, readerFields);

        var writer = new Ext.data.JsonWriter();

        var itemsPerPage = 20;
        this.store = new Ext.data.Store({
            id: 'translation_store',
            restful: false,
            proxy: proxy,
            reader: reader,
            writer: writer,
            remoteSort: true,
            baseParams: {
                limit: itemsPerPage,
                searchString: this.preconfiguredFilter
            },
            listeners: {
                write : function(store, action, result, response, rs) {
                }
            }
        });

        this.pagingtoolbar = new Ext.PagingToolbar({
            pageSize: itemsPerPage,
            store: this.store,
            displayInfo: true,
            displayMsg: '{0} - {1} / {2}',
            emptyMsg: t("no_items_found")
        });

        // add per-page selection
        this.pagingtoolbar.add("-");

        this.pagingtoolbar.add(new Ext.Toolbar.TextItem({
            text: t("items_per_page")
        }));
        this.pagingtoolbar.add(new Ext.form.ComboBox({
            store: [
                [10, "10"],
                [20, "20"],
                [40, "40"],
                [60, "60"],
                [80, "80"],
                [100, "100"]
            ],
            mode: "local",
            width: 50,
            value: 20,
            triggerAction: "all",
            listeners: {
                select: function (box, rec, index) {
                    this.pagingtoolbar.pageSize = intval(rec.data.field1);
                    this.pagingtoolbar.moveFirst();
                }.bind(this)
            }
        }));

        // filters
        this.gridFilters = new Ext.ux.grid.GridFilters({
            encode: true,
            local: false,
            filters: [{
                type: "date",
                dataIndex: "creationDate"
            },{
                type: "date",
                dataIndex: "modificationDate"
            }
            ]
        });

        this.grid = new Ext.grid.EditorGridPanel({
            frame: false,
            autoScroll: true,
            store: this.store,
            columnLines: true,
            stripeRows: true,
            plugins: [this.gridFilters],
            columns : typesColumns,
            trackMouseOver: true,
            bbar: this.pagingtoolbar,
            stateful: true,
            stateId: stateId,
            stateEvents: ['columnmove', 'columnresize', 'sortchange', 'groupchange'],
            sm: new Ext.grid.RowSelectionModel({singleSelect:true}),
            tbar: [
                {
                    text: t('add'),
                    handler: this.onAdd.bind(this),
                    iconCls: "pimcore_icon_add"
                },
                '-',{
                    text: this.getHint(),
                    xtype: "tbtext",
                    style: "margin: 0 10px 0 0;"
                },
                "->",
                {
                    text: t('cleanup'),
                    handler: this.cleanup.bind(this),
                    iconCls: "pimcore_icon_cleanup"
                },
                "-",
                {
                    text: t('merge_csv'),
                    handler: this.doMerge.bind(this),
                    iconCls: "pimcore_icon_merge"
                },
                "-",
                {
                    text: t('import_csv'),
                    handler: this.doImport.bind(this),
                    iconCls: "pimcore_icon_import"
                },
                '-',
                {
                    text: t('export_csv'),
                    handler: this.doExport.bind(this),
                    iconCls: "pimcore_icon_export"
                },'-',{
                    text: t("filter") + "/" + t("search"),
                    xtype: "tbtext",
                    style: "margin: 0 10px 0 0;"
                },this.filterField
            ],
            viewConfig: {
                forceFit: true
            }
        });

        this.store.load();

        return this.grid;
    },

    doImport:function(){
        pimcore.helpers.uploadDialog(this.importUrl, "Filedata", function() {
            this.store.reload();
        }.bind(this), function () {
            Ext.MessageBox.alert(t("error"), t("error"));
        });
    },

    doMerge:function(){
        pimcore.helpers.uploadDialog(this.mergeUrl, "Filedata", function(result) {
            var data = result.response.responseText;
            data = Ext.decode(data);

            var merger = new pimcore.settings.translation.translationmerger(this.translationType, data, this);
            this.refresh();
        }.bind(this), function () {
            Ext.MessageBox.alert(t("error"), t("error"));
        });
    },

    refresh: function() {
        this.store.reload();
    },

    doExport:function(){

        var filtersActive = this.filterField.getValue() || this.gridFilters.getFilterData().length;
        if(filtersActive) {
            Ext.MessageBox.confirm("", t("filter_active_message"), function (buttonValue) {
                if (buttonValue == "yes") {
                    var queryString = "searchString=" + this.filterField.getValue();
                    queryString += "&" + Ext.urlEncode(this.gridFilters.buildQuery(this.gridFilters.getFilterData()));
                    pimcore.helpers.download(Ext.urlAppend(this.exportUrl, queryString));
                } else {
                    pimcore.helpers.download(this.exportUrl);
                }
            }.bind(this));
        } else {
            pimcore.helpers.download(this.exportUrl);
        }
    },

    onAdd: function (btn, ev) {

        Ext.MessageBox.prompt("", t("please_enter_the_new_name"), function (button, value) {
            if(button == "ok") {
                var u = new this.grid.store.recordType();
                u.set("key", value);
                u.markDirty();

                this.grid.store.insert(0, u);
            }

        }.bind(this));
    },

    cleanup: function () {
        Ext.Ajax.request({
            url: this.cleanupUrl,
            success: function (response) {
                this.store.reload();
            }.bind(this)
        });
    }
});
<|endoftext|>


<commit_before>var fs = require("fs");
var path = require("path");
var webpackDevMiddleware = require("webpack-dev-middleware");
var express = require("express");
var socketio = require("socket.io");
var StreamCache = require("stream-cache");
var http = require("http");
var https = require("https");
var httpProxy = require("http-proxy");
var proxy = new httpProxy.createProxyServer();
var serveIndex = require("serve-index");
var historyApiFallback = require("connect-history-api-fallback");

function Server(compiler, options) {
	// Default options
	if(!options) options = {};

	if (options.lazy && !options.filename) {
		throw new Error("'filename' option must be set in lazy mode.");
	}

	this.hot = options.hot;
	this.headers = options.headers;

	// Listening for events
	var invalidPlugin = function() {
		if(this.io) this.io.sockets.emit("invalid");
	}.bind(this);
	compiler.plugin("compile", invalidPlugin);
	compiler.plugin("invalid", invalidPlugin);
	compiler.plugin("done", function(stats) {
		if(!this.io) return;
		this._sendStats(this.io.sockets, stats.toJson());
		this._stats = stats;
	}.bind(this));

	// Prepare live html page
	var livePage = this.livePage = new StreamCache();
	fs.createReadStream(path.join(__dirname, "..", "client", "live.html")).pipe(livePage);

	// Prepare the live js file
	var liveJs = new StreamCache();
	fs.createReadStream(path.join(__dirname, "..", "client", "live.bundle.js")).pipe(liveJs);

	// Prepare the inlined js file
	var inlinedJs = new StreamCache();
	fs.createReadStream(path.join(__dirname, "..", "client", "index.bundle.js")).pipe(inlinedJs);

	// Init express server
	var app = this.app = new express();

	// serve webpack bundle
	app.use(this.middleware = webpackDevMiddleware(compiler, options));

	app.get("/__webpack_dev_server__/live.bundle.js", function(req, res) {
		res.setHeader("Content-Type", "application/javascript");
		liveJs.pipe(res);
	});

	app.get("/webpack-dev-server.js", function(req, res) {
		res.setHeader("Content-Type", "application/javascript");
		inlinedJs.pipe(res);
	});

	app.get("/webpack-dev-server/*", function(req, res) {
		res.setHeader("Content-Type", "text/html");
		this.livePage.pipe(res);
	}.bind(this));

	app.get("/webpack-dev-server", function(req, res) {
		res.setHeader("Content-Type", "text/html");
		res.write('<!DOCTYPE html><meta charset="utf-8"/>');
		var path = this.middleware.getFilenameFromUrl(options.publicPath || "/");
		var fs = this.middleware.fileSystem;
		function writeDirectory(baseUrl, basePath) {
			var content = fs.readdirSync(basePath);
			res.write("
");
			content.forEach(function(item) {
				var p = basePath + "/" + item;
				if(fs.statSync(p).isFile()) {
					res.write('
');
					res.write(item);
					res.write('
');
					if(/\.js$/.test(item)) {
						var htmlItem = item.substr(0, item.length - 3);
						res.write('
');
						res.write(htmlItem);
						res.write(' (magic html for ');
						res.write(item);
						res.write(') (webpack-dev-server)
');
					}
				} else {
					res.write('
');
					res.write(item);
					res.write('
');
					writeDirectory(baseUrl + item + "/", p);
					res.write('
');
				}
			});
			res.write("
");
		}
		writeDirectory(options.publicPath || "/", path);
		res.end('');
	}.bind(this));

	if (options.proxy) {
		if (!Array.isArray(options.proxy)) {
			options.proxy = Object.keys(options.proxy).map(function (path) {
				var proxyOptions;
				if (typeof options.proxy[path] === 'string') {
					proxyOptions = {path: path, target: options.proxy[path]};
				} else {
					proxyOptions = options.proxy[path];
					proxyOptions.path = path;
				}
				return proxyOptions;
			});
		}
		options.proxy.forEach(function (proxyOptions) {
			proxyOptions.ws = proxyOptions.hasOwnProperty('ws') ? proxyOptions.ws : true;
			app.all(proxyOptions.path, function (req, res) {
				if(typeof proxyOptions.rewrite === 'function') proxyOptions.rewrite(req, proxyOptions);
				if (proxyOptions.host) {
					req.headers.host = proxyOptions.host;
				}
				proxy.web(req, res, proxyOptions, function(err){
					var msg = "cannot proxy to " + proxyOptions.target + " (" + err.message + ")";
					this.io.sockets.emit("proxy-error", [msg]);
					res.statusCode = 502;
					res.end();
				}.bind(this));
				if (proxyOptions.configure) {
					proxyOptions.configure(proxy);
				}
			}.bind(this));
		}.bind(this));
	}

	if (options.historyApiFallback) {
		// Fall back to /index.html if nothing else matches.
		app.use(historyApiFallback(typeof options.historyApiFallback === 'object' ? options.historyApiFallback : null));
		// include our middleware to ensure it is able to handle '/index.html' requrst after redirect
		app.use(this.middleware);
	}

	if(options.contentBase !== false) {
		var contentBase = options.contentBase || process.cwd();

		if(typeof contentBase === "object") {
			console.log('Using contentBase as a proxy is deprecated and will be removed in the next major version. Please use the proxy option instead.\n\nTo update remove the contentBase option from webpack.config.js and add this:');
			console.log('proxy: {\n\t"*": <your current contentBase configuration>\n}');
			// Proxy every request to contentBase.target
			app.all("*", function(req, res) {
				proxy.web(req, res, contentBase, function(err) {
					var msg = "cannot proxy to " + contentBase.target + " (" + err.message + ")";
					this.io.sockets.emit("proxy-error", [msg]);
					res.statusCode = 502;
					res.end();
				}.bind(this));
			}.bind(this));
		} else if(/^(https?:)?\/\//.test(contentBase)) {
			// Redirect every request to contentBase
			app.get("*", function(req, res) {
				res.writeHead(302, {
					'Location': contentBase + req.path + (req._parsedUrl.search || "")
				});
				res.end();
			}.bind(this));
		} else if(typeof contentBase === "number") {
			// Redirect every request to the port contentBase
			app.get("*", function(req, res) {
				res.writeHead(302, {
					'Location': "//localhost:" + contentBase + req.path + (req._parsedUrl.search || "")
				});
				res.end();
			}.bind(this));
		} else {
			// route content request
			app.get("*", this.setContentHeaders.bind(this), this.serveMagicHtml.bind(this), express.static(contentBase), serveIndex(contentBase));
		}
	}

	this.listeningApp = options.https
		? https.createServer({
				// using built-in self-signed certificate
				key: options.key || fs.readFileSync(path.join(__dirname, "../ssl/server.key")),
				cert: options.cert || fs.readFileSync(path.join(__dirname, "../ssl/server.crt")),
				ca: options.ca || fs.readFileSync(path.join(__dirname, "../ssl/ca.crt")),
				requestCert: true,
				rejectUnauthorized: false
			}, app)
		: http.createServer(app);
}

Server.prototype.use = function() {
	this.app.use.apply(this.app, arguments);
}

Server.prototype.setContentHeaders = function(req, res, next) {
	if(this.headers) {
		for(var name in this.headers) {
			res.setHeader(name, this.headers[name]);
		}
	}

	next();
}

// delegate listen call and init socket.io
Server.prototype.listen = function() {
	this.listeningApp.listen.apply(this.listeningApp, arguments);
	this.io = socketio.listen(this.listeningApp, {
		"log level": 1
	});
	this.io.sockets.on("connection", function(socket) {
		if(this.hot) socket.emit("hot");
		if(!this._stats) return;
		this._sendStats(socket, this._stats.toJson(), true);
	}.bind(this));
}

Server.prototype.serveMagicHtml = function(req, res, next) {
	var _path = req.path;
	try {
		if(!this.middleware.fileSystem.statSync(this.middleware.getFilenameFromUrl(_path + ".js")).isFile())
			return next();
		// Serve a page that executes the javascript
		res.write('<!DOCTYPE html><meta charset="utf-8"/>');
	} catch(e) { return next(); }
}

// send stats to a socket or multiple sockets
Server.prototype._sendStats = function(socket, stats, force) {
	if(!force && stats && (!stats.errors || stats.errors.length === 0) && stats.assets && stats.assets.every(function(asset) {
		return !asset.emitted;
	})) return socket.emit("still-ok");;
	socket.emit("hash", stats.hash);
	if(stats.errors.length > 0)
		socket.emit("errors", stats.errors);
	else if(stats.warnings.length > 0)
		socket.emit("warnings", stats.warnings);
	else
		socket.emit("ok");
}

Server.prototype.invalidate = function() {
	if(this.middleware) this.middleware.invalidate();
}

module.exports = Server;
<commit_msg>remove a extra ;<commit_after>var fs = require("fs");
var path = require("path");
var webpackDevMiddleware = require("webpack-dev-middleware");
var express = require("express");
var socketio = require("socket.io");
var StreamCache = require("stream-cache");
var http = require("http");
var https = require("https");
var httpProxy = require("http-proxy");
var proxy = new httpProxy.createProxyServer();
var serveIndex = require("serve-index");
var historyApiFallback = require("connect-history-api-fallback");

function Server(compiler, options) {
	// Default options
	if(!options) options = {};

	if (options.lazy && !options.filename) {
		throw new Error("'filename' option must be set in lazy mode.");
	}

	this.hot = options.hot;
	this.headers = options.headers;

	// Listening for events
	var invalidPlugin = function() {
		if(this.io) this.io.sockets.emit("invalid");
	}.bind(this);
	compiler.plugin("compile", invalidPlugin);
	compiler.plugin("invalid", invalidPlugin);
	compiler.plugin("done", function(stats) {
		if(!this.io) return;
		this._sendStats(this.io.sockets, stats.toJson());
		this._stats = stats;
	}.bind(this));

	// Prepare live html page
	var livePage = this.livePage = new StreamCache();
	fs.createReadStream(path.join(__dirname, "..", "client", "live.html")).pipe(livePage);

	// Prepare the live js file
	var liveJs = new StreamCache();
	fs.createReadStream(path.join(__dirname, "..", "client", "live.bundle.js")).pipe(liveJs);

	// Prepare the inlined js file
	var inlinedJs = new StreamCache();
	fs.createReadStream(path.join(__dirname, "..", "client", "index.bundle.js")).pipe(inlinedJs);

	// Init express server
	var app = this.app = new express();

	// serve webpack bundle
	app.use(this.middleware = webpackDevMiddleware(compiler, options));

	app.get("/__webpack_dev_server__/live.bundle.js", function(req, res) {
		res.setHeader("Content-Type", "application/javascript");
		liveJs.pipe(res);
	});

	app.get("/webpack-dev-server.js", function(req, res) {
		res.setHeader("Content-Type", "application/javascript");
		inlinedJs.pipe(res);
	});

	app.get("/webpack-dev-server/*", function(req, res) {
		res.setHeader("Content-Type", "text/html");
		this.livePage.pipe(res);
	}.bind(this));

	app.get("/webpack-dev-server", function(req, res) {
		res.setHeader("Content-Type", "text/html");
		res.write('<!DOCTYPE html><meta charset="utf-8"/>');
		var path = this.middleware.getFilenameFromUrl(options.publicPath || "/");
		var fs = this.middleware.fileSystem;
		function writeDirectory(baseUrl, basePath) {
			var content = fs.readdirSync(basePath);
			res.write("
");
			content.forEach(function(item) {
				var p = basePath + "/" + item;
				if(fs.statSync(p).isFile()) {
					res.write('
');
					res.write(item);
					res.write('
');
					if(/\.js$/.test(item)) {
						var htmlItem = item.substr(0, item.length - 3);
						res.write('
');
						res.write(htmlItem);
						res.write(' (magic html for ');
						res.write(item);
						res.write(') (webpack-dev-server)
');
					}
				} else {
					res.write('
');
					res.write(item);
					res.write('
');
					writeDirectory(baseUrl + item + "/", p);
					res.write('
');
				}
			});
			res.write("
");
		}
		writeDirectory(options.publicPath || "/", path);
		res.end('');
	}.bind(this));

	if (options.proxy) {
		if (!Array.isArray(options.proxy)) {
			options.proxy = Object.keys(options.proxy).map(function (path) {
				var proxyOptions;
				if (typeof options.proxy[path] === 'string') {
					proxyOptions = {path: path, target: options.proxy[path]};
				} else {
					proxyOptions = options.proxy[path];
					proxyOptions.path = path;
				}
				return proxyOptions;
			});
		}
		options.proxy.forEach(function (proxyOptions) {
			proxyOptions.ws = proxyOptions.hasOwnProperty('ws') ? proxyOptions.ws : true;
			app.all(proxyOptions.path, function (req, res) {
				if(typeof proxyOptions.rewrite === 'function') proxyOptions.rewrite(req, proxyOptions);
				if (proxyOptions.host) {
					req.headers.host = proxyOptions.host;
				}
				proxy.web(req, res, proxyOptions, function(err){
					var msg = "cannot proxy to " + proxyOptions.target + " (" + err.message + ")";
					this.io.sockets.emit("proxy-error", [msg]);
					res.statusCode = 502;
					res.end();
				}.bind(this));
				if (proxyOptions.configure) {
					proxyOptions.configure(proxy);
				}
			}.bind(this));
		}.bind(this));
	}

	if (options.historyApiFallback) {
		// Fall back to /index.html if nothing else matches.
		app.use(historyApiFallback(typeof options.historyApiFallback === 'object' ? options.historyApiFallback : null));
		// include our middleware to ensure it is able to handle '/index.html' requrst after redirect
		app.use(this.middleware);
	}

	if(options.contentBase !== false) {
		var contentBase = options.contentBase || process.cwd();

		if(typeof contentBase === "object") {
			console.log('Using contentBase as a proxy is deprecated and will be removed in the next major version. Please use the proxy option instead.\n\nTo update remove the contentBase option from webpack.config.js and add this:');
			console.log('proxy: {\n\t"*": <your current contentBase configuration>\n}');
			// Proxy every request to contentBase.target
			app.all("*", function(req, res) {
				proxy.web(req, res, contentBase, function(err) {
					var msg = "cannot proxy to " + contentBase.target + " (" + err.message + ")";
					this.io.sockets.emit("proxy-error", [msg]);
					res.statusCode = 502;
					res.end();
				}.bind(this));
			}.bind(this));
		} else if(/^(https?:)?\/\//.test(contentBase)) {
			// Redirect every request to contentBase
			app.get("*", function(req, res) {
				res.writeHead(302, {
					'Location': contentBase + req.path + (req._parsedUrl.search || "")
				});
				res.end();
			}.bind(this));
		} else if(typeof contentBase === "number") {
			// Redirect every request to the port contentBase
			app.get("*", function(req, res) {
				res.writeHead(302, {
					'Location': "//localhost:" + contentBase + req.path + (req._parsedUrl.search || "")
				});
				res.end();
			}.bind(this));
		} else {
			// route content request
			app.get("*", this.setContentHeaders.bind(this), this.serveMagicHtml.bind(this), express.static(contentBase), serveIndex(contentBase));
		}
	}

	this.listeningApp = options.https
		? https.createServer({
				// using built-in self-signed certificate
				key: options.key || fs.readFileSync(path.join(__dirname, "../ssl/server.key")),
				cert: options.cert || fs.readFileSync(path.join(__dirname, "../ssl/server.crt")),
				ca: options.ca || fs.readFileSync(path.join(__dirname, "../ssl/ca.crt")),
				requestCert: true,
				rejectUnauthorized: false
			}, app)
		: http.createServer(app);
}

Server.prototype.use = function() {
	this.app.use.apply(this.app, arguments);
}

Server.prototype.setContentHeaders = function(req, res, next) {
	if(this.headers) {
		for(var name in this.headers) {
			res.setHeader(name, this.headers[name]);
		}
	}

	next();
}

// delegate listen call and init socket.io
Server.prototype.listen = function() {
	this.listeningApp.listen.apply(this.listeningApp, arguments);
	this.io = socketio.listen(this.listeningApp, {
		"log level": 1
	});
	this.io.sockets.on("connection", function(socket) {
		if(this.hot) socket.emit("hot");
		if(!this._stats) return;
		this._sendStats(socket, this._stats.toJson(), true);
	}.bind(this));
}

Server.prototype.serveMagicHtml = function(req, res, next) {
	var _path = req.path;
	try {
		if(!this.middleware.fileSystem.statSync(this.middleware.getFilenameFromUrl(_path + ".js")).isFile())
			return next();
		// Serve a page that executes the javascript
		res.write('<!DOCTYPE html><meta charset="utf-8"/>');
	} catch(e) { return next(); }
}

// send stats to a socket or multiple sockets
Server.prototype._sendStats = function(socket, stats, force) {
	if(!force && stats && (!stats.errors || stats.errors.length === 0) && stats.assets && stats.assets.every(function(asset) {
		return !asset.emitted;
	})) return socket.emit("still-ok");
	socket.emit("hash", stats.hash);
	if(stats.errors.length > 0)
		socket.emit("errors", stats.errors);
	else if(stats.warnings.length > 0)
		socket.emit("warnings", stats.warnings);
	else
		socket.emit("ok");
}

Server.prototype.invalidate = function() {
	if(this.middleware) this.middleware.invalidate();
}

module.exports = Server;
<|endoftext|>


<commit_before>Boards = new Mongo.Collection('boards');

// ALLOWS
Boards.allow({
    insert: Meteor.userId,
    update: allowIsBoardAdmin,
    remove: allowIsBoardAdmin,
    fetch: ['members']
});

// We can't remove a member if it is the last administrator
Boards.deny({
    update: function(userId, doc, fieldNames, modifier) {
        if (! _.contains(fieldNames, 'members'))
            return false;

        // We only care in case of a $pull operation, ie remove a member
        if (! _.isObject(modifier.$pull && modifier.$pull.members))
            return false;

        // If there is more than one admin, it's ok to remove anyone
        var nbAdmins = _.filter(doc.members, function(member) {
            return member.isAdmin;
        }).length;
        if (nbAdmins > 1)
            return false;

        // If all the previous conditions where verified, we can't remove
        // a user if it's an admin
        var removedMemberId = modifier.$pull.members.userId;
        return !! _.findWhere(doc.members, {
            userId: removedMemberId,
            isAdmin: true
        });
    },
    fetch: ['members']
})

// HELPERS
Boards.helpers({
    lists: function() {
        return Lists.find({ boardId: this._id, archived: false }, { sort: { sort: 1 }});
    },
    activities: function() {
        return Activities.find({ boardId: this._id }, { sort: { createdAt: -1 }});
    } ,
    absoluteUrl: function() {
        return Router.path("Board", { boardId: this._id, slug: this.slug });
    }
});

// METHODS
Meteor.methods({
    removeCardMember: function(_id) {
        check(_id, String);
        CardMembers.update({ memberId: _id }, {
            $set: {
                approved: false
            }
        });
    }
});

// HOOKS
Boards.before.insert(function(userId, doc) {
    doc.slug = slugify(doc.title);
    doc.createdAt = new Date();
    doc.openWidgets = true;
    doc.archived = false;
    doc.members = [{
        userId: doc.userId,
        isAdmin: true
    }];

    // Handle labels
    var defaultLabels = ['green', 'yellow', 'orange', 'red', 'purple', 'blue'];
    doc.labels = [];
    _.each(defaultLabels, function(val) {
        doc.labels.push({
            _id: Random.id(6),
            name: '',
            color: val
        });
    });
});

Boards.before.update(function(userId, doc, fieldNames, modifier) {
    if (! _.isObject(modifier.$set))
        modifier.$set = {};
    modifier.$set.modifiedAt = new Date();
});

isServer(function() {

    // Let MongoDB ensure that a member is not included twice in the same board
    Meteor.startup(function() {
        Boards._collection._ensureIndex({
            '_id': 1,
            'members.userId': 1
        }, { unique: true });
    });

    // Genesis: the first activity of the newly created board
    Boards.after.insert(function(userId, doc) {
        Activities.insert({
            type: 'board',
            activityTypeId: doc._id,
            activityType: "createBoard",
            boardId: doc._id,
            userId: doc.userId
        });
    });

    // Add a new activity if we add or remove a member to the board
    Boards.after.update(function(userId, doc, fieldNames, modifier) {
        if (! _.contains(fieldNames, 'members'))
            return;

        // Say hello to the new member
        if (modifier.$push && modifier.$push.members) {
            var memberId = modifier.$push.members.userId;
            Activities.insert({
                type: 'member',
                activityType: "addBoardMember",
                boardId: doc._id,
                userId: userId,
                memberId: memberId
            });
        }

        // Say goodbye to the former member
        if (modifier.$pull && modifier.$pull.members) {
            var memberId = modifier.$pull.members.userId;
            Activities.insert({
                type: 'member',
                activityType: "removeBoardMember",
                boardId: doc._id,
                userId: userId,
                memberId: memberId
            });
        }
    });
});
<commit_msg>Fix board creation<commit_after>Boards = new Mongo.Collection('boards');

// ALLOWS
Boards.allow({
    insert: Meteor.userId,
    update: allowIsBoardAdmin,
    remove: allowIsBoardAdmin,
    fetch: ['members']
});

// We can't remove a member if it is the last administrator
Boards.deny({
    update: function(userId, doc, fieldNames, modifier) {
        if (! _.contains(fieldNames, 'members'))
            return false;

        // We only care in case of a $pull operation, ie remove a member
        if (! _.isObject(modifier.$pull && modifier.$pull.members))
            return false;

        // If there is more than one admin, it's ok to remove anyone
        var nbAdmins = _.filter(doc.members, function(member) {
            return member.isAdmin;
        }).length;
        if (nbAdmins > 1)
            return false;

        // If all the previous conditions where verified, we can't remove
        // a user if it's an admin
        var removedMemberId = modifier.$pull.members.userId;
        return !! _.findWhere(doc.members, {
            userId: removedMemberId,
            isAdmin: true
        });
    },
    fetch: ['members']
})

// HELPERS
Boards.helpers({
    lists: function() {
        return Lists.find({ boardId: this._id, archived: false }, { sort: { sort: 1 }});
    },
    activities: function() {
        return Activities.find({ boardId: this._id }, { sort: { createdAt: -1 }});
    } ,
    absoluteUrl: function() {
        return Router.path("Board", { boardId: this._id, slug: this.slug });
    }
});

// METHODS
Meteor.methods({
    removeCardMember: function(_id) {
        check(_id, String);
        CardMembers.update({ memberId: _id }, {
            $set: {
                approved: false
            }
        });
    }
});

// HOOKS
Boards.before.insert(function(userId, doc) {
    doc.slug = slugify(doc.title);
    doc.createdAt = new Date();
    doc.openWidgets = true;
    doc.archived = false;
    doc.members = [{
        userId: userId || doc.userId,
        isAdmin: true
    }];

    // Handle labels
    var defaultLabels = ['green', 'yellow', 'orange', 'red', 'purple', 'blue'];
    doc.labels = [];
    _.each(defaultLabels, function(val) {
        doc.labels.push({
            _id: Random.id(6),
            name: '',
            color: val
        });
    });
});

Boards.before.update(function(userId, doc, fieldNames, modifier) {
    if (! _.isObject(modifier.$set))
        modifier.$set = {};
    modifier.$set.modifiedAt = new Date();
});

isServer(function() {

    // Let MongoDB ensure that a member is not included twice in the same board
    Meteor.startup(function() {
        Boards._collection._ensureIndex({
            '_id': 1,
            'members.userId': 1
        }, { unique: true });
    });

    // Genesis: the first activity of the newly created board
    Boards.after.insert(function(userId, doc) {
        Activities.insert({
            type: 'board',
            activityTypeId: doc._id,
            activityType: "createBoard",
            boardId: doc._id,
            userId: userId || doc.userId
        });
    });

    // Add a new activity if we add or remove a member to the board
    Boards.after.update(function(userId, doc, fieldNames, modifier) {
        if (! _.contains(fieldNames, 'members'))
            return;

        // Say hello to the new member
        if (modifier.$push && modifier.$push.members) {
            var memberId = modifier.$push.members.userId;
            Activities.insert({
                type: 'member',
                activityType: "addBoardMember",
                boardId: doc._id,
                userId: userId,
                memberId: memberId
            });
        }

        // Say goodbye to the former member
        if (modifier.$pull && modifier.$pull.members) {
            var memberId = modifier.$pull.members.userId;
            Activities.insert({
                type: 'member',
                activityType: "removeBoardMember",
                boardId: doc._id,
                userId: userId,
                memberId: memberId
            });
        }
    });
});
<|endoftext|>


<commit_before>var jQuery = "https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.2.min.js";

// Register script for MDS if possible
// Is MDS enabled?
if ("undefined" != typeof g_MinimalDownload && g_MinimalDownload && (window.location.pathname.toLowerCase()).endsWith("/_layouts/15/start.aspx") && "undefined" != typeof asyncDeltaManager) {
    // Register script for MDS if possible
    RegisterModuleInit("siteClassification.js", JavaScript_Embed); //MDS registration
    JavaScript_Embed(); //non MDS run
} else {
    JavaScript_Embed();
}

function JavaScript_Embed() {

    loadScript(jQuery, function () {
        $(document).ready(function () {
            // Execute status setter only after SP.JS has been loaded
            SP.SOD.executeOrDelayUntilScriptLoaded(function () { setClassifier(); }, 'sp.js');
        });
    });
}

function setClassifier() {
    var clientContext = SP.ClientContext.get_current();
    var web = clientContext.get_web();
    var props = web.get_allProperties();
    clientContext.load(web);
    clientContext.load(props);

    clientContext.executeQueryAsync(Function.createDelegate(this, function (sender, args) {
        var policy = props.get_item('PolicyName');
        if (policy == 'HBI') {
            var img = $("");
            $('#ctl00_site_share_button').before(img);
            classified = true;
        }
        else if (policy == 'MBI') {
            var img = $("");
            $('#ctl00_site_share_button').before(img);
            classified = true;
        }
        else if (policy == 'LBI') {
            var img = $("");
            $('#ctl00_site_share_button').before(img);
            classified = true;
        }
    }));
}

function loadScript(url, callback) {
    var head = document.getElementsByTagName("head")[0];
    var script = document.createElement("script");
    script.src = url;

    // Attach handlers for all browsers
    var done = false;
    script.onload = script.onreadystatechange = function () {
        if (!done && (!this.readyState
					|| this.readyState == "loaded"
					|| this.readyState == "complete")) {
            done = true;

            // Continue your code
            callback();

            // Handle memory leak in IE
            script.onload = script.onreadystatechange = null;
            head.removeChild(script);
        }
    };

    head.appendChild(script);
}<commit_msg>updated siteClassification js<commit_after>var jQuery = "https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.2.min.js";

// Register script for MDS if possible
// Is MDS enabled?
if ("undefined" != typeof g_MinimalDownload && g_MinimalDownload && (window.location.pathname.toLowerCase()).endsWith("/_layouts/15/start.aspx") && "undefined" != typeof asyncDeltaManager) {
    // Register script for MDS if possible
    RegisterModuleInit("siteClassification.js", JavaScript_Embed); //MDS registration
    JavaScript_Embed(); //non MDS run
} else {
    JavaScript_Embed();
}

function JavaScript_Embed() {

    loadScript(jQuery, function () {
        $(document).ready(function () {
            // Execute status setter only after SP.JS has been loaded
            SP.SOD.executeOrDelayUntilScriptLoaded(function () { setClassifier(); }, 'sp.js');
        });
    });
}

function setClassifier() {
    var clientContext = SP.ClientContext.get_current();
    var web = clientContext.get_web();
    var props = web.get_allProperties();
    clientContext.load(web);
    clientContext.load(props);

    clientContext.executeQueryAsync(Function.createDelegate(this, function (sender, args) {
        var policy = props.get_item('PolicyName');
        if (policy == 'HBI') {
            var img = $("");
            $('#ctl00_site_share_button').before(img);
            classified = true;
        }
        else if (policy == 'MBI') {
            var img = $("");
            $('#ctl00_site_share_button').before(img);
            classified = true;
        }
        else if (policy == 'LBI') {
            var img = $("");
            $('#ctl00_site_share_button').before(img);
            classified = true;
        }
    }));
}

function loadScript(url, callback) {
    var head = document.getElementsByTagName("head")[0];
    var script = document.createElement("script");
    script.src = url;

    // Attach handlers for all browsers
    var done = false;
    script.onload = script.onreadystatechange = function () {
        if (!done && (!this.readyState
					|| this.readyState == "loaded"
					|| this.readyState == "complete")) {
            done = true;

            // Continue your code
            callback();

            // Handle memory leak in IE
            script.onload = script.onreadystatechange = null;
            head.removeChild(script);
        }
    };

    head.appendChild(script);
}<|endoftext|>