async = require("async") PlansLocator = require("./PlansLocator") _ = require("underscore") SubscriptionLocator = require("./SubscriptionLocator") UserFeaturesUpdater = require("./UserFeaturesUpdater") Settings = require("settings-sharelatex") logger = require("logger-sharelatex") ReferalFeatures = require("../Referal/ReferalFeatures") V1SubscriptionManager = require("./V1SubscriptionManager") oneMonthInSeconds = 60 * 60 * 24 * 30 module.exports = FeaturesUpdater = refreshFeatures: (user_id, notifyV1 = true, callback = () ->)-> if typeof notifyV1 == 'function' callback = notifyV1 notifyV1 = true if notifyV1 V1SubscriptionManager.notifyV1OfFeaturesChange user_id, (error) -> if error? logger.err {err: error, user_id}, "error notifying v1 about updated features" jobs = individualFeatures: (cb) -> FeaturesUpdater._getIndividualFeatures user_id, cb groupFeatureSets: (cb) -> FeaturesUpdater._getGroupFeatureSets user_id, cb v1Features: (cb) -> FeaturesUpdater._getV1Features user_id, cb bonusFeatures: (cb) -> ReferalFeatures.getBonusFeatures user_id, cb async.series jobs, (err, results)-> if err? logger.err err:err, user_id:user_id, "error getting subscription or group for refreshFeatures" return callback(err) {individualFeatures, groupFeatureSets, v1Features, bonusFeatures} = results logger.log {user_id, individualFeatures, groupFeatureSets, v1Features, bonusFeatures}, 'merging user features' featureSets = groupFeatureSets.concat [individualFeatures, v1Features, bonusFeatures] features = _.reduce(featureSets, FeaturesUpdater._mergeFeatures, Settings.defaultFeatures) logger.log {user_id, features}, 'updating user features' UserFeaturesUpdater.updateFeatures user_id, features, callback _getIndividualFeatures: (user_id, callback = (error, features = {}) ->) -> SubscriptionLocator.getUsersSubscription user_id, (err, sub)-> callback err, FeaturesUpdater._subscriptionToFeatures(sub) _getGroupFeatureSets: (user_id, callback = (error, featureSets = []) ->) -> SubscriptionLocator.getGroupSubscriptionsMemberOf user_id, (err, subs) -> callback err, (subs or []).map FeaturesUpdater._subscriptionToFeatures _getV1Features: (user_id, callback = (error, features = {}) ->) -> V1SubscriptionManager.getPlanCodeFromV1 user_id, (err, planCode) -> callback err, FeaturesUpdater._planCodeToFeatures(planCode) _mergeFeatures: (featuresA, featuresB) -> features = Object.assign({}, featuresA) for key, value of featuresB # Special merging logic for non-boolean features if key == 'compileGroup' if features['compileGroup'] == 'priority' or featuresB['compileGroup'] == 'priority' features['compileGroup'] = 'priority' else features['compileGroup'] = 'standard' else if key == 'collaborators' if features['collaborators'] == -1 or featuresB['collaborators'] == -1 features['collaborators'] = -1 else features['collaborators'] = Math.max( features['collaborators'] or 0, featuresB['collaborators'] or 0 ) else if key == 'compileTimeout' features['compileTimeout'] = Math.max( features['compileTimeout'] or 0, featuresB['compileTimeout'] or 0 ) else # Boolean keys, true is better features[key] = features[key] or featuresB[key] return features _subscriptionToFeatures: (subscription) -> FeaturesUpdater._planCodeToFeatures(subscription?.planCode) _planCodeToFeatures: (planCode) -> if !planCode? return {} plan = PlansLocator.findLocalPlanInSettings planCode if !plan? return {} else return plan.features