diff options
| -rw-r--r-- | app/class/agent.class.php | 15 | ||||
| -rw-r--r-- | app/class/issue.class.php | 185 | ||||
| -rw-r--r-- | srvs/mysql.sql | 25 | 
3 files changed, 198 insertions, 27 deletions
| diff --git a/app/class/agent.class.php b/app/class/agent.class.php index c8e6436..4c75f0b 100644 --- a/app/class/agent.class.php +++ b/app/class/agent.class.php @@ -75,6 +75,21 @@ abstract class agent extends obj      }      /* +     * Check whether this agent is assigned to the given issue +     */ +    public function isAssignedTo(issue $issue) : bool +    { +        foreach ($issue->getAssignees() as $assign) +        { +            if ($assign->assignee->guid == $this->guid +                && $assign->dismissed == "") +                return true; +        } + +        return false; +    } + +    /*       * Send an email message to this agent using stored configuration       * parameters.  If config is not established, delivery is not       * attempted.  Return status. diff --git a/app/class/issue.class.php b/app/class/issue.class.php index 1c77894..47adfa1 100644 --- a/app/class/issue.class.php +++ b/app/class/issue.class.php @@ -18,8 +18,8 @@ require_once "class/user.class.php";  require_once "class/mesg.class.php";  /* - * This class models Scrott issues.  Issues represent units of work, can - * be assigned to users, and advance through a pipeline. + * This class models Scrott issues.  Issues represent units of work, track + * messages, can be assigned to users, and advance through a pipeline.   */  class issue extends obj  { @@ -31,9 +31,9 @@ class issue extends obj          $this->fields['issues'] = array(              "guid",              "numb", -            "assignee", -            "seen", -            "description", +            "mesg", +            "closer", +            "closed",              "due",              "tags",          ); @@ -43,10 +43,11 @@ class issue extends obj      }      /* -     * Initialize a new issue object with the given name, parent, and -     * owner. +     * Initialize a new issue object with the given message, parent, +     * and owner.  The issue's name is taken from the message's name. +     * The given message object is updated to parent the new issue.       */ -    public static function initNew(string $name, user $owner, stage $parent) : issue +    public static function initNew(mesg $mesg, user $owner, stage $parent) : issue      {          $pad = $parent->getParent();          $numb = $pad->issueNumb++; @@ -55,32 +56,158 @@ class issue extends obj          $issue = new issue();          $issue->setOwner($owner);          $issue->setParent($parent); -        $issue->name = $name; +        $issue->name = $mesg->name;          $issue->objtype = "issue";          $issue->numb = $numb; +        $issue->mesg = $mesg->guid;          $issue->saveObj(); + +        $mesg->setParent($issue); +          return $issue;      }      /* -     * Get the assignee for this issue +     * Get all assignees.  Assignees are returned as an array of +     * stdClass instances.  Objects contain data from the 'assignees' +     * database table with no defined operations.  Pointers to users +     * will be followed and an instanciated user object will be +     * in their place.  Limit of zero returns all assignees.       */ -    public function getAssignee() : ?user +    public function getAssignees(int $limit = 0) : array      { -        if (!isset($this->assignee) || $this->assignee == "") -            return NULL; +        $assign = array(); +        $query = "SELECT * FROM assignees WHERE guid = '" . +            database::esc($this->guid) . "'"; + +        if ($limit != 0) +            $query .= " LIMIT " . database::esc($limit); + +        $res = database::query($query); + +        foreach ($res as $a) +        { +            $obj = new stdClass(); + +            foreach ($a as $k => $v) +                $obj->{$k} = $v; + +            $obj->assignee = new user($obj->assignee); +            $obj->assigner = new user($obj->assigner); + +            if (isset($obj->dismisser) && $obj->dismisser != "") +                $obj->dismisser = new user($obj->dismisser); + +            $assign[] = $obj; +        } + +        return $assign; +    } + +    /* +     * Add an assignee to this issue.  Returns false if user is +     * already assigned, or if another error occurs; true +     * otherwise. +     */ +    public function addAssignee(user $assignee, user $assigner) : bool +    { +        if ($assignee->isAssignedTo($this) || !isset($assignee->guid) +            || !isset($assigner->guid)) +            return false; + +        $query = "INSERT INTO assignees (guid, assignee, assigner, assigned)" . +            " VALUES ('" . database::esc($this->guid) . "', '" . +            database::esc($assignee->guid) . "', '" . database::esc($assigner->guid) . +            "', '" . database::esc(self::getCurrentTimestamp()) . "')"; -        return new user($this->assignee); +        database::query($query); +        return true;      }      /* -     * Reset the seen flag and reassign this issue. +     * Dismiss an assignee, effectively unassigning them.  Returns +     * false if user is not already an active assignee, or if another +     * error occurs; true otherwise.       */ -    public function assignTo(user $assignee) : void +    public function dismissAssignee(user $assignee, user $dismisser) : bool      { -        $this->seen = 0; -        $this->assignee = $assignee->guid; -        $this->saveObj(); +        if (!$assignee->isAssignedTo($this) || !isset($assignee->guid) +            || !isset($dismisser->guid)) +            return false; + +        $query = "UPDATE assignees SET dismisser = '" . database::esc($dismisser->guid) . +            "', dismissed = '" . database::esc(self::getCurrentTimestamp()) . +            "' WHERE guid = '" . database::esc($this->guid) . "' AND assignee = '" . +            database::esc($assignee->guid) . "'"; + +        database::query($query); +        return true; +    } + +    /* +     * Signoff an assignee's portion of this issue.  Returns false +     * if user is not already an active assignee, or if another +     * error occurs; true otherwise. +     */ +    public function signoffAssignee(user $assignee) : bool +    { +        if (!$assignee->isAssignedTo($this) || !isset($assignee->guid)) +            return false; + +        $query = "UPDATE assignees SET signedoff = '" . +            database::esc(self::getCurrentTimestamp()) . "' WHERE guid = '" . +            database::esc($this->guid) . "' AND assignee = '" . +            database::esc($assignee->guid) . "'"; + +        database::query($query); +        return true; +    } + +    /* +     * Get the OP message for this issue. +     */ +    public function getOPMesg() : mesg +    { +        return new mesg($this->mesg); +    } + +    /* +     * Get all messages on this issue.  The OP message is filtered +     * from results.  Messages are sorted by date created. +     */ +    public function getMesgs_ordByDatetime() : array +    { +        $mesgs = parent::getMesgs_ordByDatetime(); +        $i = -1; + +        foreach ($mesgs as $k => $m) +        { +            if ($m->guid == $this->mesg) +            { +                $i = $k; +                break; +            } +        } + +        if ($i != -1) +        { +            unset($mesgs[$i]); +            $mesgs = array_values($mesgs); +        } + +        return $mesgs; +    } + +    /* +     * Get the user that closed this issue.  If the issue is still +     * open, NULL is returned. +     */ +    public function getCloser() : ?user +    { +        if (!isset($this->closer) || $this->closer == "") +            return NULL; + +        return new user($this->closer);      }      /* @@ -98,9 +225,9 @@ class issue extends obj      /*       * Advance this issue in the pipeline, closing it if already in the -     * last stage. +     * last stage.  A closer is needed incase a close takes place.       */ -    public function advance() : void +    public function advance(user $closer) : void      {          $stage = $this->getParent(); @@ -108,7 +235,7 @@ class issue extends obj              return;          if (!($next = $stage->getNext())) -            $this->close(); +            $this->close($closer);          else              $this->setParent($next);      } @@ -116,12 +243,24 @@ class issue extends obj      /*       * Mark this issue as completed and closed.       */ -    public function close() : void +    public function close(user $closer) : void      {          $pad = $this->getParent()->getParent();          if ($pad) +        { +            $this->closer = $closer->guid; +            $this->closed = self::getCurrentTimestamp();              $this->setParent($pad); +        } +    } + +    /* +     * Check whether issue is currently open or closed. +     */ +    public function isOpen() : bool +    { +        return self::typeOf($this->parent) != "pad";      }  } diff --git a/srvs/mysql.sql b/srvs/mysql.sql index 13db8c7..72167d4 100644 --- a/srvs/mysql.sql +++ b/srvs/mysql.sql @@ -70,6 +70,23 @@ CREATE TABLE views (  );  /* + * Scrott issues may have multiple assignees.  This table is used to + * co-relate assignees and issues along with additional meta-data. + */ +DROP TABLE IF EXISTS assignees; +CREATE TABLE assignees ( +    guid        varchar(8)          NOT NULL,                   /* guid of issue */ +    assignee    varchar(8)          NOT NULL,                   /* user */ +    assigner    varchar(8)          NOT NULL,                   /* user */ +    assigned    datetime            NOT NULL,                   /* timestamp */ +    dismisser   varchar(8)          NOT NULL    DEFAULT '',     /* user */ +    dismissed   varchar(64)         NOT NULL    DEFAULT '',     /* timestamp */ +    signedoff   varchar(64)         NOT NULL    DEFAULT '',     /* timestamp */ + +    PRIMARY KEY (guid, assignee) +); + +/*   * Base table for Scrott objects   *   * Explaination of permissions... @@ -179,10 +196,10 @@ DROP TABLE IF EXISTS issues;  CREATE TABLE issues (      guid        varchar(8)          NOT NULL,      numb        int(32)             NOT NULL    DEFAULT 0, -    assignee    varchar(8)          NOT NULL    DEFAULT '', -    seen        int(1)              NOT NULL    DEFAULT 0,      /* has the assignee seen this yet? */ -    description text                NOT NULL, -    due         varchar(64)         NOT NULL    DEFAULT '', +    mesg        varchar(8)          NOT NULL    DEFAULT '', +    closer      varchar(8)          NOT NULL    DEFAULT '',     /* user */ +    closed      varchar(64)         NOT NULL    DEFAULT '',     /* timestamp */ +    due         varchar(64)         NOT NULL    DEFAULT '',     /* timestamp */      tags        text                NOT NULL,      PRIMARY KEY (guid) | 
