Browse Source

SQL CaseWhen/Sum + ContactRequest API fix

Roman 3 years ago
parent
commit
896bbe76b4

+ 9 - 2
core/Api/ContactAPI.class.php

@@ -11,6 +11,8 @@ namespace Api {
     public function __construct(User $user, bool $externalCall, array $params) {
       parent::__construct($user, $externalCall, $params);
       $this->messageId = null;
+      $this->csrfTokenRequired = false;
+
     }
 
     protected function sendMail(string $name, ?string $fromEmail, string $subject, string $message, ?string $to = null): bool {
@@ -23,6 +25,7 @@ namespace Api {
         "to" => $to
       ));
 
+      $this->lastError = $request->getLastError();
       if ($this->success) {
         $this->messageId = $request->getResult()["messageId"];
       }
@@ -39,6 +42,9 @@ namespace Api\Contact {
   use Api\Parameter\StringType;
   use Api\VerifyCaptcha;
   use Driver\SQL\Condition\Compare;
+  use Driver\SQL\Condition\CondNot;
+  use Driver\SQL\Expression\CaseWhen;
+  use Driver\SQL\Expression\Sum;
   use Objects\User;
 
   class Request extends ContactAPI {
@@ -219,9 +225,10 @@ namespace Api\Contact {
       }
 
       $sql = $this->user->getSQL();
-      $res = $sql->select("ContactRequest.uid", "from_name", "from_email", "from_name", $sql->sum("read"))
+      $res = $sql->select("ContactRequest.uid", "from_name", "from_email", "from_name",
+          new Sum(new CaseWhen(new CondNot("ContactMessage.read"), 1, 0), "unread"))
         ->from("ContactRequest")
-        ->groupBy("uid")
+        ->groupBy("ContactRequest.uid")
         ->leftJoin("ContactMessage", "ContactRequest.uid", "ContactMessage.request_id")
         ->execute();
 

+ 17 - 12
core/Api/MailAPI.class.php

@@ -187,7 +187,7 @@ namespace Api\Mail {
       $sql = $this->user->getSQL();
 
       $query = $sql->insert("ContactMessage", ["request_id", "user_id", "message", "messageId", "created_at"])
-        ->onDuplicateKeyStrategy(new UpdateStrategy(["message_id"], ["message" => new Column("message")]));
+        ->onDuplicateKeyStrategy(new UpdateStrategy(["messageId"], ["message" => new Column("message")]));
 
       $entityIds = [];
       foreach ($messages as $message) {
@@ -279,6 +279,7 @@ namespace Api\Mail {
     }
 
     private function runSearch($mbox, string $searchCriteria, ?\DateTime $lastSyncDateTime, array $messageIds, array &$messages) {
+
       $result = @imap_search($mbox, $searchCriteria);
       if ($result === false) {
         $err = imap_last_error(); // might return false, if not messages were found, so we can just abort without throwing an error
@@ -287,7 +288,7 @@ namespace Api\Mail {
 
       foreach ($result as $msgNo) {
         $header = imap_headerinfo($mbox, $msgNo);
-        $date   = $this->parseDate($header->date);
+        $date = $this->parseDate($header->date);
         if ($date === false) {
           return false;
         }
@@ -309,7 +310,9 @@ namespace Api\Mail {
               foreach ($structure->parts as $part) {
                 $disposition = (property_exists($part, "disposition") ? $part->disposition : null);
                 if ($disposition === "attachment") {
-                  $fileName = array_filter($part->dparameters, function($param) { return $param->attribute === "filename"; });
+                  $fileName = array_filter($part->dparameters, function ($param) {
+                    return $param->attribute === "filename";
+                  });
                   if (count($fileName) > 0) {
                     $attachments[] = $fileName[0]->value;
                   }
@@ -320,14 +323,16 @@ namespace Api\Mail {
             $body = imap_fetchbody($mbox, $msgNo, "1");
             $body = $this->parseBody($body);
 
-            $messages[] = [
-              "messageId" => $messageId,
-              "requestId" => $requestId,
-              "timestamp" => $date->getTimestamp(),
-              "from" => $senderAddress,
-              "body" => $body,
-              "attachments" => $attachments
-            ];
+            if (!isset($messageId[$messageId])) {
+              $messages[$messageId] = [
+                "messageId" => $messageId,
+                "requestId" => $requestId,
+                "timestamp" => $date->getTimestamp(),
+                "from" => $senderAddress,
+                "body" => $body,
+                "attachments" => $attachments
+              ];
+            }
           }
         }
       }
@@ -381,7 +386,7 @@ namespace Api\Mail {
       $messages = [];
       foreach ($boxes as $box) {
         imap_reopen($mbox, $box);
-        if (!$this->runSearch($mbox, $searchCriteria, $lastSyncDateTime, $messageIds,$messages)) {
+        if (!$this->runSearch($mbox, $searchCriteria, $lastSyncDateTime, $messageIds, $messages)) {
           return false;
         }
       }

+ 23 - 0
core/Driver/SQL/Expression/CaseWhen.class.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace Driver\SQL\Expression;
+
+use Driver\SQL\Condition\Condition;
+
+class CaseWhen extends Expression {
+
+  private Condition $condition;
+  private $trueCase;
+  private $falseCase;
+
+  public function __construct(Condition $condition, $trueCase, $falseCase) {
+    $this->condition = $condition;
+    $this->trueCase = $trueCase;
+    $this->falseCase = $falseCase;
+  }
+
+  public function getCondition(): Condition { return $this->condition; }
+  public function getTrueCase() { return $this->trueCase; }
+  public function getFalseCase() { return $this->falseCase; }
+
+}

+ 20 - 0
core/Driver/SQL/Expression/Sum.class.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace Driver\SQL\Expression;
+
+use Driver\SQL\Condition\Condition;
+
+class Sum extends Expression {
+
+  private $value;
+  private string $alias;
+
+  public function __construct($value, string $alias) {
+    $this->value = $value;
+    $this->alias = $alias;
+  }
+
+  public function getValue() { return $this->value; }
+  public function getAlias(): string { return $this->alias; }
+
+}

+ 8 - 4
core/Driver/SQL/MySQL.class.php

@@ -290,7 +290,7 @@ class MySQL extends SQL {
     }
   }
 
-  public function addValue($val, &$params = NULL) {
+  public function addValue($val, &$params = NULL, bool $unsafe = false) {
     if ($val instanceof Keyword) {
       return $val->getValue();
     } else if ($val instanceof CurrentColumn) {
@@ -300,8 +300,12 @@ class MySQL extends SQL {
     } else if ($val instanceof Expression) {
       return $this->createExpression($val, $params);
     } else {
-      $params[] = $val;
-      return "?";
+      if ($unsafe) {
+        return $this->getUnsafeValue($val);
+      } else {
+        $params[] = $val;
+        return "?";
+      }
     }
   }
 
@@ -403,7 +407,7 @@ class MySQL extends SQL {
     return $query;
   }
 
-  protected function createExpression(Expression $exp, array &$params) {
+  protected function createExpression(Expression $exp, array &$params): ?string {
     if ($exp instanceof DateAdd || $exp instanceof DateSub) {
       $lhs = $this->addValue($exp->getLHS(), $params);
       $rhs = $this->addValue($exp->getRHS(), $params);

+ 8 - 4
core/Driver/SQL/PostgreSQL.class.php

@@ -276,7 +276,7 @@ class PostgreSQL extends SQL {
     }
   }
 
-  public function addValue($val, &$params = NULL) {
+  public function addValue($val, &$params = NULL, bool $unsafe = false) {
     if ($val instanceof Keyword) {
       return $val->getValue();
     } else if ($val instanceof CurrentTable) {
@@ -288,8 +288,12 @@ class PostgreSQL extends SQL {
     } else if ($val instanceof Expression) {
       return $this->createExpression($val, $params);
     } else {
-      $params[] = is_bool($val) ? ($val ? "TRUE" : "FALSE") : $val;
-      return '$' . count($params);
+      if ($unsafe) {
+        return $this->getUnsafeValue($val);
+      } else {
+        $params[] = is_bool($val) ? ($val ? "TRUE" : "FALSE") : $val;
+        return '$' . count($params);
+      }
     }
   }
 
@@ -419,7 +423,7 @@ class PostgreSQL extends SQL {
     return $query;
   }
 
-  protected function createExpression(Expression $exp, array &$params) {
+  protected function createExpression(Expression $exp, array &$params): ?string {
     if ($exp instanceof DateAdd || $exp instanceof DateSub) {
       $lhs = $this->addValue($exp->getLHS(), $params);
       $rhs = $this->addValue($exp->getRHS(), $params);

+ 17 - 7
core/Driver/SQL/Query/Select.class.php

@@ -8,7 +8,7 @@ use Driver\SQL\SQL;
 
 class Select extends Query {
 
-  private array $columns;
+  private array $selectValues;
   private array $tables;
   private array $conditions;
   private array $joins;
@@ -18,9 +18,9 @@ class Select extends Query {
   private int $limit;
   private int $offset;
 
-  public function __construct($sql, ...$columns) {
+  public function __construct($sql, ...$selectValues) {
     parent::__construct($sql);
-    $this->columns = (!empty($columns) && is_array($columns[0])) ? $columns[0] : $columns;
+    $this->selectValues = (!empty($selectValues) && is_array($selectValues[0])) ? $selectValues[0] : $selectValues;
     $this->tables = array();
     $this->conditions = array();
     $this->joins = array();
@@ -85,7 +85,7 @@ class Select extends Query {
     return $this->sql->executeQuery($this, true);
   }
 
-  public function getColumns(): array { return $this->columns; }
+  public function getSelectValues(): array { return $this->selectValues; }
   public function getTables(): array { return $this->tables; }
   public function getConditions(): array { return $this->conditions; }
   public function getJoins(): array { return $this->joins; }
@@ -96,11 +96,21 @@ class Select extends Query {
   public function getGroupBy(): array { return $this->groupColumns; }
 
   public function build(array &$params): ?string {
-    $columns = $this->sql->columnName($this->getColumns());
+
+    $selectValues = [];
+    foreach ($this->selectValues as $value) {
+      if (is_string($value)) {
+        $selectValues[] = $this->sql->columnName($value);
+      } else {
+        $selectValues[] = $this->sql->addValue($value, $params);
+      }
+    }
+
     $tables = $this->getTables();
+    $selectValues = implode(",", $selectValues);
 
     if (!$tables) {
-      return "SELECT $columns";
+      return "SELECT $selectValues";
     }
 
     $tables = $this->sql->tableName($tables);
@@ -135,6 +145,6 @@ class Select extends Query {
 
     $limit = ($this->getLimit() > 0 ? (" LIMIT " . $this->getLimit()) : "");
     $offset = ($this->getOffset() > 0 ? (" OFFSET " . $this->getOffset()) : "");
-    return "SELECT $columns FROM $tables$joinStr$where$groupBy$orderBy$limit$offset";
+    return "SELECT $selectValues FROM $tables$joinStr$where$groupBy$orderBy$limit$offset";
   }
 }

+ 19 - 9
core/Driver/SQL/SQL.class.php

@@ -15,8 +15,10 @@ use Driver\SQL\Constraint\Constraint;
 use \Driver\SQL\Constraint\Unique;
 use \Driver\SQL\Constraint\PrimaryKey;
 use \Driver\SQL\Constraint\ForeignKey;
+use Driver\SQL\Expression\CaseWhen;
 use Driver\SQL\Expression\CurrentTimeStamp;
 use Driver\SQL\Expression\Expression;
+use Driver\SQL\Expression\Sum;
 use Driver\SQL\Query\AlterTable;
 use Driver\SQL\Query\CreateProcedure;
 use Driver\SQL\Query\CreateTable;
@@ -187,8 +189,10 @@ abstract class SQL {
   }
 
   protected function getUnsafeValue($value): ?string {
-    if (is_string($value) || is_numeric($value) || is_bool($value)) {
+    if (is_string($value)) {
       return "'" . addslashes("$value") . "'"; // unsafe operation here...
+    } else if (is_numeric($value) || is_bool($value)) {
+      return $value;
     } else if ($value instanceof Column) {
       return $this->columnName($value);
     } else if ($value === null) {
@@ -200,7 +204,7 @@ abstract class SQL {
   }
 
   protected abstract function getValueDefinition($val);
-  public abstract function addValue($val, &$params = NULL);
+  public abstract function addValue($val, &$params = NULL, bool $unsafe = false);
   protected abstract function buildUnsafe(Query $statement): string;
 
   public abstract function tableName($table): string;
@@ -222,12 +226,6 @@ abstract class SQL {
     }
   }
 
-  public function sum($col): Keyword {
-    $sumCol = strtolower(str_replace(".","_", $col)) .  "_sum";
-    $col = $this->columnName($col);
-    return new Keyword("SUM($col) AS $sumCol");
-  }
-
   public function distinct($col): Keyword {
     $col = $this->columnName($col);
     return new Keyword("DISTINCT($col)");
@@ -312,11 +310,23 @@ abstract class SQL {
     }
   }
 
-  protected function createExpression(Expression $exp, array &$params) {
+  protected function createExpression(Expression $exp, array &$params): ?string {
     if ($exp instanceof Column) {
       return $this->columnName($exp);
     } else if ($exp instanceof Query) {
       return "(" . $exp->build($params) . ")";
+    } else if ($exp instanceof CaseWhen) {
+      $condition = $this->buildCondition($exp->getCondition(), $params);
+
+      // psql requires constant values here
+      $trueCase = $this->addValue($exp->getTrueCase(), $params, true);
+      $falseCase = $this->addValue($exp->getFalseCase(), $params, true);
+
+      return "CASE WHEN $condition THEN $trueCase ELSE $falseCase END";
+    } else if ($exp instanceof Sum) {
+      $value = $this->addValue($exp->getValue(), $params);
+      $alias = $exp->getAlias();
+      return "SUM($value) AS $alias";
     } else {
       $this->lastError = "Unsupported expression type: " . get_class($exp);
       return null;