CHAPTER 6

  "dependencies": {
    "bcrypt": "^5.0.1",
    "body-parser": "^1.19.0",
    "cookie-session": "^1.4.0",
    "dotenv": "^10.0.0",
    "express": "^4.17.1",
    "express-session": "^1.17.2",
    "express-validator": "^6.12.1",
    "helmet": "^4.6.0",
    "jsonwebtoken": "^8.5.1",
    "mongoose": "^6.0.2",
    "mongoose-sanitizer-plugin": "^1.1.0",
    "mongoose-type-email": "^1.1.2",
    "mongoose-unique-validator": "^2.0.3",
    "mongoose-validator": "^2.1.0",
    "multer": "^1.4.3",
    "password-validator": "^5.1.1"
  }
const http = require('http');

const server = http.createServer((req, res) => {
    res.end('Voilà la réponse du serveur !');
});

server.listen(process.env.PORT || 3000);
npm install --save express
const express = require('express');

const app = express();



module.exports = app;
const express = require('express');

const app = express();

app.use((req, res) => {
   res.json({ message: 'Votre requête a bien été reçue !' }); 
});

module.exports = app;
const express = require('express');

const app = express();

app.use((req, res, next) => {
  console.log('Requête reçue !');
  next();
});

app.use((req, res, next) => {
  res.status(201);
  next();
});

app.use((req, res, next) => {
  res.json({ message: 'Votre requête a bien été reçue !' });
  next();
});

app.use((req, res, next) => {
  console.log('Réponse envoyée avec succès !');
});

module.exports = app;
// [1] IMPORT SECT.
// 'http' node package
const http = require("http");
// 'express' App
const app = require("./app");
// dotEnv variables
const dotEnv = require("dotenv").config({ path: "./config/.env" });
const configMongodb = require("./config/mongodb");

// [2] SERVER SECT.
// returns a valid PORT as a nbr or a string
const normalizePort = (val) => {
  const port = parseInt(val, 10);

  if (isNaN(port)) {
    return val;
  }
  if (port >= 0) {
    return port;
  }
  return false;
};
// env variable | setting up default port
const port = normalizePort(process.env.PORT);
// tells express which port to set
app.set("port", port);

const errorHandler = (error) => {
  if (error.syscall !== "listen") {
    throw error;
  }
  const address = server.address();
  const bind =
    typeof address === "string" ? "pipe " + address : "port: " + port;
  switch (error.code) {
    case "EACCES":
      console.error(bind + " requires elevated privileges.");
      process.exit(1);
      break;
    case "EADDRINUSE":
      console.error(bind + " is already in use.");
      process.exit(1);
      break;
    default:
      throw error;
  }
};

// [3] CREATE Server
// app = function => receives req & res
const server = http.createServer(app);

server.on("error", errorHandler);
server.on("listening", () => {
  const address = server.address();
  const bind = typeof address === "string" ? "pipe " + address : "port " + port;
  console.log("Listening on " + bind);
});

// [=>] LISTEN Server on chosen port
server.listen(port);
/* 
PORT=3000
DB_USER_PASS=yungdavo:cluster0011
SECRET_TOKEN="[nX*Tyk=b:shN+vrG(FD5?r@5a'?8.5X.>KX!"U=kg8zTX9prw"
SECURE_SESS="jrhWVbxUr9$`:<_a3FETgm84Lb3QR`"
*/
app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content, Accept, Content-Type, Authorization');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
  next();
});
/* Installer les dependencies nécessaires */
npm install

/* Corriger les vulnérabilités */
npm audit fix

/* Si ce n'est encore le cas */
npm audit fix --force

/* Angular update */
ng update @angular/cli

/* Le programme demande de commit ou de stash */
git add -A
git commit -m "msg"
ng update @angular/cli

/* Core update */
ng update @angular/core

/* RXJS update */
ng update rxjs

/* Fix npm start bugs */
npm install --save-dev run-script-os
/* Installer les dependencies nécessaires */
npm install

/* Corriger les vulnérabilités */
npm audit fix

/* Si ce n'est encore le cas */
npm audit fix --force

/* Angular update */
ng update @angular/cli

/* Le programme demande de commit ou de stash */
git add -A
git commit -m "msg"
ng update @angular/cli

/* Core update */
ng update @angular/core

/* RXJS update */
ng update rxjs

/* Fix npm start bugs */
npm install --save-dev run-script-os
/* Initialiser le back-end */
// création d'un dossier "backend"
// => création d'un fichier .json
npm init 

/* Lancer le projet > front-end */
npm start

/* Lancer le projet > back-end */
// permet de relancer le server à chaque intéraction
nodemon server
// [3] Middlewares
// -
// CORS midWare general | default options // allows app to acess to API
app.use((req, res, next) => {
  res.setHeader("Access-Control-Allow-Origin", "*");
  res.setHeader(
    "Access-Control-Allow-Headers",
    "Origin, X-Requested-With, Content, Accept, Content-Type, Authorization"
  );
  res.setHeader(
    "Access-Control-Allow-Methods",
    "GET, POST, PUT, DELETE, PATCH, OPTIONS"
  );
  next();
});
// [1] IMPORT SECT.
// -
const express = require("express");
const saucesCtrl = require("../controllers/saucesCtrl");
const auth = require("../middleware/auth");
const multer = require("../middleware/multer-config");

// [2] CREATE ROUTER
// -
const router = express.Router();

// [3] Middlewares
// -
// POST request
router.post("/", auth, multer, saucesCtrl.createSauce);

// PUT request | Update / modify an existing sauce
router.put("/:id", auth, multer, saucesCtrl.updateSauce);

// DELETE request | Delete an existing sauce
router.delete("/:id", auth, saucesCtrl.deleteSauce);

// GET request for ONE specific sauce
router.get("/:id", auth, saucesCtrl.getOneSauce);

// GET request for ALL sauces
router.get("/", auth, saucesCtrl.getAllSauces);

// POST request for likes & dislikes
router.post("/:id/like", auth, saucesCtrl.manageLike);

// [=>] EXPORT Router
// -
module.exports = router;
// [1] IMPORT SECT.
// -
const express = require("express");
const userCtrl = require("../controllers/userCtrl");
const checkPassword = require("../middleware/checkPassword");

// [2] CREATE ROUTER
// -
const router = express.Router();

// [3] Middlewares
// -
// POST request => signUp
router.post("/signup", checkPassword, userCtrl.signup);

// POST request => logIn
router.post("/login", userCtrl.login);

// [=>] EXPORT Router
// -
module.exports = router;
// [1] IMPORT SECT.
// -
const mongoose = require("mongoose");
const dotEnv = require("dotenv").config({ path: "./config/.env" });

mongoose
  .connect(
    "mongodb+srv://" +
      process.env.DB_USER_PASS +
      "@cluster0.xsxcm.mongodb.net/test?retryWrites=true&w=majority",
    { useNewUrlParser: true, useUnifiedTopology: true }
  )
  .then(() => console.log("Connexion à MongoDB réussie !"))
  .catch((err) => console.log("Connexion à MongoDB échouée !", err));
npm install --save mongoose
const mongoose = require('mongoose');
// [1] IMPORT SECT.
// -
const mongoose = require("mongoose");
const sanitizerPlugin = require("mongoose-sanitizer-plugin");

const sauceSchema = mongoose.Schema({
  userId: { type: String, required: true },
  name: {
    type: String,
    required: true,
    minLength: [3, "Must be at least 3 characters, got {VALUE}"],
    maxLength: [30, "Must be less than 50 characters, got {VALUE}"],
  },
  manufacturer: { type: String, required: true, minLength: 3, maxLength: 30 },
  description: { type: String, required: true, minLength: 3, maxLength: 50 },
  mainPepper: { type: String, required: true, minLength: 3, maxLength: 20 },
  imageUrl: { type: String, required: true },
  heat: { type: Number, required: true },
  likes: { type: Number },
  dislikes: { type: Number },
  usersLiked: { type: [String] },
  usersDisliked: { type: [String] },
});

// [2] PLUGIN | Sanitizer
// -
// Sanitizer for Mongoose model, cleans model data before saving in MongoDB
// Uses HTML Sanitizer from Google Caja in order to purify
sauceSchema.plugin(sanitizerPlugin);

// [=>] MODULE EXPORT
// -
module.exports = mongoose.model("Sauce", sauceSchema);
// [1] IMPORT SECT.
// 'http' node package
const http = require("http");
// 'express' App
const app = require("./app");
// dotEnv variables
const dotEnv = require("dotenv").config({ path: "./config/.env" });
const configMongodb = require("./config/mongodb");
// [2] SERVER SECT.
// returns a valid PORT as a nbr or a string
const normalizePort = (val) => {
  const port = parseInt(val, 10);

  if (isNaN(port)) {
    return val;
  }
  if (port >= 0) {
    return port;
  }
  return false;
};
// env variable | setting up default port
const port = normalizePort(process.env.PORT);
// tells express which port to set
app.set("port", port);
// [3] ERROR SECT.
const errorHandler = (error) => {
  if (error.syscall !== "listen") {
    throw error;
  }
  const address = server.address();
  const bind =
    typeof address === "string" ? "pipe " + address : "port: " + port;
  switch (error.code) {
    case "EACCES":
      console.error(bind + " requires elevated privileges.");
      process.exit(1);
      break;
    case "EADDRINUSE":
      console.error(bind + " is already in use.");
      process.exit(1);
      break;
    default:
      throw error;
  }
};
// [4] CREATE Server
// app = function => receives req & res
const server = http.createServer(app);

server.on("error", errorHandler);
server.on("listening", () => {
  const address = server.address();
  const bind = typeof address === "string" ? "pipe " + address : "port " + port;
  console.log("Listening on " + bind);
});

// [=>] LISTEN Server on chosen port
server.listen(port);
// [1] IMPORT SECT.
// -
const express = require("express");
// mongoDB IMPORT !
require("./config/mongodb");
const saucesRoutes = require("./routes/saucesRoutes");
const userRoutes = require("./routes/userRoutes");
// allows to access the file system paths
const path = require("path");
// [2] Create & Execute express App
// -
const app = express();
// for parsing body application/json
app.use(express.json());

// for this route => use *dirname*Routes
app.use("/api/sauces", saucesRoutes);
app.use("/api/auth", userRoutes);

// [=>] EXPORT App
// -
module.exports = app;
// [1] IMPORT SECT.
// -
// Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment.
// Mongoose supports both promises and callbacks
const mongoose = require("mongoose");
const sanitizerPlugin = require("mongoose-sanitizer-plugin");
const uniqueValidator = require("mongoose-unique-validator");
const mongooseTypeEmail = require("mongoose-type-email");

// [2] User Schema
// -
const userSchema = mongoose.Schema({
  email: {
    type: String,
    unique: true,
    required: [true, "Veuillez entrer votre adresse email"],
    match: [
      /^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/,
      "Veuillez entrer une adresse email correcte",
    ],
  },
  password: {
    type: String,
    required: [true, "Veuillez entrer votre mot de passe"],
  },
});

// [2] PLUGIN | Validator
// -
userSchema.plugin(uniqueValidator);

// [3] PLUGIN | Sanitizer
// -
// Sanitizer for Mongoose model, cleans model data before saving in MongoDB
// Uses HTML Sanitizer from Google Caja in order to purify
userSchema.plugin(sanitizerPlugin);

// [=>] MODULE EXPORT
// -
module.exports = mongoose.model("User", userSchema);
// [1] IMPORT SECT.
// -
const passwordValidator = require("password-validator");

// [2] Create a more secure password schema
// -
const passwordSchema = new passwordValidator();

// [3] passwordSchema properties
passwordSchema
  .is()
  .min(8) // Minimum length 8
  .is()
  .max(30) // Maximum length 30
  .has()
  .uppercase() // Must have uppercase letters
  .has()
  .lowercase() // Must have lowercase letters
  .has()
  .digits(2) // Must have at least 2 digits
  .has()
  .not()
  .spaces() // Should not have spaces
  .is()
  .not()
  .oneOf(["Passw0rd", "Password123"]); // Blacklist these values

// [=>] MODULE EXPORT
// -
module.exports = passwordSchema;
// [1] IMPORT SECT.
// -
const passwordSchema = require("../models/passwordValidator");

// [2] Check if password is valid
// -
module.exports = (req, res, next) => {
  if (!passwordSchema.validate(req.body.password)) {
    res.writeHead(
      400,
      '{"message": "Mot de passe requis : 8 caractères minimum, au moins une majuscule, une minuscule, 2 digits, et sans espaces}',
      { "content-type": "application/json" }
    );
    res.end("Format de mot de passe incorrect");
  } else {
    next();
  }
};
// [1] IMPORT SECT.
// -
const bcrypt = require("bcrypt");
const User = require("../models/user");
const jwt = require("jsonwebtoken");
// dotEnv variables
const dotEnv = require("dotenv").config({ path: "./config/.env" });
// [2] middleWares Functions
// -
exports.signup = (req, res, next) => {
  bcrypt
    .hash(req.body.password, 10)
    .then((hash) => {
      const user = new User({
        email: req.body.email,
        password: hash,
      });
      user
        .save()
        .then(() => res.status(201).json({ message: "User créé !" }))
        .catch((error) => res.status(400).json({ error }));
    })
    .catch((error) => res.status(500).json({ error }));
};
exports.login = (req, res, next) => {
  User.findOne({ email: req.body.email })
    .then((user) => {
      if (!user) {
        return res.status(401).json({ error: "User non trouvé !" });
      }
      bcrypt
        .compare(req.body.password, user.password)
        .then((valid) => {
          if (!valid) {
            return res.status(401).json({ error: "Password incorrect !" });
          }
          res.status(200).json({
            userId: user._id,
            // jsonWebToken => function "sign", takes 3 args
            // token will hold encoded userId => create new signed objects and auth
            // if user[1] uploads a new sauce, it prevents user[2] to modify it
            token: jwt.sign(
              {
                // [1] arg = > userId matches
                userId: user._id,
              },
              // [2] arg => secret token
              process.env.SECRET_TOKEN,
              {
                // [3] arg => token duration
                expiresIn: "24h",
              }
            ),
          });
        })
        .catch((error) => res.status(500).json({ error }));
    })
    .catch((error) => res.status(500).json({ error }));
};
// [1] IMPORT SECT.
// -
// Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment.
// Mongoose supports both promises and callbacks
const mongoose = require("mongoose");
const sanitizerPlugin = require("mongoose-sanitizer-plugin");

const sauceSchema = mongoose.Schema({
  userId: { type: String, required: true },
  name: {
    type: String,
    required: true,
    minLength: [3, "Must be at least 3 characters"],
    maxLength: [30, "Must be less than 30 characters"],
  },
  manufacturer: {
    type: String,
    required: true,
    minLength: [3, "Must be at least 3 characters"],
    maxLength: [30, "Must be less than 30 characters"],
  },
  description: {
    type: String,
    required: true,
    minLength: [3, "Must be at least 3 characters"],
    maxLength: [50, "Must be less than 50 characters"],
  },
  mainPepper: {
    type: String,
    required: true,
    minLength: [3, "Must be at least 3 characters"],
    maxLength: [30, "Must be less than 30 characters"],
  },
  imageUrl: { type: String, required: true },
  heat: { type: Number, required: true },
  likes: { type: Number },
  dislikes: { type: Number },
  usersLiked: { type: [String] },
  usersDisliked: { type: [String] },
});

// [2] PLUGIN | Sanitizer
// -
// Sanitizer for Mongoose model, cleans model data before saving in MongoDB
// Uses HTML Sanitizer from Google Caja in order to purify
sauceSchema.plugin(sanitizerPlugin);

// [=>] MODULE EXPORT
// -
module.exports = mongoose.model("Sauce", sauceSchema);
// [1] IMPORT SECT.
// -
const jwt = require("jsonwebtoken");

// [=>] EXPORT middleware
module.exports = (req, res, next) => {
  try {
    // targets headers => authorization
    // split and select [1]
    // => [0] = [bearer] & [1] = [token]
    const token = req.headers.authorization.split(" ")[1];
    // verify token
    const decodedToken = jwt.verify(token, process.env.SECRET_TOKEN);

    // targets userId in decodedToken
    const userId = decodedToken.userId;
    if (req.body.userId && req.body.userId !== userId) {
      throw "User ID non valable !";
    } else {
      // if all good => to next middleware !
      next();
    }
  } catch (error) {
    res.status(401).json({ error: error | "Requête non authentifiée !" });
  }
};
// [1] IMPORT SECT.
// -
const multer = require("multer");

// [2] create a dictionary for the img folder
// -
const MIME_TYPES = {
  "image/jpg": "jpg",
  "image/jpeg": "jpg",
  "image/png": "png",
};

// [3] create a config object for multer
// -
const storage = multer.diskStorage({
  // [1] sets destination folder
  destination: (req, file, callback) => {
    callback(null, "images");
  },
  // [2] sets new file name
  filename: (req, file, callback) => {
    // [a] remove space / replace by '_'
    const name = file.originalname.split(" ").join("_");
    // [b] use mime_types to generate file extension
    // element from dictionnary = matches mime type sent from front-end
    const extension = MIME_TYPES[file.mimetype];
    callback(null, name + Date.now() + "." + extension);
  },
});

// [=>] EXPORT multer
// -
// multer method => store single image file
module.exports = multer({ storage: storage }).single("image");
// [1] IMPORT SECT.
// -
// mongoose modelSchema
const Sauce = require("../models/sauce");
// node package 'file system'
// https://practicalprogramming.fr/how-to-use-node-fs
const fs = require("fs");
exports.createSauce = (req, res, next) => {
  // stocking data sent from front-end, as form-data, in a variable, parsed as a JS object
  // req.body = string 'sauce' that must be parsed
  const sauceObject = JSON.parse(req.body.sauce);

  // remove Id sent from front-end.
  // the sauce's Id is created by MongoDB
  delete sauceObject._id;

  // [1] 'new' => create new instance of Sauce model
  // [2] the Spread operator '...' => creates a new copy of all elements of req.body
  // https://geeklecode.com/loperateur-spread-en-javascript-va-vous-simplifier-la-vie/
  const sauce = new Sauce({
    ...sauceObject,
    // (a) ${req.protocol} = http or https
    // (b) ${req.get("host")} = target host server
    // (c) ${req.file.filename} = filename
    imageUrl: `${req.protocol}://${req.get("host")}/images/${
      req.file.filename
    }`,
    likes: 0,
    dislikes: 0,
    usersLiked: [],
    usersDisliked: [],
  });
exports.updateSauce = (req, res, next) => {

  // if update has to process a new image
  // Ternary Operator | if req.file exists ?
  // => like if() {} else {} => condition checks ? if TRUE : if FALSE
  const sauceObject = req.file
    ? {
        // if exists =>
        // (a) targets all infos from req object
        ...JSON.parse(req.body.sauce),
        // (b) generates new img
        imageUrl: `${req.protocol}://${req.get("host")}/images/${
          req.file.filename
        }`,
      }
    : // if doesn't exist => simply copy req.body
      { ...req.body };

  // [=>] UPDATE / save object
  // [1] _id = param Req id
  // [2] new object version = targets param Req sauce / _id = param Req id
  Sauce.updateOne(
    { _id: req.params.id },
    { ...sauceObject, _id: req.params.id }
  )
    .then(() => res.status(200).json({ message: "Objet modifié !" }))
    .catch((error) => res.status(400).json({ error }));
};
exports.deleteSauce = (req, res, next) => {
  // finds object in database
  Sauce.findOne({ _id: req.params.id })
    .then((sauce) => {
      // extract filename
      const filename = sauce.imageUrl.split("/images/")[1];
      // delete file thanks to unlink
      fs.unlink(`images/${filename}`, () => {
        // in callback: once file is deleted => deletes object from database
        Sauce.deleteOne({ _id: req.params.id })
          .then(() => res.status(200).json({ message: "Objet supprimé !" }))
          .catch((error) => res.status(400).json({ error }));
      });
    })
    .catch((error) => res.status(500).json({ error }));
};
exports.getOneSauce = (req, res, next) => {
  // mongoose model
  // targets _id = must be the same as the req param
  Sauce.findOne({ _id: req.params.id })
    .then((sauce) => res.status(200).json(sauce))
    .catch((error) => res.status(404).json({ error }));
};
exports.getAllSauces = (req, res, next) => {
  Sauce.find()
    .then((sauces) => res.status(200).json(sauces))
    .catch((error) => res.status(400).json({ error }));
};
exports.manageLike = (req, res, next) => {
  // grabs user id
  let userId = req.body.userId;
  // grabs sauce id
  let sauceId = req.params.id;
  // grabs 'like' in request body
  let like = req.body.like;

  if (like === 1) {
    // if user smashes the like button
    // => updates the sauce given its id
    Sauce.updateOne(
      { _id: sauceId },
      {
        // [ mongoDB push operator ]
        // pushes userId to usersLiked: [array]
        $push: { usersLiked: userId },
        // [ mongoDB increment operator ]
        // increments likes [array]
        $inc: { likes: +1 },
      }
    )
      .then(() =>
        res.status(200).json({ message: "Like ajouté par l'utilisateur !" })
      )
      .catch((error) => res.status(400).json({ error }));
  }

  if (like === -1) {
    // if user smashes the dislike button
    // => updates the sauce given its id
    Sauce.updateOne(
      { _id: sauceId },
      {
        // [ mongoDB push operator ]
        // pushes userId to usersDisliked: [array]
        $push: { usersDisliked: userId },
        // [ mongoDB increment operator ]
        // increments dislikes [array]
        $inc: { dislikes: +1 },
      }
    )
      .then(() =>
        res.status(200).json({ message: "Dislike ajouté par l'utilisateur !" })
      )
      .catch((error) => res.status(400).json({ error }));
  }

  // Remove like / dislike
  if (like === 0) {
    Sauce.findOne({
      _id: sauceId,
    })
      .then((sauce) => {
        // remove like
        // if user has already liked
        if (sauce.usersLiked.includes(userId)) {
          Sauce.updateOne(
            { _id: sauceId },
            { $pull: { usersLiked: userId }, $inc: { likes: -1 } }
          )
            .then(() =>
              res
                .status(200)
                .json({ message: "Like retiré par l'utilisateur !" })
            )
            .catch((error) => res.status(400).json({ error }));
        }
        // remove dislike
        // if user has already disliked
        if (sauce.usersDisliked.includes(userId)) {
          Sauce.updateOne(
            { _id: sauceId },
            { $pull: { usersDisliked: userId }, $inc: { dislikes: -1 } }
          )
            .then(() =>
              res
                .status(200)
                .json({ message: "Dislike retiré par l'utilisateur !" })
            )
            .catch((error) => res.status(400).json({ error }));
        }
      })
      .catch((error) => res.status(400).json({ error }));
  }
};

Similiar Collections

Python strftime reference pandas.Period.strftime python - Formatting Quarter time in pandas columns - Stack Overflow python - Pandas: Change day - Stack Overflow python - Check if multiple columns exist in a df - Stack Overflow Pandas DataFrame apply() - sending arguments examples python - How to filter a dataframe of dates by a particular month/day? - Stack Overflow python - replace a value in the entire pandas data frame - Stack Overflow python - Replacing blank values (white space) with NaN in pandas - Stack Overflow python - get list from pandas dataframe column - Stack Overflow python - How to drop rows of Pandas DataFrame whose value in a certain column is NaN - Stack Overflow python - How to drop rows of Pandas DataFrame whose value in a certain column is NaN - Stack Overflow python - How to lowercase a pandas dataframe string column if it has missing values? - Stack Overflow How to Convert Integers to Strings in Pandas DataFrame - Data to Fish How to Convert Integers to Strings in Pandas DataFrame - Data to Fish create a dictionary of two pandas Dataframe columns? - Stack Overflow python - ValueError: No axis named node2 for object type <class 'pandas.core.frame.DataFrame'> - Stack Overflow Python Pandas iterate over rows and access column names - Stack Overflow python - Creating dataframe from a dictionary where entries have different lengths - Stack Overflow python - Deleting DataFrame row in Pandas based on column value - Stack Overflow python - How to check if a column exists in Pandas - Stack Overflow python - Import pandas dataframe column as string not int - Stack Overflow python - What is the most efficient way to create a dictionary of two pandas Dataframe columns? - Stack Overflow Python Loop through Excel sheets, place into one df - Stack Overflow python - How do I get the row count of a Pandas DataFrame? - Stack Overflow python - How to save a new sheet in an existing excel file, using Pandas? - Stack Overflow Python Loop through Excel sheets, place into one df - Stack Overflow How do I select a subset of a DataFrame? — pandas 1.2.4 documentation python - Delete column from pandas DataFrame - Stack Overflow python - Convert list of dictionaries to a pandas DataFrame - Stack Overflow How to Add or Insert Row to Pandas DataFrame? - Python Examples python - Check if a value exists in pandas dataframe index - Stack Overflow python - Set value for particular cell in pandas DataFrame using index - Stack Overflow python - Pandas Dataframe How to cut off float decimal points without rounding? - Stack Overflow python - Pandas: Change day - Stack Overflow python - Clean way to convert quarterly periods to datetime in pandas - Stack Overflow Pandas - Number of Months Between Two Dates - Stack Overflow python - MonthEnd object result in <11 * MonthEnds> instead of number - Stack Overflow python - Extracting the first day of month of a datetime type column in pandas - Stack Overflow
MySQL MULTIPLES INNER JOIN How to Use EXISTS, UNIQUE, DISTINCT, and OVERLAPS in SQL Statements - dummies postgresql - SQL OVERLAPS PostgreSQL Joins: Inner, Outer, Left, Right, Natural with Examples PostgreSQL Joins: A Visual Explanation of PostgreSQL Joins PL/pgSQL Variables ( Format Dates ) The Ultimate Guide to PostgreSQL Date By Examples Data Type Formatting Functions PostgreSQL - How to calculate difference between two timestamps? | TablePlus Date/Time Functions and Operators PostgreSQL - DATEDIFF - Datetime Difference in Seconds, Days, Months, Weeks etc - SQLines CASE Statements in PostgreSQL - DataCamp SQL Optimizations in PostgreSQL: IN vs EXISTS vs ANY/ALL vs JOIN PostgreSQL DESCRIBE TABLE Quick and best way to Compare Two Tables in SQL - DWgeek.com sql - Best way to select random rows PostgreSQL - Stack Overflow PostgreSQL: Documentation: 13: 70.1. Row Estimation Examples Faster PostgreSQL Counting How to Add a Default Value to a Column in PostgreSQL - PopSQL How to Add a Default Value to a Column in PostgreSQL - PopSQL SQL Subquery - Dofactory SQL IN - SQL NOT IN - JournalDev DROP FUNCTION (Transact-SQL) - SQL Server | Microsoft Docs SQL : Multiple Row and Column Subqueries - w3resource PostgreSQL: Documentation: 9.5: CREATE FUNCTION PostgreSQL CREATE FUNCTION By Practical Examples datetime - PHP Sort a multidimensional array by element containing date - Stack Overflow database - Oracle order NULL LAST by default - Stack Overflow PostgreSQL: Documentation: 9.5: Modifying Tables PostgreSQL: Documentation: 14: SELECT
PostgreSQL POSITION() function PostgresQL ANY / SOME Operator ( IN vs ANY ) PostgreSQL Substring - Extracting a substring from a String How to add an auto-incrementing primary key to an existing table, in PostgreSQL PostgreSQL STRING_TO_ARRAY()function mysql FIND_IN_SET equivalent to postgresql PL/pgSQL Variables ( Format Dates ) The Ultimate Guide to PostgreSQL Date By Examples Data Type Formatting Functions PostgreSQL - How to calculate difference between two timestamps? | TablePlus Date/Time Functions and Operators PostgreSQL - DATEDIFF - Datetime Difference in Seconds, Days, Months, Weeks etc - SQLines CASE Statements in PostgreSQL - DataCamp SQL Optimizations in PostgreSQL: IN vs EXISTS vs ANY/ALL vs JOIN PL/pgSQL Variables PostgreSQL: Documentation: 11: CREATE PROCEDURE Reading a Postgres EXPLAIN ANALYZE Query Plan Faster PostgreSQL Counting sql - Fast way to discover the row count of a table in PostgreSQL - Stack Overflow PostgreSQL: Documentation: 9.1: tablefunc PostgreSQL DESCRIBE TABLE Quick and best way to Compare Two Tables in SQL - DWgeek.com sql - Best way to select random rows PostgreSQL - Stack Overflow How to Add a Default Value to a Column in PostgreSQL - PopSQL How to Add a Default Value to a Column in PostgreSQL - PopSQL PL/pgSQL IF Statement PostgreSQL: Documentation: 9.1: Declarations SQL Subquery - Dofactory SQL IN - SQL NOT IN - JournalDev PostgreSQL - IF Statement - GeeksforGeeks How to work with control structures in PostgreSQL stored procedures: Using IF, CASE, and LOOP statements | EDB PL/pgSQL IF Statement How to combine multiple selects in one query - Databases - ( loop reference ) DROP FUNCTION (Transact-SQL) - SQL Server | Microsoft Docs SQL : Multiple Row and Column Subqueries - w3resource PostgreSQL: Documentation: 9.5: CREATE FUNCTION PostgreSQL CREATE FUNCTION By Practical Examples datetime - PHP Sort a multidimensional array by element containing date - Stack Overflow database - Oracle order NULL LAST by default - Stack Overflow PostgreSQL: Documentation: 9.5: Modifying Tables PostgreSQL: Documentation: 14: SELECT
כמה עוד נשאר למשלוח חינם גם לעגלה ולצקאאוט הוספת צ'קבוקס לאישור דיוור בצ'קאאוט הסתרת אפשרויות משלוח אחרות כאשר משלוח חינם זמין דילוג על מילוי כתובת במקרה שנבחרה אפשרות איסוף עצמי הוספת צ'קבוקס לאישור דיוור בצ'קאאוט שינוי האפשרויות בתפריט ה-סידור לפי בווקומרס שינוי הטקסט "אזל מהמלאי" הערה אישית לסוף עמוד העגלה הגבלת רכישה לכל המוצרים למקסימום 1 מכל מוצר קבלת שם המוצר לפי ה-ID בעזרת שורטקוד הוספת כפתור וואטסאפ לקנייה בלופ ארכיון מוצרים הפיכה של מיקוד בצ'קאאוט ללא חובה מעבר ישיר לצ'קאאוט בלחיתה על הוספה לסל (דילוג עגלה) התראה לקבלת משלוח חינם בדף עגלת הקניות גרסה 1 התראה לקבלת משלוח חינם בדף עגלת הקניות גרסה 2 קביעה של מחיר הזמנה מינימלי (מוצג בעגלה ובצ'קאאוט) העברת קוד הקופון ל-ORDER REVIEW העברת קוד הקופון ל-ORDER REVIEW Kadence WooCommerce Email Designer קביעת פונט אסיסנט לכל המייל בתוסף מוצרים שאזלו מהמלאי - יופיעו מסומנים באתר, אבל בתחתית הארכיון הוספת כפתור "קנה עכשיו" למוצרים הסתרת אפשרויות משלוח אחרות כאשר משלוח חינם זמין שיטה 2 שינוי סימן מטבע ש"ח ל-ILS להפוך סטטוס הזמנה מ"השהייה" ל"הושלם" באופן אוטומטי תצוגת הנחה באחוזים שינוי טקסט "בחר אפשרויות" במוצרים עם וריאציות חיפוש מוצר לפי מק"ט שינוי תמונת מוצר לפי וריאציה אחרי בחירה של וריאציה אחת במקרה של וריאציות מרובות הנחה קבועה לפי תפקיד בתעריף קבוע הנחה קבועה לפי תפקיד באחוזים הסרה של שדות משלוח לקבצים וירטואליים הסתרת טאבים מעמוד מוצר הצגת תגית "אזל מהמלאי" בלופ המוצרים להפוך שדות ל-לא חובה בצ'קאאוט שינוי טקסט "אזל מהמלאי" לוריאציות שינוי צבע ההודעות המובנות של ווקומרס הצגת ה-ID של קטגוריות המוצרים בעמוד הקטגוריות אזל מהמלאי- שינוי ההודעה, תגית בלופ, הודעה בדף המוצר והוספת אזל מהמלאי על וריאציה הוספת שדה מחיר ספק לדף העריכה שינוי טקסט אזל מהמלאי תמונות מוצר במאונך לצד תמונת המוצר הראשית באלמנטור הוספת כפתור קנה עכשיו לעמוד המוצר בקניה הזו חסכת XX ש''ח לאפשר למנהל חנות לנקות קאש ברוקט לאפשר רק מוצר אחד בעגלת קניות
הודעת שגיאה מותאמת אישית בטפסים להפוך כל סקשן/עמודה לקליקבילית (לחיצה) - שיטה 1 להפוך כל סקשן/עמודה לקליקבילית (לחיצה) - שיטה 2 שינוי הגבלת הזיכרון בשרת הוספת לינק להורדת מסמך מהאתר במייל הנשלח ללקוח להפוך כל סקשן/עמודה לקליקבילית (לחיצה) - שיטה 3 יצירת כפתור שיתוף למובייל פתיחת דף תודה בטאב חדש בזמן שליחת טופס אלמנטור - טופס בודד בדף פתיחת דף תודה בטאב חדש בזמן שליחת טופס אלמנטור - טפסים מרובים בדף ביי ביי לאריק ג'ונס (חסימת ספאם בטפסים) זיהוי אלו אלמנטים גורמים לגלילה אופקית לייבלים מרחפים בטפסי אלמנטור יצירת אנימציה של "חדשות רצות" בג'ט (marquee) שינוי פונט באופן דינאמי בג'ט פונקציה ששולפת שדות מטא מתוך JET ומאפשרת לשים הכל בתוך שדה SELECT בטופס אלמנטור הוספת קו בין רכיבי התפריט בדסקטופ ולדציה למספרי טלפון בטפסי אלמנטור חיבור שני שדות בטופס לשדה אחד שאיבת נתון מתוך כתובת ה-URL לתוך שדה בטופס וקידוד לעברית מדיה קוורי למובייל לייבלים מרחפים בטפסי אלמנטור תמונות מוצר במאונך לצד תמונת המוצר הראשית באלמנטור הצגת תאריך עברי פורמט תאריך מותאם אישית