view client/src/components/usermanagement/Userdetail.vue @ 2624:9dbaf69c7a66

Improve geoserver config to better calculate bounding boxes * Disable the use of estimated extents for the postgis storage configuration for geoserver, which is set via the gemma middleware. This way we are able to get better bounding boxes for many layers where the postgis function `ST_EstimatedExtent()` would be far off.
author Bernhard Reiter <bernhard@intevation.de>
date Wed, 13 Mar 2019 16:18:39 +0100
parents f185503ef35a
children 13cd92b99d22
line wrap: on
line source

<template>
  <div class="userdetails shadow-xs fadeIn animated card">
    <UIBoxHeader
      icon="user"
      :title="this.cardHeader"
      :closeCallback="closeDetailview"
    />
    <div class="card-body">
      <form @submit.prevent="save" class="ml-3">
        <div class="formfields">
          <div v-if="currentUser.isNew" class="form-group row">
            <label for="user"> <translate>Username</translate> </label>
            <input
              type="user"
              :placeholder="userNamePlaceholder"
              class="form-control form-control-sm"
              id="user"
              aria-describedby="userHelp"
              v-model="currentUser.user"
            />
            <div v-show="errors.user" class="text-danger">
              <small>
                <font-awesome-icon
                  icon="exclamation-triangle"
                ></font-awesome-icon>
                {{ errors.user }}
              </small>
            </div>
          </div>
          <div class="form-group row">
            <label for="country"> <translate>Country</translate> </label>
            <select
              class="form-control form-control-sm"
              v-on:change="validateCountry"
              v-model="currentUser.country"
            >
              <option disabled value>
                <translate>Please select one</translate>
              </option>
              <option
                v-for="country in countries"
                v-bind:value="country"
                v-bind:key="country"
                >{{ country }}</option
              >
            </select>
            <div v-show="errors.country" class="text-danger">
              <small>
                <font-awesome-icon
                  icon="exclamation-triangle"
                ></font-awesome-icon>
                {{ errors.country }}
              </small>
            </div>
          </div>
          <div class="form-group row">
            <label for="email"> <translate>Email address</translate> </label>
            <input
              type="email"
              v-on:change="validateEmailaddress"
              class="form-control form-control-sm"
              id="email"
              aria-describedby="emailHelp"
              v-model="currentUser.email"
            />
            <div v-show="errors.email" class="text-danger">
              <small>
                <font-awesome-icon
                  icon="exclamation-triangle"
                ></font-awesome-icon>
                {{ errors.email }}
              </small>
            </div>
          </div>
          <div class="form-group row">
            <label for="role"> <translate>Role</translate> </label>
            <select
              class="form-control form-control-sm"
              v-on:change="validateRole"
              v-model="currentUser.role"
            >
              <option disabled value>
                <translate>Please select one</translate>
              </option>
              <option value="sys_admin">
                <translate>Sysadmin</translate>
              </option>
              <option value="waterway_admin">
                <translate>Waterway Admin</translate>
              </option>
              <option value="waterway_user">
                <translate>Waterway User</translate>
              </option>
            </select>
            <div v-show="errors.role" class="text-danger">
              <small>
                <font-awesome-icon
                  icon="exclamation-triangle"
                ></font-awesome-icon>
                {{ errors.role }}
              </small>
            </div>
          </div>
          <div class="form-group row">
            <PasswordField
              @fieldchange="passwordChanged"
              :placeholder="passwordPlaceholder"
              :label="passwordLabel"
              :passworderrors="errors.password"
            ></PasswordField>
          </div>
          <div class="form-group row">
            <PasswordField
              @fieldchange="passwordReChanged"
              :placeholder="passwordRePlaceholder"
              :label="passwordReLabel"
              :passworderrors="errors.passwordre"
            ></PasswordField>
          </div>
        </div>
        <div>
          <button
            type="submit"
            :disabled="submitted"
            class="shadow-sm btn btn-info submit-button"
          >
            <translate>Submit</translate>
          </button>
        </div>
      </form>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.submit-button {
  position: absolute;
  right: $offset;
  bottom: $offset;
}
.mailbutton {
  width: 12vw;
  position: absolute;
  left: $large-offset;
  bottom: 0;
}

.formfields {
  width: 60%;
}

.userdetails {
  height: 600px;
  margin-right: $offset;
}

form {
  font-size: $smaller;
}
</style>

<script>
/* This is Free Software under GNU Affero General Public License v >= 3.0
 * without warranty, see README.md and license for details.
 *
 * SPDX-License-Identifier: AGPL-3.0-or-later
 * License-Filename: LICENSES/AGPL-3.0.txt
 *
 * Copyright (C) 2018 by via donau
 *   – Österreichische Wasserstraßen-Gesellschaft mbH
 * Software engineering by Intevation GmbH
 *
 * Author(s):
 * Thomas Junk <thomas.junk@intevation.de>
 */
import { displayError } from "@/lib/errors.js";
import { mapState } from "vuex";

const emptyErrormessages = () => {
  return {
    email: "",
    country: "",
    role: "",
    password: "",
    passwordre: ""
  };
};

const isEmailValid = email => {
  /**
   *
   * For convenience purposes the same regex used as in the go code
   * cf. types.go
   *
   */
  // eslint-disable-next-line
  return /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/.test(
    email
  );
};

const violatedPasswordRules = password => {
  return (
    // rules according to issue 70
    password.length < 7 ||
    /\W/.test(password) == false ||
    /\d/.test(password) == false
  );
};

export default {
  name: "userdetail",
  components: {
    PasswordField: () => import("./Passwordfield")
  },
  data() {
    return {
      passwordLabel: this.$gettext("Password"),
      passwordReLabel: this.$gettext("Repeat Password"),
      passwordPlaceholder: this.$gettext("password"),
      passwordRePlaceholder: this.$gettext("password again"),
      password: "",
      passwordre: "",
      currentUser: {},
      path: null,
      submitted: false,
      errors: {
        email: "",
        country: "",
        role: "",
        password: "",
        passwordre: ""
      }
    };
  },
  mounted() {
    this.currentUser = { ...this.user };
    this.path = this.user.name;
  },
  watch: {
    user() {
      this.currentUser = { ...this.user };
      this.path = this.user.name;
      this.clearPassword();
      this.clearErrors();
    }
  },
  computed: {
    cardHeader() {
      if (this.currentUser.isNew) return this.$gettext("Add User");
      return this.currentUser.user;
    },
    userNamePlaceholder() {
      if (this.currentUser.isNew) return this.$gettext("Username");
      return "";
    },
    ...mapState("application", ["countries"]),
    user() {
      return this.$store.getters["usermanagement/currentUser"];
    },
    isFormValid() {
      return (
        isEmailValid(this.currentUser.email) &&
        this.currentUser.country &&
        this.password === this.passwordre &&
        (this.password === "" || !violatedPasswordRules(this.password))
      );
    }
  },
  methods: {
    passwordChanged(value) {
      this.password = value;
      this.validatePassword();
    },
    passwordReChanged(value) {
      this.passwordre = value;
      this.validatePassword();
    },
    clearErrors() {
      this.errors = emptyErrormessages();
    },
    clearPassword() {
      this.password = "";
      this.passwordre = "";
    },
    closeDetailview() {
      this.$store.commit("usermanagement/clearCurrentUser");
      this.$store.commit("usermanagement/setUserDetailsInvisible");
    },
    validateCountry() {
      this.errors.country = this.currentUser.country
        ? ""
        : this.$gettext("Please choose a country");
    },
    validateRole() {
      this.errors.role = this.currentUser.role
        ? ""
        : this.$gettext("Please choose a role");
    },
    validatePassword() {
      this.errors.passwordre =
        this.password === this.passwordre
          ? ""
          : this.$gettext("Passwords do not match!");
      this.errors.password =
        this.password === "" || !violatedPasswordRules(this.password)
          ? ""
          : this.$gettext(
              "Password should at least be 8 char long including 1 digit and 1 special char like $"
            );
    },
    validateEmailaddress() {
      this.errors.email = isEmailValid(this.currentUser.email)
        ? ""
        : this.$gettext("invalid email");
    },
    validate() {
      this.validateCountry();
      this.validateRole();
      this.validatePassword();
      this.validateEmailaddress();
    },
    save() {
      this.validate();
      if (!this.isFormValid) return;
      if (this.password) this.currentUser.password = this.password;
      this.submitted = true;
      this.$store
        .dispatch("usermanagement/saveCurrentUser", {
          path: this.user.user,
          user: this.currentUser
        })
        .then(() => {
          this.submitted = false;
          this.$store.dispatch("usermanagement/loadUsers").catch(error => {
            const { status, data } = error.response;
            displayError({
              title: this.$gettext("Backend Error"),
              message: `${status}: ${data.message || data}`
            });
          });
        })
        .catch(error => {
          this.submitted = false;
          const { status, data } = error.response;
          displayError({
            title: this.$gettext("Error while saving user"),
            message: `${status}: ${data.message || data}`
          });
        });
    }
  }
};
</script>