diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 0eebfc0f9b8fa4a114826e30b8c3877168bd8a2d..c5048cadca89397b43402858564b02a6692a8e2e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,7 +1,8 @@
 stages:
-  - build
   - Static analysis
-
+  - build
+  - deploy
+  
 variables:
   SONAR_PROJECTKEY: "$CI_PROJECT_NAME"
   SONAR_HOST_URL: "https://sonarqube.neogeo.fr"
@@ -23,6 +24,17 @@ build testing docker image:
     - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination neogeo/geocontrib-front:testing
     - echo Image docker neogeo/geocontrib-front:testing livrée
 
+deploy testing docker image:
+  stage: deploy
+  only:
+    - develop
+  tags:
+    - build
+  image:
+    name: curlimages/curl
+  script:
+    - curl -X POST      -F token=$TRIGGER_TOKEN -F ref=main      https://git.neogeo.fr/api/v4/projects/226/trigger/pipeline
+
 build stable docker image:
   stage: build
   only:
diff --git a/Dockerfile b/Dockerfile
index 307c845f6c400793a1dab711dd633c2842587108..cc27e038c58e349e28dccf75daccde01322bb594 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -16,5 +16,14 @@ RUN mkdir /usr/share/nginx/html/geocontrib
 COPY --from=builder /app/dist /usr/share/nginx/html/geocontrib
 COPY nginx.conf /etc/nginx/conf.d/default.conf 
 
+RUN useradd -r -m apprunner
+
+RUN mkdir -p /opt/geocontrib/media /opt/geocontrib/static /opt/geocontrib/config/ && \
+    chown -R apprunner /opt/geocontrib/ && \
+    rm -Rf /usr/share/nginx/html/geocontrib/config/ && \
+    ln -s /opt/geocontrib/config/ /usr/share/nginx/html/geocontrib/config
+
+VOLUME /opt/geocontrib/media /opt/geocontrib/static /opt/geocontrib/config/
+
 EXPOSE 80
 CMD ["nginx", "-g", "daemon off;"]
diff --git a/src/views/NotFound.vue b/src/views/NotFound.vue
index 45383de0811961bfbe763d28584eecabf24efc8b..57d7e54f229a9ed836f9de938c0265bbb4dfb6d6 100644
--- a/src/views/NotFound.vue
+++ b/src/views/NotFound.vue
@@ -2,8 +2,8 @@
   <div>
     <h1>Not Found</h1>
     <p>
-      Oups, la page demandée n'a pas été trouvé. Essayez de retourner à la
-      <router-link to="/">page d'acceuil</router-link>
+      Oups, la page demandée n'a pas été trouvée. Essayez de retourner à la
+      <router-link to="/">page d'accueil</router-link>
     </p>
   </div>
-</template>
\ No newline at end of file
+</template>
diff --git a/src/views/feature/Feature_edit.vue b/src/views/feature/Feature_edit.vue
index 4916b6ba1b3aa8553eecadc15f864b3503aab495..6502947fcb45afe9ab0a5c2c4d9af894cf95884e 100644
--- a/src/views/feature/Feature_edit.vue
+++ b/src/views/feature/Feature_edit.vue
@@ -346,7 +346,7 @@ export default {
   },
 
   computed: {
-    ...mapGetters(["project"]),
+    ...mapGetters(["project", "permissions"]),
     ...mapGetters("feature_type", ["feature_type"]),
     ...mapState(["user", "USER_LEVEL_PROJECTS"]),
     ...mapState("map", ["basemaps"]),
@@ -689,6 +689,15 @@ export default {
       }
 
       if (is_valid) {
+        //* if moderate project modified by someone else than admin or moderator, switch status to pending
+        if (
+          this.project.moderation &&
+          !this.permissions.is_project_administrator &&
+          !this.permissions.is_project_moderator
+        ) {
+          this.form.status.value = { name: "Brouillon", value: "draft" };
+          this.updateStore();
+        }
         this.$store.dispatch("feature/SEND_FEATURE", this.currentRouteName);
       }
     },
@@ -921,7 +930,7 @@ export default {
       setTimeout(
         function () {
           let project_id = this.$route.params.slug.split("-")[0];
-          const mvtUrl = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}/features.mvt/?tile={z}/{x}/{y}&project_id=${project_id}`;
+          const mvtUrl = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}features.mvt/?tile={z}/{x}/{y}&project_id=${project_id}`;
 
           mapUtil.addVectorTileLayer(
             mvtUrl,
diff --git a/src/views/feature/Feature_list.vue b/src/views/feature/Feature_list.vue
index b86b137acf5366c17f40509a440b35c175c9e64f..a9f9fb4a9e67a2fe57881e470d1cbdbd2dda80cf 100644
--- a/src/views/feature/Feature_list.vue
+++ b/src/views/feature/Feature_list.vue
@@ -313,7 +313,7 @@ export default {
               .dispatch("feature/GET_PROJECT_FEATURES", this.project.slug)
               .then(() => {
                 this.getNloadGeojsonFeatures();
-                this.checkedFeatures.splice(feature_id)
+                this.checkedFeatures.splice(feature_id);
               });
           }
         })
@@ -384,7 +384,7 @@ export default {
       setTimeout(
         function () {
           let project_id = this.$route.params.slug.split("-")[0];
-          const mvtUrl = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}/features.mvt/?tile={z}/{x}/{y}&project_id=${project_id}`;
+          const mvtUrl = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}features.mvt/?tile={z}/{x}/{y}&project_id=${project_id}`;
           mapUtil.addVectorTileLayer(
             mvtUrl,
             this.$route.params.slug,
diff --git a/src/views/project/Project_detail.vue b/src/views/project/Project_detail.vue
index 84888b12a4054e5fe90f51046cb8e55d1516b00c..ee2ea87f07ed1227a706d0385ace4f0890eda9c6 100644
--- a/src/views/project/Project_detail.vue
+++ b/src/views/project/Project_detail.vue
@@ -52,19 +52,24 @@
           <h1 class="ui header">
             <div class="content">
               {{ project.title }}
-              <div v-if="arraysOffline.length>0">{{arraysOffline.length}} modifications en attente
+              <div v-if="arraysOffline.length > 0">
+                {{ arraysOffline.length }} modifications en attente
                 <button
-                :disabled="isOffline()"
-                @click="sendOfflineFeatures()"
-                class="ui fluid teal icon button"
-              >
-                <i class="upload icon"></i> Envoyer au serveur
-              </button>
-
+                  :disabled="isOffline()"
+                  @click="sendOfflineFeatures()"
+                  class="ui fluid teal icon button"
+                >
+                  <i class="upload icon"></i> Envoyer au serveur
+                </button>
               </div>
               <div class="ui icon right floated compact buttons">
                 <a
-                  v-if="user && permissions && permissions.can_view_project && isOffline()!=true"
+                  v-if="
+                    user &&
+                    permissions &&
+                    permissions.can_view_project &&
+                    isOffline() != true
+                  "
                   id="subscribe-button"
                   class="ui button button-hover-green"
                   data-tooltip="S'abonner au projet"
@@ -75,7 +80,11 @@
                   <i class="inverted grey envelope icon"></i>
                 </a>
                 <router-link
-                  v-if="permissions && permissions.can_update_project && isOffline()!=true"
+                  v-if="
+                    permissions &&
+                    permissions.can_update_project &&
+                    isOffline() != true
+                  "
                   :to="{ name: 'project_edit', params: { slug: project.slug } }"
                   class="ui button button-hover-orange"
                   data-tooltip="Modifier le projet"
@@ -166,7 +175,8 @@
                   v-if="
                     project &&
                     permissions &&
-                    permissions.can_create_feature_type && isOffline()!=true
+                    permissions.can_create_feature_type &&
+                    isOffline() != true
                   "
                   class="
                     ui
@@ -192,7 +202,8 @@
                     project &&
                     type.is_editable &&
                     permissions &&
-                    permissions.can_create_feature_type && isOffline()!=true
+                    permissions.can_create_feature_type &&
+                    isOffline() != true
                   "
                   class="
                     ui
@@ -218,7 +229,11 @@
 
           <div class="nouveau-type-signalement">
             <router-link
-              v-if="permissions && permissions.can_update_project && isOffline()!=true"
+              v-if="
+                permissions &&
+                permissions.can_update_project &&
+                isOffline() != true
+              "
               :to="{
                 name: 'ajouter-type-signalement',
                 params: { slug: project.slug },
@@ -230,7 +245,11 @@
           </div>
           <div class="nouveau-type-signalement">
             <a
-              v-if="permissions && permissions.can_update_project && isOffline()!=true"
+              v-if="
+                permissions &&
+                permissions.can_update_project &&
+                isOffline() != true
+              "
               class="
                 ui
                 compact
@@ -531,67 +550,75 @@ export default {
     refreshId() {
       return "?ver=" + Math.random();
     },
-    isOffline(){
-      return navigator.onLine==false;
+    isOffline() {
+      return navigator.onLine == false;
     },
-    checkForOfflineFeature(){
-      let arraysOffline=[];
-      let localStorageArray=localStorage.getItem("geocontrib_offline");
-      if(localStorageArray){
-        arraysOffline=JSON.parse(localStorageArray);
-        this.arraysOffline=arraysOffline.filter(x=>x.project==this.project.slug);
+    checkForOfflineFeature() {
+      let arraysOffline = [];
+      let localStorageArray = localStorage.getItem("geocontrib_offline");
+      if (localStorageArray) {
+        arraysOffline = JSON.parse(localStorageArray);
+        this.arraysOffline = arraysOffline.filter(
+          (x) => x.project == this.project.slug
+        );
       }
     },
-    sendOfflineFeatures(){
+    sendOfflineFeatures() {
       var promises = [];
-      this.arraysOffline.forEach((feature, index, object)=>{
+      this.arraysOffline.forEach((feature, index, object) => {
         console.log(feature);
-        if(feature.type=='post') {
+        if (feature.type == "post") {
           promises.push(
-          axios
-          .post(`${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}features/`, feature.geojson)
-          .then((response) => {
-            console.log(response)
-            if (response.status === 201 && response.data) {
-              object.splice(index, 1);
-            }
-          })
-          .catch((error) => {
-            console.log(error);
-          }));
-        }
-        else if(feature.type=='put') {
+            axios
+              .post(
+                `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}features/`,
+                feature.geojson
+              )
+              .then((response) => {
+                console.log(response);
+                if (response.status === 201 && response.data) {
+                  object.splice(index, 1);
+                }
+              })
+              .catch((error) => {
+                console.log(error);
+              })
+          );
+        } else if (feature.type == "put") {
           promises.push(
-          axios
-          .put(`${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}features/${feature.featureId}`, feature.geojson)
-          .then((response) => {
-            console.log(response)
-            if (response.status === 200 && response.data) {
-              object.splice(index, 1);
-            }
-          })
-          .catch((error) => {
-            console.log(error);
-          }));
+            axios
+              .put(
+                `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}features/${feature.featureId}`,
+                feature.geojson
+              )
+              .then((response) => {
+                console.log(response);
+                if (response.status === 200 && response.data) {
+                  object.splice(index, 1);
+                }
+              })
+              .catch((error) => {
+                console.log(error);
+              })
+          );
         }
       });
       Promise.all(promises).then(() => {
-          this.updateLocalStorage();
-          window.location.reload();
-      }
-
-      );
-      
+        this.updateLocalStorage();
+        window.location.reload();
+      });
     },
-    updateLocalStorage(){
-      let arraysOffline=[];
-      let localStorageArray=localStorage.getItem("geocontrib_offline");
-      if(localStorageArray){
-        arraysOffline=JSON.parse(localStorageArray);
+    updateLocalStorage() {
+      let arraysOffline = [];
+      let localStorageArray = localStorage.getItem("geocontrib_offline");
+      if (localStorageArray) {
+        arraysOffline = JSON.parse(localStorageArray);
       }
-      let arraysOfflineOtherProject = arraysOffline.filter(x=>x.project!=this.project.slug);
-      arraysOffline=arraysOfflineOtherProject.concat(this.arraysOffline);
-      localStorage.setItem("geocontrib_offline",JSON.stringify(arraysOffline));
+      let arraysOfflineOtherProject = arraysOffline.filter(
+        (x) => x.project != this.project.slug
+      );
+      arraysOffline = arraysOfflineOtherProject.concat(this.arraysOffline);
+      localStorage.setItem("geocontrib_offline", JSON.stringify(arraysOffline));
     },
     toNewFeatureType() {
       this.$router.push({
@@ -642,54 +669,65 @@ export default {
           setTimeout(() => (this.infoMessage = ""), 3000);
         });
     },
-    initMap(){
-        if (this.project && this.permissions.can_view_project) {
-          this.$store.dispatch("map/INITIATE_MAP");
-          const url = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}projects/${this.$route.params.slug}/feature/?output=geojson`;
-          let self = this;
-          this.checkForOfflineFeature();
-          let project_id=this.$route.params.slug.split('-')[0];
-          const mvtUrl = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}/features.mvt/?tile={z}/{x}/{y}&project_id=${project_id}`;
-          mapUtil.addVectorTileLayer(mvtUrl,this.$route.params.slug,this.$store.state.feature_type.feature_types);
-          axios
-            .get(url)
-            .then((response) => {
-              let features = response.data.features;
-              self.arraysOffline.forEach(x=>x.geojson.properties.color="red");
-              features=response.data.features.concat(self.arraysOffline.map(x=>x.geojson));
-              const featureGroup = mapUtil.addFeatures(features,{},false,this.$store.state.feature_type.feature_types);
+    initMap() {
+      if (this.project && this.permissions.can_view_project) {
+        this.$store.dispatch("map/INITIATE_MAP");
+        const url = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}projects/${this.$route.params.slug}/feature/?output=geojson`;
+        let self = this;
+        this.checkForOfflineFeature();
+        let project_id = this.$route.params.slug.split("-")[0];
+        const mvtUrl = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}features.mvt/?tile={z}/{x}/{y}&project_id=${project_id}`;
+        mapUtil.addVectorTileLayer(
+          mvtUrl,
+          this.$route.params.slug,
+          this.$store.state.feature_type.feature_types
+        );
+        axios
+          .get(url)
+          .then((response) => {
+            let features = response.data.features;
+            self.arraysOffline.forEach(
+              (x) => (x.geojson.properties.color = "red")
+            );
+            features = response.data.features.concat(
+              self.arraysOffline.map((x) => x.geojson)
+            );
+            const featureGroup = mapUtil.addFeatures(
+              features,
+              {},
+              false,
+              this.$store.state.feature_type.feature_types
+            );
 
-              if (featureGroup && featureGroup.getLayers().length > 0) {
-                mapUtil
-                  .getMap()
-                  .fitBounds(featureGroup.getBounds(), { padding: [25, 25] });
-                this.$store.commit("map/SET_GEOJSON_FEATURES", features);
-              } else {
-                this.$store.commit("map/SET_GEOJSON_FEATURES", []);
-              }
-            })
-            .catch((error) => {
-              throw error;
-            });
-        
-        }
-      },
+            if (featureGroup && featureGroup.getLayers().length > 0) {
+              mapUtil
+                .getMap()
+                .fitBounds(featureGroup.getBounds(), { padding: [25, 25] });
+              this.$store.commit("map/SET_GEOJSON_FEATURES", features);
+            } else {
+              this.$store.commit("map/SET_GEOJSON_FEATURES", []);
+            }
+          })
+          .catch((error) => {
+            throw error;
+          });
+      }
+    },
   },
 
   created() {
-    
-  
     if (this.user) {
       projectAPI
         .getProjectSubscription({ projectSlug: this.$route.params.slug })
         .then((data) => (this.is_suscriber = data.is_suscriber));
     }
   },
-  
 
   mounted() {
-    let self=this;
-    this.$store.dispatch("GET_PROJECT_INFO", this.slug).then(setTimeout(self.initMap,1000));
+    let self = this;
+    this.$store
+      .dispatch("GET_PROJECT_INFO", this.slug)
+      .then(setTimeout(self.initMap, 1000));
     if (this.message) {
       this.tempMessage = this.message;
       document