<commit_before>var d = require('./date_helpers.js')
var date = d.today(process.env.NODE_ENV)
var set_current = d.set_current(process.env.NODE_ENV)

const columns = `first_name, last_name, title, address1, address2,
  address3, address4, county, postcode, primary_email, secondary_email`

exports.subsReminderQuery = news_type => (
  `select ${columns},
  datediff(${date}, max(payments.date)) as overdue,
  members.standing_order, members.due_date, members.id,
  sum(case payments.category
    when 'payment' then -payments.amount
    else                 payments.amount
    end) as amount
    from members right outer join payments
    on members.id = payments.member
    where activation_status='activated'
    and ${news_type === 'online'
        ? 'primary_email is not null and email_bounced != true'
        : '(primary_email is null or email_bounced = true)'}
    and members.membership_type in
    ('annual-single', 'annual-double', 'annual-family', 'annual-corporate', 'annual-group')
    group by members.id
    having sum(case payments.category
      when 'payment' then -payments.amount
      else                 payments.amount
      end) > 0
      and datediff(${date}, max(payments.date)) > 30;`
)

exports.newsletter_reminder = () => (
  `select ${columns}
  from members
  where news_type = 'online'
  and primary_email is not null
  and email_bounced != true
  and activation_status='activated'
  and membership_type != 'accounts';`
)

exports.update_subscription = body =>
  `insert into payments (member, category, description, amount, date, createdAt)
  select id, 'subscription', 'Subscription', amount, ${set_current('due_date')}, now()
  from members, membershiptypes
  where members.membership_type = membershiptypes.value
  and members.membership_type in
  ('annual-single', 'annual-double', 'annual-family', 'annual-corporate', 'annual-group')
  and
    date_sub(${date}, interval 11 month)
    > (ifnull((select max(date) from payments
      where members.id = payments.member
      and payments.category = 'subscription'), '1970-01-01')
    )
  and ${d.due_dates(body.start)(body.end)('members.due_date')}
  and activation_status='activated';`

// if news_type is given, then this is used to send correspondence
exports.subscription_due_template = body =>
  `select ${columns}, id, due_date, membership_type, amount, news_type, email_bounced
  from members, membershiptypes
  where members.membership_type = membershiptypes.value
  and (standing_order is null or standing_order=false)
  and members.membership_type in
  ('annual-single', 'annual-double', 'annual-family', 'annual-corporate', 'annual-group')
  ${body.news_type
    ? body.news_type === 'online'
      ? 'and primary_email is not null and email_bounced != true '
      : 'and (primary_email is null or email_bounced = true) '
    : ' ' // if no news_type given, this query is uesed to retrieve all members who have a subscription due
  }
  and
    date_sub(${date}, interval 11 month)
    > (ifnull((select max(date) from payments
      where members.id = payments.member
      and payments.category = 'subscription'), '1970-01-01')
    )
  and ${d.due_dates(body.start)(body.end)('members.due_date')}
  and activation_status='activated';`

exports.subs_due_correspondence = (body) => `
select first_name, last_name, title, address1, address2,
address3, address4, county, postcode, primary_email, secondary_email,
datediff(curdate(), max(payments.date)) as overdue,
members.standing_order, members.due_date, members.id,
sum(case payments.category
    when 'payment' then -payments.amount
    else                 payments.amount
    end
) as amount
from members right outer join payments
on members.id = payments.member
where
  activation_status='activated'
  ${body.news_type === 'online'
      ? 'and primary_email is not null and email_bounced != true '
      : 'and (primary_email is null or email_bounced = true) '
  }
  and members.membership_type in
    ('annual-single', 'annual-double', 'annual-family', 'annual-corporate', 'annual-group')
  group by members.id
  # ensure balance due is > 0
  having
    sum(case payments.category
      when 'payment' then -payments.amount
      else                 payments.amount
      end) > 0
  and
    # ensure the member has a subscription charge made in the dates provided
    members.id in (
      select member from payments
      where category = 'subscription'
      and date between date_format("${body.start}", "%Y-%m-%d") and date_format("${body.end}", "%Y-%m-%d")
    );
`

exports.custom_email = () =>
  `select first_name, last_name, title, primary_email, secondary_email
  from members
  where primary_email is not null
  and email_bounced != true
  and activation_status='activated'
  and membership_type != 'accounts';`

exports.newsletter_labels = () =>
  `select title, first_name, last_name, initials,
  address1, address2, address3, address4,
  postcode, deliverer from members
  where (members.news_type = 'post'
  or members.email_bounced = true)
  and activation_status='activated'
  and membership_type != 'accounts';`

exports.list_deliverers = () =>
  `select deliverer from members
    group by deliverer;`
<commit_msg>#522 - remove unnecessary news type check from get all members with subs due query<commit_after>var d = require('./date_helpers.js')
var date = d.today(process.env.NODE_ENV)
var set_current = d.set_current(process.env.NODE_ENV)

const columns = `first_name, last_name, title, address1, address2,
  address3, address4, county, postcode, primary_email, secondary_email`

exports.subsReminderQuery = news_type => (
  `select ${columns},
  datediff(${date}, max(payments.date)) as overdue,
  members.standing_order, members.due_date, members.id,
  sum(case payments.category
    when 'payment' then -payments.amount
    else                 payments.amount
    end) as amount
    from members right outer join payments
    on members.id = payments.member
    where activation_status='activated'
    and ${news_type === 'online'
        ? 'primary_email is not null and email_bounced != true'
        : '(primary_email is null or email_bounced = true)'}
    and members.membership_type in
    ('annual-single', 'annual-double', 'annual-family', 'annual-corporate', 'annual-group')
    group by members.id
    having sum(case payments.category
      when 'payment' then -payments.amount
      else                 payments.amount
      end) > 0
      and datediff(${date}, max(payments.date)) > 30;`
)

exports.newsletter_reminder = () => (
  `select ${columns}
  from members
  where news_type = 'online'
  and primary_email is not null
  and email_bounced != true
  and activation_status='activated'
  and membership_type != 'accounts';`
)

exports.update_subscription = body =>
  `insert into payments (member, category, description, amount, date, createdAt)
  select id, 'subscription', 'Subscription', amount, ${set_current('due_date')}, now()
  from members, membershiptypes
  where members.membership_type = membershiptypes.value
  and members.membership_type in
  ('annual-single', 'annual-double', 'annual-family', 'annual-corporate', 'annual-group')
  and
    date_sub(${date}, interval 11 month)
    > (ifnull((select max(date) from payments
      where members.id = payments.member
      and payments.category = 'subscription'), '1970-01-01')
    )
  and ${d.due_dates(body.start)(body.end)('members.due_date')}
  and activation_status='activated';`

// get all members who have a subscription due in time period
exports.subscription_due_template = body =>
  `select ${columns}, id, due_date, membership_type, amount, news_type, email_bounced
  from members, membershiptypes
  where members.membership_type = membershiptypes.value
  and (standing_order is null or standing_order=false)
  and members.membership_type in
  ('annual-single', 'annual-double', 'annual-family', 'annual-corporate', 'annual-group')
  and
    date_sub(${date}, interval 11 month)
    > (ifnull((select max(date) from payments
      where members.id = payments.member
      and payments.category = 'subscription'), '1970-01-01')
    )
  and ${d.due_dates(body.start)(body.end)('members.due_date')}
  and activation_status='activated';`

exports.subs_due_correspondence = (body) => `
select first_name, last_name, title, address1, address2,
address3, address4, county, postcode, primary_email, secondary_email,
datediff(curdate(), max(payments.date)) as overdue,
members.standing_order, members.due_date, members.id,
sum(case payments.category
    when 'payment' then -payments.amount
    else                 payments.amount
    end
) as amount
from members right outer join payments
on members.id = payments.member
where
  activation_status='activated'
  ${body.news_type === 'online'
      ? 'and primary_email is not null and email_bounced != true '
      : 'and (primary_email is null or email_bounced = true) '
  }
  and members.membership_type in
    ('annual-single', 'annual-double', 'annual-family', 'annual-corporate', 'annual-group')
  group by members.id
  # ensure balance due is > 0
  having
    sum(case payments.category
      when 'payment' then -payments.amount
      else                 payments.amount
      end) > 0
  and
    # ensure the member has a subscription charge made in the dates provided
    members.id in (
      select member from payments
      where category = 'subscription'
      and date between date_format("${body.start}", "%Y-%m-%d") and date_format("${body.end}", "%Y-%m-%d")
    );
`

exports.custom_email = () =>
  `select first_name, last_name, title, primary_email, secondary_email
  from members
  where primary_email is not null
  and email_bounced != true
  and activation_status='activated'
  and membership_type != 'accounts';`

exports.newsletter_labels = () =>
  `select title, first_name, last_name, initials,
  address1, address2, address3, address4,
  postcode, deliverer from members
  where (members.news_type = 'post'
  or members.email_bounced = true)
  and activation_status='activated'
  and membership_type != 'accounts';`

exports.list_deliverers = () =>
  `select deliverer from members
    group by deliverer;`
<|endoftext|>


<commit_before>import Util from '../util'
import {execSync, spawn} from 'child_process'
import path from 'path'
import fs from 'fs'
import config from 'gen/config'
import zipper from 'zip-local'
import {
    CAPABILITY_MANAGE_BACKUPS,
    CAPABILITY_MANAGE_COLLECTION,
    CAPABILITY_RUN_COMMAND
} from 'util/capabilities'
import Cache from 'util/cache'
import {pubsub} from 'api/subscription'
import {withFilter} from 'graphql-subscriptions'
import {ObjectId} from 'mongodb'
import {sendMail} from '../util/mail'

const {BACKUP_DIR, UPLOAD_DIR} = config

const SKIP_CAPABILITY_CHECK = ['ls -l', 'less ', 'pwd', 'ls', 'ping']
const ENDOFCOMMAND = '__ENDOFCOMMAND__\n'

const execs = {}

const killExec = (id) => {
    if (execs[id]) {
        //execs[id].kill('SIGINT')
        if (execs[id].exitCode === null) {
            process.kill(-execs[id].pid, 'SIGKILL')
        }
        delete execs[id]
    }
}

const findAndReplaceObjectIds = function (obj) {
    for (var i in obj) {
        if (obj.hasOwnProperty(i)) {
            const v = obj[i]
            if (v)
                if (v.constructor === Array) {
                    v.forEach((x, j) => {
                        if (x.constructor === String) {
                            if (i === '$in' && ObjectId.isValid(x)) {
                                v[j] = ObjectId(x)
                            }
                        } else {
                            findAndReplaceObjectIds(x)
                        }
                    })
                } else if (v.constructor === String) {
                    if (ObjectId.isValid(v)) {
                        obj[i] = ObjectId(v)
                    }
                } else {
                    findAndReplaceObjectIds(v);
                }
        }
    }
    return null;
}

export const systemResolver = (db) => ({
    Query: {
        killRun: async ({id}, {context}) => {
            killExec(id)
            return {id}
        },
        run: async ({command, scope, id, sync}, {context}) => {
            let performCheck = true, response = ''

            const currentId = id || (context.id + String((new Date()).getTime()))

            if (command.indexOf('&') < 0 && command.indexOf('>') < 0) {
                for (const c of SKIP_CAPABILITY_CHECK) {
                    if (command.indexOf(c) === 0) {
                        performCheck = false
                    }
                }
            }
            if (performCheck) {
                await Util.checkIfUserHasCapability(db, context, CAPABILITY_RUN_COMMAND)
            }

            if (scope === 'lu') {
                // lu commands

                if (command === 'clearcache') {
                    pubsub.publish('subscribeRun', {
                        userId: context.id,
                        subscribeRun: {event: 'end', response: `${Object.keys(Cache.cache).length} cached items cleared.`, id: currentId}
                    })
                    Cache.cache = {}

                } else if (command === 'memusage') {
                    const memusage = process.memoryUsage().heapUsed / 1024 / 1024

                    pubsub.publish('subscribeRun', {
                        userId: context.id,
                        subscribeRun: {event: 'end', response: memusage, id: currentId}
                    })

                } else {
                    pubsub.publish('subscribeRun', {
                        userId: context.id,
                        subscribeRun: {event: 'error', error: 'Unknown command', id: currentId}
                    })
                }

            } else {
                //Shell

                if (!command) {
                    throw new Error('No command to execute.')
                }

                if (sync) {
                    response = execSync(command, {encoding: 'utf8'})
                } else {
                    /*execs[id] = spawn(command, options, (err, stdout, stderr) => {
                     console.log(stdout)
                     pubsub.publish('subscribeRun', {
                     userId: context.id,
                     subscribeRun: {response: stdout, error: stderr, id}
                     })
                     })*/
                    if (!execs[currentId] || execs[currentId].exitCode !== null) {
                        execs[currentId] = spawn('bash', [], {detached: true})

                        execs[currentId].stdout.on('data', (data) => {
                            let str = data.toString('utf8')
                            const isEnd = (str.indexOf(ENDOFCOMMAND) === str.length - ENDOFCOMMAND.length)
                            if (isEnd) {
                                str = str.substring(0, str.length - ENDOFCOMMAND.length)
                            }

                            pubsub.publish('subscribeRun', {
                                userId: context.id,
                                subscribeRun: {event: isEnd ? 'end' : 'data', response: str, id: currentId}
                            })
                        })

                        execs[currentId].stderr.on('data', (data) => {
                            pubsub.publish('subscribeRun', {
                                userId: context.id,
                                subscribeRun: {event: 'error', error: data.toString('utf8'), id: currentId}
                            })
                        })

                        execs[currentId].on('close', (code) => {
                            pubsub.publish('subscribeRun', {
                                userId: context.id,
                                subscribeRun: {event: 'close', id: currentId}
                            })
                        })
                        execs[currentId].on('error', (error) => {
                            pubsub.publish('subscribeRun', {
                                userId: context.id,
                                subscribeRun: {event: 'error', error, id: currentId}
                            })
                        })
                    }
                    execs[currentId].stdin.write(`${command} && echo ${ENDOFCOMMAND}`)

                    clearTimeout(execs[currentId].execTimeout)
                    execs[currentId].execTimeout = setTimeout(function () {
                        killExec(currentId)
                        pubsub.publish('subscribeRun', {
                            userId: context.id,
                            subscribeRun: {
                                event: 'error',
                                error: 'Execution timeout reached. Console has been reseted',
                                id: currentId
                            }
                        })
                    }, 60000)
                }
            }

            return {response, id: currentId}
        },
        sendMail: async (data, {context}) => {
            //Util.checkIfUserIsLoggedIn(context)
            const response = await sendMail(db, context, data)
            return {response: JSON.stringify(response)}
        },
        ping: async ({}, {context}) => {

            // read something from the db
            const values = Util.keyValueGlobalMap(db, context, ['MailSettings'])

            const response = 'ok'

            return {response}
        },
        brokenReferences: async ({command}, {context}) => {
            Util.checkIfUserIsLoggedIn(context)

            throw new Error('Not implmented yet.')
        },
        dbDumps: async (data, {context}) => {
            Util.checkIfUserIsLoggedIn(context)

            // make sure upload dir exists
            const backup_dir = path.join(__dirname, '../../' + BACKUP_DIR + '/dbdumps/')
            if (!Util.ensureDirectoryExistence(backup_dir)) {
                throw new Error(`Backup folder could not be created -> ${backup_dir}`)
            }

            const files = []
            fs.readdirSync(backup_dir).forEach(file => {
                if (file !== '.DS_Store') {
                    const stats = fs.statSync(backup_dir + '/' + file)
                    files.push({
                        name: file,
                        createdAt: (new Date(stats.mtime)).getTime(),
                        size: (stats.size / 1000) + 'kb'
                    })
                }
            })
            files.reverse()

            const response = {
                results: files,
                offset: 0,
                limit: 0,
                total: files.length
            }
            return response
        },
        mediaDumps: async (data, {context}) => {
            Util.checkIfUserIsLoggedIn(context)

            // make sure upload dir exists
            const backup_dir = path.join(__dirname, '../../' + BACKUP_DIR + '/mediadumps/')
            if (!Util.ensureDirectoryExistence(backup_dir)) {
                throw new Error(`Backup folder coud not be created -> ${backup_dir}`)
            }

            const files = []
            fs.readdirSync(backup_dir).forEach(file => {
                if (file !== '.DS_Store') {
                    const stats = fs.statSync(backup_dir + '/' + file)
                    files.push({
                        name: file,
                        createdAt: (new Date(stats.mtime)).getTime(),
                        size: (stats.size / 1000) + 'kb'
                    })
                }
            })
            files.reverse()
            const response = {
                results: files,
                offset: 0,
                limit: 0,
                total: files.length
            }

            return response
        },
        collections: async ({filter}, {context}) => {
            Util.checkIfUserIsLoggedIn(context)

            const cacheKey = 'system-collections-' + filter

            let collections = Cache.get(cacheKey)
            if (!collections) {
                collections = await db.listCollections({name: {$regex: new RegExp(filter), $options: 'i'}}).toArray()
                Cache.set(cacheKey, collections, 86400000) // cache expires in 1 day
            }

            return {results: collections}
        },
        collectionAggregate: async ({collection, json}, {context}) => {
            await Util.checkIfUserHasCapability(db, context, CAPABILITY_RUN_COMMAND)
            const jsonParsed = JSON.parse(json)
            findAndReplaceObjectIds(jsonParsed)
            const startTimeAggregate = new Date()

            let a = await (db.collection(collection).aggregate(jsonParsed, {allowDiskUse: true}).toArray())
            console.log(`Aggregate time = ${new Date() - startTimeAggregate}ms`)

            return {result: JSON.stringify(a[0])}
        }
    },
    Mutation: {
        createDbDump: async ({type}, {context}) => {
            await Util.checkIfUserHasCapability(db, context, CAPABILITY_MANAGE_BACKUPS)

            // make sure upload dir exists
            const backup_dir = path.join(__dirname, '../../' + BACKUP_DIR + '/dbdumps/')
            if (!Util.ensureDirectoryExistence(backup_dir)) {
                throw new Error(`Backup folder coud not be created -> ${backup_dir}`)
            }

            /*

             Backup: mongodump --uri $LUNUC_MONGO_URL -v --archive=backup.25022018.gz --gzip

             */
            const date = Date.now(),
                name = 'backup.db.' + date + '.gz',
                fullName = path.join(backup_dir, name)

            const response = execSync('mongodump --uri $MONGO_URL -v --archive="' + fullName + '" --gzip')
            console.log('createDbDump', response)

            const stats = fs.statSync(fullName)

            return {name, createdAt: date, size: (stats.size / 1000) + 'kb'}
        },
        createMediaDump: async ({type}, {context}) => {
            await Util.checkIfUserHasCapability(db, context, CAPABILITY_MANAGE_BACKUPS)

            // make sure upload dir exists
            const backup_dir = path.join(__dirname, '../../' + BACKUP_DIR + '/mediadumps/')
            if (!Util.ensureDirectoryExistence(backup_dir)) {
                throw new Error(`Backup folder coud not be created -> ${backup_dir}`)
            }

            const date = Date.now(),
                name = 'backup.media.' + date + '.gz',
                fullName = path.join(backup_dir, name)

            const media_dir = path.join(__dirname, '../../' + UPLOAD_DIR)

            const files = fs.readdirSync(media_dir)
            if (files.length === 0) {
                throw new Error(`No files in folder -> ${media_dir}`)
            }

            // zip media dir
            zipper.sync.zip(media_dir).compress().save(fullName)

            const stats = fs.statSync(fullName)

            return {name, createdAt: date, size: (stats.size / 1000) + 'kb'}
        },
        cloneCollection: async ({type, name}, {context}) => {
            await Util.checkIfUserHasCapability(db, context, CAPABILITY_MANAGE_COLLECTION)

            let customName
            if (name) {
                customName = name.replace(/[\W_]+/g, '_')
                if (customName) {
                    customName = '_' + customName
                }
            }

            const newName = `${type}_${new Date().getTime()}${customName}`
            const oldCollection = db.collection(type)
            const newCollection = db.collection(newName)

            // this is not the best way to copy a collection
            // better do it with aggregate function or mongodumb
            // but for now it is the only solution that worked
            const result = await oldCollection.find().forEach((x) => {
                newCollection.insert(x)
            })
            //const result = await oldCollection.aggregate([{$out: newName}])

            //copy indexes
            const indexes = []
            await oldCollection.listIndexes().forEach((x) => {
                if (!x.key._id) {
                    delete x.ns
                    indexes.push(x)
                }
            })
            if (indexes.length > 0) {
                newCollection.createIndexes(indexes)
            }

            Cache.clearStartWith('system-collections')

            return {status: 'success', collection: {name: newName}}
        },
        deleteCollection: async ({name}, {context}) => {
            await Util.checkIfUserHasCapability(db, context, CAPABILITY_MANAGE_COLLECTION)

            db.collection(name).drop()

            Cache.clearStartWith('system-collections')

            return {status: 'success', collection: {name}}
        }
    },
    Subscription: {
        subscribeRun: withFilter(() => pubsub.asyncIterator('subscribeRun'),
            (payload, context) => {
                if (payload) {
                    return payload.userId === context.id
                }
            }
        )
    }
})
<commit_msg>Small improvements<commit_after>import Util from '../util'
import {execSync, spawn} from 'child_process'
import path from 'path'
import fs from 'fs'
import config from 'gen/config'
import zipper from 'zip-local'
import {
    CAPABILITY_MANAGE_BACKUPS,
    CAPABILITY_MANAGE_COLLECTION,
    CAPABILITY_RUN_COMMAND
} from 'util/capabilities'
import Cache from 'util/cache'
import {pubsub} from 'api/subscription'
import {withFilter} from 'graphql-subscriptions'
import {ObjectId} from 'mongodb'
import {sendMail} from '../util/mail'

const {BACKUP_DIR, UPLOAD_DIR} = config

const SKIP_CAPABILITY_CHECK = ['ls -l', 'less ', 'pwd', 'ls', 'ping']
const ENDOFCOMMAND = '__ENDOFCOMMAND__\n'

const execs = {}

const killExec = (id) => {
    if (execs[id]) {
        //execs[id].kill('SIGINT')
        if (execs[id].exitCode === null) {
            process.kill(-execs[id].pid, 'SIGKILL')
        }
        delete execs[id]
    }
}

const findAndReplaceObjectIds = function (obj) {
    for (var i in obj) {
        if (obj.hasOwnProperty(i)) {
            const v = obj[i]
            if (v)
                if (v.constructor === Array) {
                    v.forEach((x, j) => {
                        if (x.constructor === String) {
                            if (i === '$in' && ObjectId.isValid(x)) {
                                v[j] = ObjectId(x)
                            }
                        } else {
                            findAndReplaceObjectIds(x)
                        }
                    })
                } else if (v.constructor === String) {
                    if (ObjectId.isValid(v)) {
                        obj[i] = ObjectId(v)
                    }
                } else {
                    findAndReplaceObjectIds(v)
                }
        }
    }
    return null
}

export const systemResolver = (db) => ({
    Query: {
        killRun: async ({id}, {context}) => {
            killExec(id)
            return {id}
        },
        run: async ({command, scope, id, sync}, {context}) => {
            let performCheck = true, response = ''

            const currentId = id || (context.id + String((new Date()).getTime()))

            if (command.indexOf('&') < 0 && command.indexOf('>') < 0) {
                for (const c of SKIP_CAPABILITY_CHECK) {
                    if (command.indexOf(c) === 0) {
                        performCheck = false
                    }
                }
            }
            if (performCheck) {
                await Util.checkIfUserHasCapability(db, context, CAPABILITY_RUN_COMMAND)
            }

            if (scope === 'lu') {
                // lu commands

                if (command === 'clearcache') {
                    pubsub.publish('subscribeRun', {
                        userId: context.id,
                        subscribeRun: {event: 'end', response: `${Object.keys(Cache.cache).length} cached items cleared.`, id: currentId}
                    })
                    Cache.cache = {}

                } else if (command === 'memusage') {
                    const memusage = process.memoryUsage().heapUsed / 1024 / 1024

                    pubsub.publish('subscribeRun', {
                        userId: context.id,
                        subscribeRun: {event: 'end', response: memusage, id: currentId}
                    })

                } else {
                    pubsub.publish('subscribeRun', {
                        userId: context.id,
                        subscribeRun: {event: 'error', error: 'Unknown command', id: currentId}
                    })
                }

            } else {
                //Shell

                if (!command) {
                    throw new Error('No command to execute.')
                }

                if (sync) {
                    response = execSync(command, {encoding: 'utf8'})
                } else {
                    /*execs[id] = spawn(command, options, (err, stdout, stderr) => {
                     console.log(stdout)
                     pubsub.publish('subscribeRun', {
                     userId: context.id,
                     subscribeRun: {response: stdout, error: stderr, id}
                     })
                     })*/
                    if (!execs[currentId] || execs[currentId].exitCode !== null) {
                        execs[currentId] = spawn('bash', [], {detached: true})

                        execs[currentId].stdout.on('data', (data) => {
                            let str = data.toString('utf8')
                            const isEnd = (str.indexOf(ENDOFCOMMAND) === str.length - ENDOFCOMMAND.length)
                            if (isEnd) {
                                str = str.substring(0, str.length - ENDOFCOMMAND.length)
                            }

                            pubsub.publish('subscribeRun', {
                                userId: context.id,
                                subscribeRun: {event: isEnd ? 'end' : 'data', response: str, id: currentId}
                            })
                        })

                        execs[currentId].stderr.on('data', (data) => {
                            pubsub.publish('subscribeRun', {
                                userId: context.id,
                                subscribeRun: {event: 'error', error: data.toString('utf8'), id: currentId}
                            })
                        })

                        execs[currentId].on('close', (code) => {
                            pubsub.publish('subscribeRun', {
                                userId: context.id,
                                subscribeRun: {event: 'close', id: currentId}
                            })
                        })
                        execs[currentId].on('error', (error) => {
                            pubsub.publish('subscribeRun', {
                                userId: context.id,
                                subscribeRun: {event: 'error', error, id: currentId}
                            })
                        })
                    }
                    execs[currentId].stdin.write(`${command} && echo ${ENDOFCOMMAND}`)

                    clearTimeout(execs[currentId].execTimeout)
                    execs[currentId].execTimeout = setTimeout(function () {
                        killExec(currentId)
                        pubsub.publish('subscribeRun', {
                            userId: context.id,
                            subscribeRun: {
                                event: 'error',
                                error: 'Execution timeout reached. Console has been reseted',
                                id: currentId
                            }
                        })
                    }, 60000)
                }
            }

            return {response, id: currentId}
        },
        sendMail: async (data, {context}) => {
            //Util.checkIfUserIsLoggedIn(context)
            const response = await sendMail(db, context, data)
            return {response: JSON.stringify(response)}
        },
        ping: async ({}, {context}) => {

            // read something from the db
            const values = Util.keyValueGlobalMap(db, context, ['MailSettings'])

            const response = 'ok'

            return {response}
        },
        brokenReferences: async ({command}, {context}) => {
            Util.checkIfUserIsLoggedIn(context)

            throw new Error('Not implmented yet.')
        },
        dbDumps: async (data, {context}) => {
            Util.checkIfUserIsLoggedIn(context)

            // make sure upload dir exists
            const backup_dir = path.join(__dirname, '../../' + BACKUP_DIR + '/dbdumps/')
            if (!Util.ensureDirectoryExistence(backup_dir)) {
                throw new Error(`Backup folder could not be created -> ${backup_dir}`)
            }

            const files = []
            fs.readdirSync(backup_dir).forEach(file => {
                if (file !== '.DS_Store') {
                    const stats = fs.statSync(backup_dir + '/' + file)
                    files.push({
                        name: file,
                        createdAt: (new Date(stats.mtime)).getTime(),
                        size: (stats.size / 1000) + 'kb'
                    })
                }
            })
            files.reverse()

            const response = {
                results: files,
                offset: 0,
                limit: 0,
                total: files.length
            }
            return response
        },
        mediaDumps: async (data, {context}) => {
            Util.checkIfUserIsLoggedIn(context)

            // make sure upload dir exists
            const backup_dir = path.join(__dirname, '../../' + BACKUP_DIR + '/mediadumps/')
            if (!Util.ensureDirectoryExistence(backup_dir)) {
                throw new Error(`Backup folder coud not be created -> ${backup_dir}`)
            }

            const files = []
            fs.readdirSync(backup_dir).forEach(file => {
                if (file !== '.DS_Store') {
                    const stats = fs.statSync(backup_dir + '/' + file)
                    files.push({
                        name: file,
                        createdAt: (new Date(stats.mtime)).getTime(),
                        size: (stats.size / 1000) + 'kb'
                    })
                }
            })
            files.reverse()
            const response = {
                results: files,
                offset: 0,
                limit: 0,
                total: files.length
            }

            return response
        },
        collections: async ({filter}, {context}) => {
            Util.checkIfUserIsLoggedIn(context)

            const cacheKey = 'system-collections-' + filter

            let collections = Cache.get(cacheKey)
            if (!collections) {
                collections = await db.listCollections({name: {$regex: new RegExp(filter), $options: 'i'}}).toArray()
                Cache.set(cacheKey, collections, 86400000) // cache expires in 1 day
            }

            return {results: collections}
        },
        collectionAggregate: async ({collection, json}, {context}) => {
            await Util.checkIfUserHasCapability(db, context, CAPABILITY_RUN_COMMAND)
            const jsonParsed = JSON.parse(json)
            findAndReplaceObjectIds(jsonParsed)
            const startTimeAggregate = new Date()

            let a = await (db.collection(collection).aggregate(jsonParsed, {allowDiskUse: true}).toArray())
            console.log(`Aggregate time = ${new Date() - startTimeAggregate}ms`)

            return {result: JSON.stringify(a[0])}
        }
    },
    Mutation: {
        createDbDump: async ({type}, {context}) => {
            await Util.checkIfUserHasCapability(db, context, CAPABILITY_MANAGE_BACKUPS)

            // make sure upload dir exists
            const backup_dir = path.join(__dirname, '../../' + BACKUP_DIR + '/dbdumps/')
            if (!Util.ensureDirectoryExistence(backup_dir)) {
                throw new Error(`Backup folder coud not be created -> ${backup_dir}`)
            }

            /*

             Backup: mongodump --uri $LUNUC_MONGO_URL -v --archive=backup.25022018.gz --gzip

             */
            const date = Date.now(),
                name = 'backup.db.' + date + '.gz',
                fullName = path.join(backup_dir, name)

            const response = execSync('mongodump --uri $MONGO_URL -v --archive="' + fullName + '" --gzip')
            console.log('createDbDump', response)

            const stats = fs.statSync(fullName)

            return {name, createdAt: date, size: (stats.size / 1000) + 'kb'}
        },
        createMediaDump: async ({type}, {context}) => {
            await Util.checkIfUserHasCapability(db, context, CAPABILITY_MANAGE_BACKUPS)

            // make sure upload dir exists
            const backup_dir = path.join(__dirname, '../../' + BACKUP_DIR + '/mediadumps/')
            if (!Util.ensureDirectoryExistence(backup_dir)) {
                throw new Error(`Backup folder coud not be created -> ${backup_dir}`)
            }

            const date = Date.now(),
                name = 'backup.media.' + date + '.gz',
                fullName = path.join(backup_dir, name)

            const media_dir = path.join(__dirname, '../../' + UPLOAD_DIR)

            const files = fs.readdirSync(media_dir)
            if (files.length === 0) {
                throw new Error(`No files in folder -> ${media_dir}`)
            }

            // zip media dir
            zipper.sync.zip(media_dir).compress().save(fullName)

            const stats = fs.statSync(fullName)

            return {name, createdAt: date, size: (stats.size / 1000) + 'kb'}
        },
        cloneCollection: async ({type, name}, {context}) => {
            await Util.checkIfUserHasCapability(db, context, CAPABILITY_MANAGE_COLLECTION)

            let customName
            if (name) {
                customName = name.replace(/[\W_]+/g, '_')
                if (customName) {
                    customName = '_' + customName
                }
            }

            const newName = `${type}_${new Date().getTime()}${customName}`
            const oldCollection = db.collection(type)
            const newCollection = db.collection(newName)

            // this is not the best way to copy a collection
            // better do it with aggregate function or mongodumb
            // but for now it is the only solution that worked
            const result = await oldCollection.find().forEach((x) => {
                newCollection.insert(x)
            })
            //const result = await oldCollection.aggregate([{$out: newName}])

            //copy indexes
            const indexes = []
            await oldCollection.listIndexes().forEach((x) => {
                if (!x.key._id) {
                    delete x.ns
                    indexes.push(x)
                }
            })
            if (indexes.length > 0) {
                newCollection.createIndexes(indexes)
            }

            Cache.clearStartWith('system-collections')

            return {status: 'success', collection: {name: newName}}
        },
        deleteCollection: async ({name}, {context}) => {
            await Util.checkIfUserHasCapability(db, context, CAPABILITY_MANAGE_COLLECTION)

            db.collection(name).drop()

            Cache.clearStartWith('system-collections')

            return {status: 'success', collection: {name}}
        }
    },
    Subscription: {
        subscribeRun: withFilter(() => pubsub.asyncIterator('subscribeRun'),
            (payload, context) => {
                if (payload) {
                    return payload.userId === context.id
                }
            }
        )
    }
})
<|endoftext|>


<commit_before>/* ************************************************************************
 *
 *    qooxdoo-compiler - node.js based replacement for the Qooxdoo python
 *    toolchain
 *
 *    https://github.com/qooxdoo/qooxdoo-compiler
 *
 *    Copyright:
 *      2011-2018 Zenesis Limited, http://www.zenesis.com
 *
 *    License:
 *      MIT: https://opensource.org/licenses/MIT
 *
 *      This software is provided under the same licensing terms as Qooxdoo,
 *      please see the LICENSE file in the Qooxdoo project's top-level directory
 *      for details.
 *
 *    Authors:
 *      * John Spackman (john.spackman@zenesis.com, @johnspackman)
 *
 * *********************************************************************** */

require("@qooxdoo/framework");
const Ajv = require("ajv");
const betterAjvErrors = require("better-ajv-errors");
const fs = qx.tool.compiler.utils.Promisify.fs;
const JsonToAst = require("json-to-ast");

qx.Class.define("qx.tool.compiler.utils.Json", {

  statics: {
    /**
     * Parses JSON string into an object
     *
     * @param str {String} the data to parse
     * @return {Object}
     */
    parseJson: function(str) {
      if (str === null) {
        return null;
      }
      str = str.trim();
      if (!str) {
        return null;
      }
      // eslint-disable-next-line no-path-concat
      var qxRoot = __dirname + "/../../../../../";
      var ast = JsonToAst.parseToAst(str);
      var json = JsonToAst.astToObject(ast);
      var id = this.identifyData(json);
      if (id) {
        var ajv = new Ajv({
          allErrors: true,
          jsonPointers: true
        });
        var schema = "https://raw.githubusercontent.com/qooxdoo/qooxdoo-compiler/master/resource/" + id.type + "-" + String(id.version) + ".json";
        var valid = ajv
          .addSchema(require(qxRoot + "resource/compile-" + String(id.version) + ".json"))
          .addSchema(require(qxRoot + "resource/Manifest-" + String(id.version) + ".json"))
          .validate(schema, json);
        if (!valid) {
          const err = betterAjvErrors(schema, json, ajv.errors, {format: "js"});
          if (id.version > 0) {
            throw new Error(err[0].error);
          } else {
            console.warn(err[0].error);
          }
        }
      }
      return json;
    },
    /**
     * Identify Type and Schema Version
     *
     * @param data {Object} Manifest.json or compile.json
     * @return {Object?} object with data and version key
     */

    identifyData: function(data) {
      if (data.$schema) {
        let match = data.$schema.match(/([^-/\s]+)-(\d+)\.json$/);
        if (match) {
          return {
            type: match[1],
            version: parseInt(match[2])
          };
        }
      }
      if (data.applications) {
        return {
          type: "compile",
          version: 0
        };
      }
      if (data.provides) {
        return {
          type: "Manifest",
          version: 0
        };
      }
      return null;
    },

    /**
     * Loads JSON data from a file and returns it as an object; if the file does not exist, then
     * null is returned
     *
     * @param filename {String} the filename to load
     * @return {Object?} the parsed contents, or null if the file does not exist
     */
    loadJsonAsync: async function(filename) {
      if (!await fs.existsAsync(filename)) {
        return null;
      }
      var data = await fs.readFileAsync(filename, "utf8");
      try {
        return qx.tool.compiler.utils.Json.parseJson(data);
      } catch (ex) {
        throw new Error("Failed to load " + filename + ": " + ex);
      }
    },

    /**
     * Saves JSON data to a file, or erases the file if data is null
     *
     * @param filename {String} filename to write to
     * @param data {Object?} the data to write
     */
    saveJsonAsync: async function(filename, data) {
      if (data) {
        await fs.writeFileAsync(filename, JSON.stringify(data, null, 2), "utf8");
      } else if (await fs.existsAsync(filename)) {
        fs.unlinkAsync(filename);
      }
    }

  }
});
<commit_msg>better warning format<commit_after>/* ************************************************************************
 *
 *    qooxdoo-compiler - node.js based replacement for the Qooxdoo python
 *    toolchain
 *
 *    https://github.com/qooxdoo/qooxdoo-compiler
 *
 *    Copyright:
 *      2011-2018 Zenesis Limited, http://www.zenesis.com
 *
 *    License:
 *      MIT: https://opensource.org/licenses/MIT
 *
 *      This software is provided under the same licensing terms as Qooxdoo,
 *      please see the LICENSE file in the Qooxdoo project's top-level directory
 *      for details.
 *
 *    Authors:
 *      * John Spackman (john.spackman@zenesis.com, @johnspackman)
 *
 * *********************************************************************** */

require("@qooxdoo/framework");
const Ajv = require("ajv");
const betterAjvErrors = require("better-ajv-errors");
const fs = qx.tool.compiler.utils.Promisify.fs;
const JsonToAst = require("json-to-ast");

qx.Class.define("qx.tool.compiler.utils.Json", {

  statics: {
    /**
     * Parses JSON string into an object
     *
     * @param str {String} the data to parse
     * @return {Object}
     */
    parseJson: function(str) {
      if (str === null) {
        return null;
      }
      str = str.trim();
      if (!str) {
        return null;
      }
      // eslint-disable-next-line no-path-concat
      var qxRoot = __dirname + "/../../../../../";
      var ast = JsonToAst.parseToAst(str);
      var json = JsonToAst.astToObject(ast);
      var id = this.identifyData(json);
      if (id) {
        var ajv = new Ajv({
          allErrors: true,
          jsonPointers: true
        });
        var schema = "https://raw.githubusercontent.com/qooxdoo/qooxdoo-compiler/master/resource/" + id.type + "-" + String(id.version) + ".json";
        var valid = ajv
          .addSchema(require(qxRoot + "resource/compile-" + String(id.version) + ".json"))
          .addSchema(require(qxRoot + "resource/Manifest-" + String(id.version) + ".json"))
          .validate(schema, json);
        if (!valid) {
          const err = betterAjvErrors(schema, json, ajv.errors, {format: "js"});
          if (id.version > 0) {
            throw new Error(err[0].error);
          } else {
            console.warn("WARNING: "+id.type + ".json#" + err[0].error);
          }
        }
      }
      return json;
    },
    /**
     * Identify Type and Schema Version
     *
     * @param data {Object} Manifest.json or compile.json
     * @return {Object?} object with data and version key
     */

    identifyData: function(data) {
      if (data.$schema) {
        let match = data.$schema.match(/([^-/\s]+)-(\d+)\.json$/);
        if (match) {
          return {
            type: match[1],
            version: parseInt(match[2])
          };
        }
      }
      if (data.applications) {
        return {
          type: "compile",
          version: 0
        };
      }
      if (data.provides) {
        return {
          type: "Manifest",
          version: 0
        };
      }
      return null;
    },

    /**
     * Loads JSON data from a file and returns it as an object; if the file does not exist, then
     * null is returned
     *
     * @param filename {String} the filename to load
     * @return {Object?} the parsed contents, or null if the file does not exist
     */
    loadJsonAsync: async function(filename) {
      if (!await fs.existsAsync(filename)) {
        return null;
      }
      var data = await fs.readFileAsync(filename, "utf8");
      try {
        return qx.tool.compiler.utils.Json.parseJson(data);
      } catch (ex) {
        throw new Error("Failed to load " + filename + ": " + ex);
      }
    },

    /**
     * Saves JSON data to a file, or erases the file if data is null
     *
     * @param filename {String} filename to write to
     * @param data {Object?} the data to write
     */
    saveJsonAsync: async function(filename, data) {
      if (data) {
        await fs.writeFileAsync(filename, JSON.stringify(data, null, 2), "utf8");
      } else if (await fs.existsAsync(filename)) {
        fs.unlinkAsync(filename);
      }
    }

  }
});
<|endoftext|>


<commit_before>export const addFilteredTotal = async (ctx) => {
  if (!ctx.params.skipFilteredTotal) {
    ctx.result.filteredTotal = ctx.result.total
    const count = await ctx.service.find({
      query: { $limit: '0' },
      skipFilteredTotal: true,
    })
    ctx.result.total = count.total
  }
}

const mapRelationsToIds = (obj, relations) => {
  relations.forEach((r) => {
    if (obj[r]) {
      obj[r] = obj[r].map((i) => i.id)
    }
  })
  return obj
}

const toArray = (relationExpression) =>
  relationExpression
    .substring(1, relationExpression.length - 1)
    .split(',')
    .map((r) => r.trim())

export const mapResultListRelationsToIds =
  (relationExpression) => async (ctx) => {
    const relations = toArray(relationExpression)
    if (ctx.result.data) {
      ctx.result.data = ctx.result.data.map((obj) =>
        mapRelationsToIds(obj, relations)
      )
    }
    return ctx
  }

export const mapResultRelationsToIds = (relationExpression) => async (ctx) => {
  const relations = toArray(relationExpression)
  if (ctx.result) {
    ctx.result = mapRelationsToIds(ctx.result, relations)
  }
}

export const transformAutocompleteQuery = async (ctx) => {
  if (ctx.params.query && ctx.params.query.q) {
    ctx.params.query.name = { $ilike: `%${ctx.params.query.q}%` }
    delete ctx.params.query.q
  }
  return ctx
}
<commit_msg>Add substring match for name parameter.<commit_after>export const addFilteredTotal = async (ctx) => {
  if (!ctx.params.skipFilteredTotal) {
    ctx.result.filteredTotal = ctx.result.total
    const count = await ctx.service.find({
      query: { $limit: '0' },
      skipFilteredTotal: true,
    })
    ctx.result.total = count.total
  }
}

const mapRelationsToIds = (obj, relations) => {
  relations.forEach((r) => {
    if (obj[r]) {
      obj[r] = obj[r].map((i) => i.id)
    }
  })
  return obj
}

const toArray = (relationExpression) =>
  relationExpression
    .substring(1, relationExpression.length - 1)
    .split(',')
    .map((r) => r.trim())

export const mapResultListRelationsToIds =
  (relationExpression) => async (ctx) => {
    const relations = toArray(relationExpression)
    if (ctx.result.data) {
      ctx.result.data = ctx.result.data.map((obj) =>
        mapRelationsToIds(obj, relations)
      )
    }
    return ctx
  }

export const mapResultRelationsToIds = (relationExpression) => async (ctx) => {
  const relations = toArray(relationExpression)
  if (ctx.result) {
    ctx.result = mapRelationsToIds(ctx.result, relations)
  }
}

export const transformAutocompleteQuery = async (ctx) => {
  const query = ctx.params.query
  if (query) {
    if (query.q) {
      ctx.params.query.name = { $ilike: `%${ctx.params.query.q}%` }
      delete ctx.params.query.q
    }
    if (query.name) {
      ctx.params.query.name = { $ilike: `%${ctx.params.query.name}%` }
    }
  }
  return ctx
}
<|endoftext|>


<commit_before>/* vuex-i18n defines the Vuexi18nPlugin to enable localization using a vuex
** module to store the translation information. Make sure to also include the
** file vuex-i18n-store.js to include a respective vuex module.
*/

import module from './vuex-i18n-store';
import plurals from './vuex-i18n-plurals';

// initialize the plugin object
let VuexI18nPlugin = {};

// internationalization plugin for vue js using vuex
VuexI18nPlugin.install = function install(Vue, store, config) {

	// TODO: remove this block for next major update (API break)
	if (typeof arguments[2] === 'string' || typeof arguments[3] === 'string') {
		console.warn('i18n: Registering the plugin vuex-i18n with a string for `moduleName` or `identifiers` is deprecated. Use a configuration object instead.', 'https://github.com/dkfbasel/vuex-i18n#setup');
		config = {
			moduleName: arguments[2],
			identifiers: arguments[3]
		};
	}

	// merge default options with user supplied options
	let mergedConfig = Object.assign({
		moduleName: 'i18n',
		identifiers: ['{', '}'],
		preserveState: false,
		onTranslationNotFound: function() {}
	}, config);

	// define module name and identifiers as constants to prevent any changes
	const moduleName = mergedConfig.moduleName;
	const identifiers = mergedConfig.identifiers;

	// initialize the onTranslationNotFound function and make sure it is actually
	// a function
	let onTranslationNotFound = mergedConfig.onTranslationNotFound;
	if (typeof onTranslationNotFound !== 'function') {
		console.error('i18n: i18n config option onTranslationNotFound must be a function');
		onTranslationNotFound = function() {};
	}

	// register the i18n module in the vuex store
	// preserveState can be used via configuration if server side rendering is used
	store.registerModule(moduleName, module, {preserveState: mergedConfig.preserveState});

	// check if the plugin was correctly initialized
	if (store.state.hasOwnProperty(moduleName) === false) {
		console.error('i18n: i18n vuex module is not correctly initialized. Please check the module name:', moduleName);

		// always return the key if module is not initialized correctly
		Vue.prototype.$i18n = function(key) {
			return key;
		};

		Vue.prototype.$getLanguage = function() {
			return null;
		};

		Vue.prototype.$setLanguage = function() {
			console.error('i18n: i18n vuex module is not correctly initialized');
		};

		return;
	};

	// initialize the replacement function
	let render = renderFn(identifiers);

	// get localized string from store. note that we pass the arguments passed
	// to the function directly to the translateInLanguage function
	let translate = function $t() {

		// get the current language from the store
		let locale = store.state[moduleName].locale;

		return translateInLanguage(locale, ...arguments);
	};

	// get localized string from store in a given language if available.
	// there are two possible signatures for the function.
	// we will check the arguments to make up the options passed.
	// 1: locale, key, options, pluralization
	// 2: locale, key, defaultValue, options, pluralization
	let translateInLanguage = function translateInLanguage(locale) {

		// read the function arguments
		let args = arguments;

		// initialize options
		let key = '';
		let defaultValue = '';
		let options = {};
		let pluralization = null;

		let count = args.length;

		// check if a default value was specified and fill options accordingly
		if (count >= 3 && typeof args[2] === 'string') {

			key = args[1];
			defaultValue = args[2];

			if (count > 3) {
				options = args[3];
			}

			if (count > 4) {
				pluralization = args[4];
			}

		} else {

			key = args[1];

			// default value was not specified and is therefore the same as the key
			defaultValue = key;

			if (count > 2) {
				options = args[2];
			}

			if (count > 3) {
				pluralization = args[3];
			}

		}

		// return the default value if the locale is not set (could happen on initialization)
		if (!locale) {
			console.warn('i18n: i18n locale is not set when trying to access translations:', key);
			return defaultValue;
		}

		// get the translations from the store
		let translations = store.state[moduleName].translations;

		// get the last resort fallback from the store
		let fallback = store.state[moduleName].fallback;

		// split locale by - to support partial fallback for regional locales
		// like de-CH, en-UK
		let localeRegional = locale.split('-');

		// flag for translation to exist or not
		let translationExists = true;

		// check if the language exists in the store. return the key if not
		if (translations.hasOwnProperty(locale) === false ) {
			translationExists = false;

		// check if the key exists in the store. return the key if not
		} else if (translations[locale].hasOwnProperty(key) === false) {
			translationExists = false;
		}

		// return the value from the store
		if (translationExists === true) {
			return render(locale, translations[locale][key], options, pluralization);
		}

		// check if a regional locale translation would be available for the key
		// i.e. de for de-CH
		if (localeRegional.length > 1 &&
			translations.hasOwnProperty(localeRegional[0]) === true &&
			translations[localeRegional[0]].hasOwnProperty(key) === true) {
			return render(localeRegional[0], translations[localeRegional[0]][key], options, pluralization);
		}

		// invoke a method if a translation is not found
		let asyncTranslation = onTranslationNotFound(locale, key, defaultValue);

		// resolve async translations by updating the store
		if (asyncTranslation) {
			Promise.resolve(asyncTranslation).then((value) => {
				let additionalTranslations = {};
				additionalTranslations[key] = value;
				addLocale(locale, additionalTranslations);
			});
		}

		// check if a vaild fallback exists in the store.
		// return the default value if not
		if (translations.hasOwnProperty(fallback) === false ) {
			return render(locale, defaultValue, options, pluralization);
		}

		// check if the key exists in the fallback locale in the store.
		// return the default value if not
		if (translations[fallback].hasOwnProperty(key) === false) {
			return render(fallback, defaultValue, options, pluralization);
		}

		return render(locale, translations[fallback][key], options, pluralization);

	};

	// check if the given key exists in the current locale
	let checkKeyExists = function checkKeyExists(key, scope = 'fallback') {

		// get the current language from the store
		let locale = store.state[moduleName].locale;
		let fallback = store.state[moduleName].fallback;
		let translations = store.state[moduleName].translations;

		// check the current translation
		if (translations.hasOwnProperty(locale) && translations[locale].hasOwnProperty(key)) {
			return true;
		}

		if (scope == 'strict') {
			return false;
		}

		// check any localized translations
		let localeRegional = locale.split('-');

		if (localeRegional.length > 1 &&
			translations.hasOwnProperty(localeRegional[0]) &&
			translations[localeRegional[0]].hasOwnProperty(key)) {
			return true;
		}

		if (scope == 'locale') {
			return false;
		}

		// check if a fallback locale exists
		if (translations.hasOwnProperty(fallback) && translations[fallback].hasOwnProperty(key)) {
			return true;
		}

		// key does not exist in the store
		return false;
	};

	// set fallback locale
	let setFallbackLocale = function setFallbackLocale(locale) {
		store.dispatch({
			type: `${moduleName}/setFallbackLocale`,
			locale: locale
		});
	};

	// set the current locale
	let setLocale = function setLocale(locale) {
		store.dispatch({
			type: `${moduleName}/setLocale`,
			locale: locale
		});
	};

	// get the current locale
	let getLocale = function getLocale() {
		return store.state[moduleName].locale;
	};

	// add predefined translations to the store (keeping existing information)
	let addLocale = function addLocale(locale, translations) {
		return store.dispatch({
			type: `${moduleName}/addLocale`,
			locale: locale,
			translations: translations
		});
	};

	// replace all locale information in the store
	let replaceLocale = function replaceLocale(locale, translations) {
		return store.dispatch({
			type: `${moduleName}/replaceLocale`,
			locale: locale,
			translations: translations
		});
	};

	// remove the givne locale from the store
	let removeLocale = function removeLocale(locale) {
		if (store.state[moduleName].translations.hasOwnProperty(locale)) {
			store.dispatch({
				type: `${moduleName}/removeLocale`,
				locale: locale
			});
		}
	};

	// we are phasing out the exists function
	let phaseOutExistsFn = function phaseOutExistsFn(locale) {
		console.warn('i18n: $i18n.exists is depreceated. Please use $i18n.localeExists instead. It provides exatly the same functionality.');
		return checkLocaleExists(locale);
	};

	// check if the given locale is already loaded
	let checkLocaleExists = function checkLocaleExists(locale) {
		return store.state[moduleName].translations.hasOwnProperty(locale);
	};

	// register vue prototype methods
	Vue.prototype.$i18n = {
		locale: getLocale,
		set: setLocale,
		add: addLocale,
		replace: replaceLocale,
		remove: removeLocale,
		fallback: setFallbackLocale,
		localeExists: checkLocaleExists,
		keyExists: checkKeyExists,

		exists: phaseOutExistsFn
	};

	// register global methods
	Vue.i18n = {
		locale: getLocale,
		set: setLocale,
		add: addLocale,
		replace: replaceLocale,
		remove: removeLocale,
		fallback: setFallbackLocale,
		translate: translate,
		translateIn: translateInLanguage,
		localeExists: checkLocaleExists,
		keyExists: checkKeyExists,

		exists: phaseOutExistsFn
	};

	// register the translation function on the vue instance
	Vue.prototype.$t = translate;

	// register the specific language translation function on the vue instance
	Vue.prototype.$tlang = translateInLanguage;

	// register a filter function for translations
	Vue.filter('translate', translate);

};

// renderFn will initialize a function to render the variable substitutions in
// the translation string. identifiers specify the tags will be used to find
// variable substitutions, i.e. {test} or {{test}}, note that we are using a
// closure to avoid recompilation of the regular expression to match tags on
// every render cycle.
let renderFn = function(identifiers) {

	if (identifiers == null || identifiers.length != 2) {
		console.warn('i18n: You must specify the start and end character identifying variable substitutions');
	}

	// construct a regular expression ot find variable substitutions, i.e. {test}
	let matcher = new RegExp('' + identifiers[0] + '\\w+' + identifiers[1], 'g');

	// define the replacement function
	let replace = function replace(translation, replacements, warn = true) {

		// check if the object has a replace property
		if (!translation.replace) {
			return translation;
		}

		return translation.replace(matcher, function(placeholder) {

			// remove the identifiers (can be set on the module level)
			let key = placeholder.replace(identifiers[0], '').replace(identifiers[1], '');

			if (replacements[key] !== undefined) {
				return replacements[key];
			}

			// warn user that the placeholder has not been found
			if (warn === true) {
				console.group ? console.group('i18n: Not all placeholders found') : console.warn('i18n: Not all placeholders found');
				console.warn('Text:', translation);
				console.warn('Placeholder:', placeholder);
				if(console.groupEnd) {
					console.groupEnd();
				}
			}

			// return the original placeholder
			return placeholder;
		});
	};

	// the render function will replace variable substitutions and prepare the
	// translations for rendering
	let render = function render(locale, translation, replacements = {}, pluralization = null) {
		// get the type of the property
		let objType = typeof translation;
		let pluralizationType = typeof pluralization;

		let resolvePlaceholders = function() {

			if (isArray(translation)) {

				// replace the placeholder elements in all sub-items
				return translation.map((item) => {
					return replace(item, replacements, false);
				});

			} else if (objType === 'string') {
				return replace(translation, replacements, true);
			}

		};

			// return translation item directly
		if (pluralization === null) {
			return resolvePlaceholders();
		}

		// check if pluralization value is countable
		if (pluralizationType !== 'number') {
			console.warn('i18n: pluralization is not a number');
			return resolvePlaceholders();
		}

		// --- handle pluralizations ---

		// replace all placeholders
		let resolvedTranslation = resolvePlaceholders();

		// initialize pluralizations
		let pluralizations = null;

		// if translations are already an array and have more than one entry,
		// we will not perform a split operation on :::
		if (isArray(resolvedTranslation) && resolvedTranslation.length > 0) {
			pluralizations = resolvedTranslation;

		} else {
			// split translation strings by ::: to find create the pluralization array
			pluralizations = resolvedTranslation.split(':::');
		}

		// determine the pluralization version to use by locale
		let index = plurals.getTranslationIndex(locale, pluralization);

		// check if the specified index is present in the pluralization
		if(typeof pluralizations[index] === 'undefined') {
			console.warn('i18n: pluralization not provided in locale', translation, locale, index);

			// return the first element of the pluralization by default
			return pluralizations[0].trim();
		}

		// return the requested item from the pluralizations
		return pluralizations[index].trim();

	};

	// return the render function to the caller
	return render;

};

// check if the given object is an array
function isArray(obj) {
	return !!obj && Array === obj.constructor;
}

export default VuexI18nPlugin;
<commit_msg>add a method to get the available locales<commit_after>/* vuex-i18n defines the Vuexi18nPlugin to enable localization using a vuex
** module to store the translation information. Make sure to also include the
** file vuex-i18n-store.js to include a respective vuex module.
*/

import module from './vuex-i18n-store';
import plurals from './vuex-i18n-plurals';

// initialize the plugin object
let VuexI18nPlugin = {};

// internationalization plugin for vue js using vuex
VuexI18nPlugin.install = function install(Vue, store, config) {

	// TODO: remove this block for next major update (API break)
	if (typeof arguments[2] === 'string' || typeof arguments[3] === 'string') {
		console.warn('i18n: Registering the plugin vuex-i18n with a string for `moduleName` or `identifiers` is deprecated. Use a configuration object instead.', 'https://github.com/dkfbasel/vuex-i18n#setup');
		config = {
			moduleName: arguments[2],
			identifiers: arguments[3]
		};
	}

	// merge default options with user supplied options
	let mergedConfig = Object.assign({
		moduleName: 'i18n',
		identifiers: ['{', '}'],
		preserveState: false,
		onTranslationNotFound: function() {}
	}, config);

	// define module name and identifiers as constants to prevent any changes
	const moduleName = mergedConfig.moduleName;
	const identifiers = mergedConfig.identifiers;

	// initialize the onTranslationNotFound function and make sure it is actually
	// a function
	let onTranslationNotFound = mergedConfig.onTranslationNotFound;
	if (typeof onTranslationNotFound !== 'function') {
		console.error('i18n: i18n config option onTranslationNotFound must be a function');
		onTranslationNotFound = function() {};
	}

	// register the i18n module in the vuex store
	// preserveState can be used via configuration if server side rendering is used
	store.registerModule(moduleName, module, {preserveState: mergedConfig.preserveState});

	// check if the plugin was correctly initialized
	if (store.state.hasOwnProperty(moduleName) === false) {
		console.error('i18n: i18n vuex module is not correctly initialized. Please check the module name:', moduleName);

		// always return the key if module is not initialized correctly
		Vue.prototype.$i18n = function(key) {
			return key;
		};

		Vue.prototype.$getLanguage = function() {
			return null;
		};

		Vue.prototype.$setLanguage = function() {
			console.error('i18n: i18n vuex module is not correctly initialized');
		};

		return;
	};

	// initialize the replacement function
	let render = renderFn(identifiers);

	// get localized string from store. note that we pass the arguments passed
	// to the function directly to the translateInLanguage function
	let translate = function $t() {

		// get the current language from the store
		let locale = store.state[moduleName].locale;

		return translateInLanguage(locale, ...arguments);
	};

	// get localized string from store in a given language if available.
	// there are two possible signatures for the function.
	// we will check the arguments to make up the options passed.
	// 1: locale, key, options, pluralization
	// 2: locale, key, defaultValue, options, pluralization
	let translateInLanguage = function translateInLanguage(locale) {

		// read the function arguments
		let args = arguments;

		// initialize options
		let key = '';
		let defaultValue = '';
		let options = {};
		let pluralization = null;

		let count = args.length;

		// check if a default value was specified and fill options accordingly
		if (count >= 3 && typeof args[2] === 'string') {

			key = args[1];
			defaultValue = args[2];

			if (count > 3) {
				options = args[3];
			}

			if (count > 4) {
				pluralization = args[4];
			}

		} else {

			key = args[1];

			// default value was not specified and is therefore the same as the key
			defaultValue = key;

			if (count > 2) {
				options = args[2];
			}

			if (count > 3) {
				pluralization = args[3];
			}

		}

		// return the default value if the locale is not set (could happen on initialization)
		if (!locale) {
			console.warn('i18n: i18n locale is not set when trying to access translations:', key);
			return defaultValue;
		}

		// get the translations from the store
		let translations = store.state[moduleName].translations;

		// get the last resort fallback from the store
		let fallback = store.state[moduleName].fallback;

		// split locale by - to support partial fallback for regional locales
		// like de-CH, en-UK
		let localeRegional = locale.split('-');

		// flag for translation to exist or not
		let translationExists = true;

		// check if the language exists in the store. return the key if not
		if (translations.hasOwnProperty(locale) === false ) {
			translationExists = false;

		// check if the key exists in the store. return the key if not
		} else if (translations[locale].hasOwnProperty(key) === false) {
			translationExists = false;
		}

		// return the value from the store
		if (translationExists === true) {
			return render(locale, translations[locale][key], options, pluralization);
		}

		// check if a regional locale translation would be available for the key
		// i.e. de for de-CH
		if (localeRegional.length > 1 &&
			translations.hasOwnProperty(localeRegional[0]) === true &&
			translations[localeRegional[0]].hasOwnProperty(key) === true) {
			return render(localeRegional[0], translations[localeRegional[0]][key], options, pluralization);
		}

		// invoke a method if a translation is not found
		let asyncTranslation = onTranslationNotFound(locale, key, defaultValue);

		// resolve async translations by updating the store
		if (asyncTranslation) {
			Promise.resolve(asyncTranslation).then((value) => {
				let additionalTranslations = {};
				additionalTranslations[key] = value;
				addLocale(locale, additionalTranslations);
			});
		}

		// check if a vaild fallback exists in the store.
		// return the default value if not
		if (translations.hasOwnProperty(fallback) === false ) {
			return render(locale, defaultValue, options, pluralization);
		}

		// check if the key exists in the fallback locale in the store.
		// return the default value if not
		if (translations[fallback].hasOwnProperty(key) === false) {
			return render(fallback, defaultValue, options, pluralization);
		}

		return render(locale, translations[fallback][key], options, pluralization);

	};

	// check if the given key exists in the current locale
	let checkKeyExists = function checkKeyExists(key, scope = 'fallback') {

		// get the current language from the store
		let locale = store.state[moduleName].locale;
		let fallback = store.state[moduleName].fallback;
		let translations = store.state[moduleName].translations;

		// check the current translation
		if (translations.hasOwnProperty(locale) && translations[locale].hasOwnProperty(key)) {
			return true;
		}

		if (scope == 'strict') {
			return false;
		}

		// check any localized translations
		let localeRegional = locale.split('-');

		if (localeRegional.length > 1 &&
			translations.hasOwnProperty(localeRegional[0]) &&
			translations[localeRegional[0]].hasOwnProperty(key)) {
			return true;
		}

		if (scope == 'locale') {
			return false;
		}

		// check if a fallback locale exists
		if (translations.hasOwnProperty(fallback) && translations[fallback].hasOwnProperty(key)) {
			return true;
		}

		// key does not exist in the store
		return false;
	};

	// set fallback locale
	let setFallbackLocale = function setFallbackLocale(locale) {
		store.dispatch({
			type: `${moduleName}/setFallbackLocale`,
			locale: locale
		});
	};

	// set the current locale
	let setLocale = function setLocale(locale) {
		store.dispatch({
			type: `${moduleName}/setLocale`,
			locale: locale
		});
	};

	// get the current locale
	let getLocale = function getLocale() {
		return store.state[moduleName].locale;
	};

	// get all available locales
	let getLocales = function getLocales() {
		return Object.keys(store.state[moduleName].translations);
	};

	// add predefined translations to the store (keeping existing information)
	let addLocale = function addLocale(locale, translations) {
		return store.dispatch({
			type: `${moduleName}/addLocale`,
			locale: locale,
			translations: translations
		});
	};

	// replace all locale information in the store
	let replaceLocale = function replaceLocale(locale, translations) {
		return store.dispatch({
			type: `${moduleName}/replaceLocale`,
			locale: locale,
			translations: translations
		});
	};

	// remove the givne locale from the store
	let removeLocale = function removeLocale(locale) {
		if (store.state[moduleName].translations.hasOwnProperty(locale)) {
			store.dispatch({
				type: `${moduleName}/removeLocale`,
				locale: locale
			});
		}
	};

	// we are phasing out the exists function
	let phaseOutExistsFn = function phaseOutExistsFn(locale) {
		console.warn('i18n: $i18n.exists is depreceated. Please use $i18n.localeExists instead. It provides exatly the same functionality.');
		return checkLocaleExists(locale);
	};

	// check if the given locale is already loaded
	let checkLocaleExists = function checkLocaleExists(locale) {
		return store.state[moduleName].translations.hasOwnProperty(locale);
	};

	// register vue prototype methods
	Vue.prototype.$i18n = {
		locale: getLocale,
		locales: getLocales,
		set: setLocale,
		add: addLocale,
		replace: replaceLocale,
		remove: removeLocale,
		fallback: setFallbackLocale,
		localeExists: checkLocaleExists,
		keyExists: checkKeyExists,

		exists: phaseOutExistsFn
	};

	// register global methods
	Vue.i18n = {
		locale: getLocale,
		locales: getLocales,
		set: setLocale,
		add: addLocale,
		replace: replaceLocale,
		remove: removeLocale,
		fallback: setFallbackLocale,
		translate: translate,
		translateIn: translateInLanguage,
		localeExists: checkLocaleExists,
		keyExists: checkKeyExists,

		exists: phaseOutExistsFn
	};

	// register the translation function on the vue instance
	Vue.prototype.$t = translate;

	// register the specific language translation function on the vue instance
	Vue.prototype.$tlang = translateInLanguage;

	// register a filter function for translations
	Vue.filter('translate', translate);

};

// renderFn will initialize a function to render the variable substitutions in
// the translation string. identifiers specify the tags will be used to find
// variable substitutions, i.e. {test} or {{test}}, note that we are using a
// closure to avoid recompilation of the regular expression to match tags on
// every render cycle.
let renderFn = function(identifiers) {

	if (identifiers == null || identifiers.length != 2) {
		console.warn('i18n: You must specify the start and end character identifying variable substitutions');
	}

	// construct a regular expression ot find variable substitutions, i.e. {test}
	let matcher = new RegExp('' + identifiers[0] + '\\w+' + identifiers[1], 'g');

	// define the replacement function
	let replace = function replace(translation, replacements, warn = true) {

		// check if the object has a replace property
		if (!translation.replace) {
			return translation;
		}

		return translation.replace(matcher, function(placeholder) {

			// remove the identifiers (can be set on the module level)
			let key = placeholder.replace(identifiers[0], '').replace(identifiers[1], '');

			if (replacements[key] !== undefined) {
				return replacements[key];
			}

			// warn user that the placeholder has not been found
			if (warn === true) {
				console.group ? console.group('i18n: Not all placeholders found') : console.warn('i18n: Not all placeholders found');
				console.warn('Text:', translation);
				console.warn('Placeholder:', placeholder);
				if(console.groupEnd) {
					console.groupEnd();
				}
			}

			// return the original placeholder
			return placeholder;
		});
	};

	// the render function will replace variable substitutions and prepare the
	// translations for rendering
	let render = function render(locale, translation, replacements = {}, pluralization = null) {
		// get the type of the property
		let objType = typeof translation;
		let pluralizationType = typeof pluralization;

		let resolvePlaceholders = function() {

			if (isArray(translation)) {

				// replace the placeholder elements in all sub-items
				return translation.map((item) => {
					return replace(item, replacements, false);
				});

			} else if (objType === 'string') {
				return replace(translation, replacements, true);
			}

		};

			// return translation item directly
		if (pluralization === null) {
			return resolvePlaceholders();
		}

		// check if pluralization value is countable
		if (pluralizationType !== 'number') {
			console.warn('i18n: pluralization is not a number');
			return resolvePlaceholders();
		}

		// --- handle pluralizations ---

		// replace all placeholders
		let resolvedTranslation = resolvePlaceholders();

		// initialize pluralizations
		let pluralizations = null;

		// if translations are already an array and have more than one entry,
		// we will not perform a split operation on :::
		if (isArray(resolvedTranslation) && resolvedTranslation.length > 0) {
			pluralizations = resolvedTranslation;

		} else {
			// split translation strings by ::: to find create the pluralization array
			pluralizations = resolvedTranslation.split(':::');
		}

		// determine the pluralization version to use by locale
		let index = plurals.getTranslationIndex(locale, pluralization);

		// check if the specified index is present in the pluralization
		if(typeof pluralizations[index] === 'undefined') {
			console.warn('i18n: pluralization not provided in locale', translation, locale, index);

			// return the first element of the pluralization by default
			return pluralizations[0].trim();
		}

		// return the requested item from the pluralizations
		return pluralizations[index].trim();

	};

	// return the render function to the caller
	return render;

};

// check if the given object is an array
function isArray(obj) {
	return !!obj && Array === obj.constructor;
}

export default VuexI18nPlugin;
<|endoftext|>


<commit_before>
$(function() {

    function request_permission() {
        // Try to get permission to show Notifications
        if (window.Notification && Notification.permission !== "granted") {
            Notification.requestPermission(function (status) {
                if (Notification.permission !== status) {
                    Notification.permission = status;
                }
            });
        }
    }

    request_permission();
    $('#request_permission').click(function(){ request_permission(); return false; });

    function notify(input) {
        var icon_url = "/static/img/notification_medium.png";
        var title = input.show.user.names + " started streaming";
        if (input.show.name) {
            var body = "Show: " + input.show.name;
        }
        else {
            var body = "Show has no name ;_;";
        }

        if (window.Notification && Notification.permission === "granted") {
            var n = new Notification(title, {body: body, icon: icon_url, tag: 'show_start'});
        }

        // If the user hasn't told if he wants to be notified or not
        // Note: because of Chrome, we are not sure the permission property
        // is set, therefore it's unsafe to check for the "default" value.
        else if (window.Notification && Notification.permission !== "denied") {
            Notification.requestPermission(function (status) {
                if (Notification.permission !== status) {
                    Notification.permission = status;
                }

                // If the user said okay
                if (status === "granted") {
                    var n = new Notification(title, {body: body, icon: icon_url, tag: 'show_start'});
                }
            });
        }
    }

    setTimeout(function() {
        $(document).on('show_started', notify);
        $(document).on('show_changed', notify);
    }, 5000);

});
<commit_msg>Don't always ask for HTML5 notification permission (fixes #18)<commit_after>$(function() {

    function request_permission() {
        // Try to get permission to show Notifications
        if (window.Notification && Notification.permission !== "granted") {
            Notification.requestPermission(function (status) {
                if (Notification.permission !== status) {
                    Notification.permission = status;
                }
            });
        }
    }

    $('#request_permission').click(function(){ request_permission(); return false; });

    function notify(input) {
        var icon_url = "/static/img/notification_medium.png";
        var title = input.show.user.names + " started streaming";
        if (input.show.name) {
            var body = "Show: " + input.show.name;
        }
        else {
            var body = "Show has no name ;_;";
        }

        if (window.Notification && Notification.permission === "granted") {
            var n = new Notification(title, {body: body, icon: icon_url, tag: 'show_start'});
        }
    }

    setTimeout(function() {
        $(document).on('show_started', notify);
        $(document).on('show_changed', notify);
    }, 5000);

});
<|endoftext|>


<commit_before>/* vuex-i18n defines the Vuexi18nPlugin to enable localization using a vuex
** module to store the translation information. Make sure to also include the
** file vuex-i18n-store.js to include a respective vuex module.
*/

// initialize the plugin object
let VuexI18nPlugin = {};

// internationalization plugin for vue js using vuex
VuexI18nPlugin.install = function install(Vue, store, moduleName = 'i18n') {

	// check if the plugin was correctly initialized
	if (store.state.hasOwnProperty(moduleName) === false) {
		console.error('i18n vuex module is not correctly initialized. Please check the module name:', moduleName);

		// always return the key if module is not initialized correctly
		Vue.prototype.$i18n = function(key) {
			return key;
		};

		Vue.prototype.$getLanguage = function() {
			return null;
		};

		Vue.prototype.$setLanguage = function() {
			console.error('i18n vuex module is not correctly initialized');
		};

		return;
	};

	// get localized string from store
	Vue.prototype.$t = function $t(key, options) {

		// get the current language from the store
		var locale = store.state[moduleName].locale;

		// check if the language exists in the store. return the key if not
		if (store.state[moduleName].translations.hasOwnProperty(locale) === false ) {
			return render(key, options);
		}

		// check if the key exists in the store. return the key if not
		if (store.state[moduleName].translations[locale].hasOwnProperty(key) === false) {
			return render(key, options);
		}

		// return the value from the store
		return render(store.state[moduleName].translations[locale][key], options);
	};

	var setLocale = function setLocale(locale) {
		store.dispatch({
			type: 'setLocale',
			locale: locale
		});
	};

	var getLocale = function getLocale() {
		return store.state[moduleName].locale;
	};

	// add predefined translations to the store
	var addLocale = function addLocale(locale, translations) {
		return store.dispatch({
			type: 'addLocale',
			locale: locale,
			translations: translations
		});
	};

	// remove the givne locale from the store
	var removeLocale = function removeLocale(locale) {
		if (store.state[moduleName].translations.hasOwnProperty(locale)) {
			store.dispatch({
				type: 'removeLocale',
				locale: locale
			});
		}
	};

	// check if the given locale is already loaded
	var checkLocaleExists = function checkLocaleExists(locale) {
		return store.state[moduleName].translations.hasOwnProperty(locale);
	};

	Vue.prototype.$i18n = {
		locale: getLocale,
		set: setLocale,
		add: addLocale,
		remove: removeLocale,
		exists: checkLocaleExists
	};

	Vue.i18n = {
		locale: getLocale,
		set: setLocale,
		add: addLocale,
		remove: removeLocale,
		exists: checkLocaleExists
	};

};

// render the given translation with placeholder replaced
let render = function render(translation, replacements = {}) {

	return translation.replace(/\{\w+\}/g, function(placeholder) {

		var key = placeholder.replace('{', '').replace('}', '');

		if (replacements[key] !== undefined) {
			return replacements[key];
		}

		// warn user that the placeholder has not been found
		console.group('Not all placeholder founds');
		console.warn('Text:', translation);
		console.warn('Placeholder:', placeholder);
		console.groupEnd();

		// return the original placeholder
		return placeholder;
	});

};

export default VuexI18nPlugin;
<commit_msg>add a translation filter<commit_after>/* vuex-i18n defines the Vuexi18nPlugin to enable localization using a vuex
** module to store the translation information. Make sure to also include the
** file vuex-i18n-store.js to include a respective vuex module.
*/

// initialize the plugin object
let VuexI18nPlugin = {};

// internationalization plugin for vue js using vuex
VuexI18nPlugin.install = function install(Vue, store, moduleName = 'i18n') {

	// check if the plugin was correctly initialized
	if (store.state.hasOwnProperty(moduleName) === false) {
		console.error('i18n vuex module is not correctly initialized. Please check the module name:', moduleName);

		// always return the key if module is not initialized correctly
		Vue.prototype.$i18n = function(key) {
			return key;
		};

		Vue.prototype.$getLanguage = function() {
			return null;
		};

		Vue.prototype.$setLanguage = function() {
			console.error('i18n vuex module is not correctly initialized');
		};

		return;
	};

	// get localized string from store
	var translate = function $t(key, options) {

		// get the current language from the store
		var locale = store.state[moduleName].locale;

		// check if the language exists in the store. return the key if not
		if (store.state[moduleName].translations.hasOwnProperty(locale) === false ) {
			return render(key, options);
		}

		// check if the key exists in the store. return the key if not
		if (store.state[moduleName].translations[locale].hasOwnProperty(key) === false) {
			return render(key, options);
		}

		// return the value from the store
		return render(store.state[moduleName].translations[locale][key], options);
	};

	var setLocale = function setLocale(locale) {
		store.dispatch({
			type: 'setLocale',
			locale: locale
		});
	};

	var getLocale = function getLocale() {
		return store.state[moduleName].locale;
	};

	// add predefined translations to the store
	var addLocale = function addLocale(locale, translations) {
		return store.dispatch({
			type: 'addLocale',
			locale: locale,
			translations: translations
		});
	};

	// remove the givne locale from the store
	var removeLocale = function removeLocale(locale) {
		if (store.state[moduleName].translations.hasOwnProperty(locale)) {
			store.dispatch({
				type: 'removeLocale',
				locale: locale
			});
		}
	};

	// check if the given locale is already loaded
	var checkLocaleExists = function checkLocaleExists(locale) {
		return store.state[moduleName].translations.hasOwnProperty(locale);
	};

	// register vue prototype methods
	Vue.prototype.$i18n = {
		locale: getLocale,
		set: setLocale,
		add: addLocale,
		remove: removeLocale,
		exists: checkLocaleExists
	};

	// register global methods
	Vue.i18n = {
		locale: getLocale,
		set: setLocale,
		add: addLocale,
		remove: removeLocale,
		exists: checkLocaleExists,
		translate: translate
	};

	// register the translation function on the vue instance
	Vue.prototype.$t = translate;

	// register a filter function for translations
	Vue.filter('translate', translate);

};

// render the given translation with placeholder replaced
let render = function render(translation, replacements = {}) {

	return translation.replace(/\{\w+\}/g, function(placeholder) {

		var key = placeholder.replace('{', '').replace('}', '');

		if (replacements[key] !== undefined) {
			return replacements[key];
		}

		// warn user that the placeholder has not been found
		console.group('Not all placeholder founds');
		console.warn('Text:', translation);
		console.warn('Placeholder:', placeholder);
		console.groupEnd();

		// return the original placeholder
		return placeholder;
	});

};

export default VuexI18nPlugin;
<|endoftext|>