Просмотр исходного кода

Merge pull request #2414 from overleaf/ta-features-overrides

Persistent Features Override

GitOrigin-RevId: 9bb389458a4fde3cc36dbc5b8c3f7185b7e4e9bf
Eric Mc Sween 6 лет назад
Родитель
Сommit
a493ec94b2

+ 39 - 3
services/web/app/src/Features/Subscription/FeaturesUpdater.js

@@ -50,6 +50,9 @@ const FeaturesUpdater = {
       },
       samlFeatures(cb) {
         return FeaturesUpdater._getSamlFeatures(user_id, cb)
+      },
+      featuresOverrides(cb) {
+        return FeaturesUpdater._getFeaturesOverrides(user_id, cb)
       }
     }
     return async.series(jobs, function(err, results) {
@@ -67,7 +70,8 @@ const FeaturesUpdater = {
         institutionFeatures,
         v1Features,
         bonusFeatures,
-        samlFeatures
+        samlFeatures,
+        featuresOverrides
       } = results
       logger.log(
         {
@@ -77,7 +81,8 @@ const FeaturesUpdater = {
           institutionFeatures,
           v1Features,
           bonusFeatures,
-          samlFeatures
+          samlFeatures,
+          featuresOverrides
         },
         'merging user features'
       )
@@ -86,7 +91,8 @@ const FeaturesUpdater = {
         institutionFeatures,
         v1Features,
         bonusFeatures,
-        samlFeatures
+        samlFeatures,
+        featuresOverrides
       ])
       const features = _.reduce(
         featureSets,
@@ -143,6 +149,36 @@ const FeaturesUpdater = {
     })
   },
 
+  _getFeaturesOverrides(user_id, callback) {
+    UserGetter.getUser(user_id, { featuresOverrides: 1 }, (error, user) => {
+      if (error) {
+        return callback(error)
+      }
+      if (
+        !user ||
+        !user.featuresOverrides ||
+        user.featuresOverrides.length === 0
+      ) {
+        return callback(null, {})
+      }
+      let activeFeaturesOverrides = []
+      for (let featuresOverride of user.featuresOverrides) {
+        if (
+          !featuresOverride.expiresAt ||
+          featuresOverride.expiresAt > new Date()
+        ) {
+          activeFeaturesOverrides.push(featuresOverride.features)
+        }
+      }
+      const features = _.reduce(
+        activeFeaturesOverrides,
+        FeaturesUpdater._mergeFeatures,
+        {}
+      )
+      return callback(null, features)
+    })
+  },
+
   _getV1Features(user_id, callback) {
     if (callback == null) {
       callback = function(error, features) {}

+ 26 - 0
services/web/app/src/models/User.js

@@ -91,6 +91,32 @@ const UserSchema = new Schema({
       default: Settings.defaultFeatures.referencesSearch
     }
   },
+  featuresOverrides: [
+    {
+      createdAt: {
+        type: Date,
+        default() {
+          return new Date()
+        }
+      },
+      expiresAt: { type: Date },
+      note: { type: String },
+      features: {
+        collaborators: { type: Number },
+        versioning: { type: Boolean },
+        dropbox: { type: Boolean },
+        github: { type: Boolean },
+        gitBridge: { type: Boolean },
+        compileTimeout: { type: Number },
+        compileGroup: { type: String },
+        templates: { type: Boolean },
+        trackChanges: { type: Boolean },
+        mendeley: { type: Boolean },
+        zotero: { type: Boolean },
+        referencesSearch: { type: Boolean }
+      }
+    }
+  ],
   // when auto-merged from SL and must-reconfirm is set, we may end up using
   // `sharelatexHashedPassword` to recover accounts...
   sharelatexHashedPassword: String,

+ 47 - 0
services/web/test/acceptance/src/FeatureUpdaterTests.js

@@ -338,4 +338,51 @@ describe('FeatureUpdater.refreshFeatures', function() {
       )
     }) // returns a promise
   })
+
+  describe('when the user has features overrides', function() {
+    beforeEach(function() {
+      const futureDate = new Date()
+      futureDate.setDate(futureDate.getDate() + 1)
+      return User.update(
+        {
+          _id: this.user._id
+        },
+        {
+          featuresOverrides: [
+            {
+              features: {
+                github: true
+              }
+            },
+            {
+              features: {
+                dropbox: true
+              },
+              expiresAt: new Date(1990, 12, 25)
+            },
+            {
+              features: {
+                trackChanges: true
+              },
+              expiresAt: futureDate
+            }
+          ]
+        }
+      )
+    }) // returns a promise
+
+    it('should set their features to the overridden set', function(done) {
+      return syncUserAndGetFeatures(this.user, (error, features) => {
+        if (error != null) {
+          throw error
+        }
+        let expectedFeatures = Object.assign(settings.defaultFeatures, {
+          github: true,
+          trackChanges: true
+        })
+        expect(features).to.deep.equal(expectedFeatures)
+        return done()
+      })
+    })
+  })
 })

+ 5 - 0
services/web/test/acceptance/src/helpers/User.js

@@ -142,6 +142,11 @@ class User {
     UserModel.update({ _id: this.id }, update, callback)
   }
 
+  setFeaturesOverride(featuresOverride, callback) {
+    const update = { $push: { featuresOverrides: featuresOverride } }
+    UserModel.update({ _id: this.id }, update, callback)
+  }
+
   setOverleafId(overleafId, callback) {
     UserModel.update({ _id: this.id }, { 'overleaf.id': overleafId }, callback)
   }