_binary From 9fe2992987a09321ce2f5020e35a03e865f9f3a1 Mon Sep 17 00:00:00 2001
From: Prajna Puranik <prajnagp@gmail.com>
Date: Thu, 20 Apr 2023 20:37:18 -0700
Subject: [PATCH 1/9] Added flagging and moderation functionality

Signed-off-by: Prajna Puranik <prajnagp@gmail.com>
---
 src/configs/Config.php                        |   4 +-
 src/configs/Createdb.php                      |  10 +
 .../components/SocialComponent.php            |  81 ++++-
 src/models/GroupModel.php                     | 289 ++++++++++++++++++
 src/models/ProfileModel.php                   |   4 +
 src/views/elements/GroupfeedElement.php       |  86 +++++-
 src/views/helpers/IconlinkHelper.php          |   2 +
 7 files changed, 472 insertions(+), 4 deletions(-)

diff --git a/src/configs/Config.php b/src/configs/Config.php
index 6fcace61e..0246ab1eb 100755
--- a/src/configs/Config.php
+++ b/src/configs/Config.php
@@ -162,7 +162,7 @@ nsconddefine('GENERATOR_STRING', "Yioop");
  * Version number for upgrade database function
  * @var int
  */
-nsdefine('DATABASE_VERSION', 78);
+nsdefine('DATABASE_VERSION', 79);
 /**
  * Minimum Version fo Yioop for which keyword ad script
  * still works with this version
@@ -636,6 +636,8 @@ nsdefine('HELP_GROUP_ID', 3);
  * belong.
  */
 nsconddefine('SEARCH_GROUP_ID', 4);
+/*ID of the group for moderators */
+nsconddefine('MODERATION_GROUP_ID', 5);
 /** Length of advertisement name string */
 nsdefine('ADVERTISEMENT_NAME_LEN', 25);
 /** Length of advertisement text description */
diff --git a/src/configs/Createdb.php b/src/configs/Createdb.php
index 670853f13..8b5529bf2 100755
--- a/src/configs/Createdb.php
+++ b/src/configs/Createdb.php
@@ -201,6 +201,16 @@ $db->execute("INSERT INTO USER_GROUP VALUES (" . PUBLIC_USER_ID . ", " .
 /* we insert 1 by 1 rather than comma separate as sqlite
    does not support comma separated inserts
  */
+ //Create a group for moderator users
+  $sql = "INSERT INTO SOCIAL_GROUPS VALUES(".MODERATION_GROUP_ID.",'Moderation','" .
+      $creation_time . "','".ROOT_ID."', '"  .INVITE_ONLY_JOIN . "', '" . GROUP_READ .
+      "', " . NON_VOTING_GROUP.", " . FOREVER . ", 0)";
+  $db->execute($sql);
+  $now = time();
+  $db->execute("INSERT INTO USER_GROUP VALUES (" . ROOT_ID . ", " .
+      MODERATION_GROUP_ID . ", " . ACTIVE_STATUS . ", $now)");
+  $db->execute("INSERT INTO USER_GROUP VALUES (" . PUBLIC_USER_ID . ", " .
+      MODERATION_GROUP_ID . ", " . ACTIVE_STATUS . ", $now)");
 $locales = [
     ['ar', 'العربية', 'rl-tb'],
     ['bn', 'বাংলা', 'lr-tb', ''],
diff --git a/src/controllers/components/SocialComponent.php b/src/controllers/components/SocialComponent.php
index 20e4aa268..da4ae4f82 100644
--- a/src/controllers/components/SocialComponent.php
+++ b/src/controllers/components/SocialComponent.php
@@ -1348,7 +1348,7 @@ class SocialComponent extends Component implements CrawlConstants
                 $user_messages_id), [$user_id]);
             $possible_arguments = ["addcomment", "addcontact", "addgroup",
                 "deletepost", "downvote", "newthread", "status", "updatepost",
-                "upvote"];
+                "upvote", "flagpost", "approveflagged", "deleteflagged"];
         }
         if (isset($_REQUEST['arg']) &&
             in_array($_REQUEST['arg'], $possible_arguments)) {
@@ -1523,6 +1523,76 @@ class SocialComponent extends Component implements CrawlConstants
                             ['page_name']);
                     }
                     break;
+                case "flagpost":
+                    if (!isset($_REQUEST['post_id'])) {
+                        return $parent->redirectWithMessage(
+                            tl('social_component_flag_error'),
+                            ['page_name']);
+                        break;
+                    }
+                    $post_id = $parent->clean($_REQUEST['post_id'], "int");
+                    $group_item = $group_model->getGroupItem($post_id);
+                    if ($group_model->alreadyFlagged($user_id, $post_id)) {
+                        return $parent->redirectWithMessage(
+                            tl('social_component_already_flagged'));
+                    }
+                    $success = false;
+                    if ($group_item) {
+                        $success =
+                            $group_model->flagGroupItem($post_id, $user_id);
+                    }
+                    if ($success) {
+                        return $parent->redirectWithMessage(
+                            tl('social_component_item_flagged'),
+                            ['page_name']);
+                    } else {
+                        return $parent->redirectWithMessage(
+                            tl('social_component_no_item_flagged'),
+                            ['page_name']);
+                    }
+                    break;
+                case "approveflagged":
+                    if (!isset($_REQUEST['post_id'])){
+                        return $parent->redirectWithMessage(
+                          tl('social_component_no_approve_access'));
+                    }
+                    $post_id = $parent->clean($_REQUEST['post_id'], "int");
+                    $group_item = $group_model->getGroupItem($post_id);
+                    $success = false;
+                    if ($group_item) {
+                       $success = $group_model->approveFlaggedPost($post_id);
+                    }
+                    if ($success) {
+                        return $parent->redirectWithMessage(
+                            tl('social_component_flagged_item_approved'),
+                            ['page_name']);
+                    } else {
+                        return $parent->redirectWithMessage(
+                            tl('social_component_no_flagged_item_approved'),
+                            ['page_name']);
+                    }
+                    break;
+                case "deleteflagged":
+                    if (!isset($_REQUEST['post_id'])){
+                        return $parent->redirectWithMessage(
+                          tl('social_component_no_delete_access'));
+                    }
+                    $post_id = $parent->clean($_REQUEST['post_id'], "int");
+                    $group_item = $group_model->getGroupItem($post_id);
+                    $success = false;
+                    if ($group_item) {
+                        $success = $group_model->deleteFlaggedPost($post_id);
+                    }
+                    if ($success) {
+                        return $parent->redirectWithMessage(
+                            tl('social_component_flagged_item_deleted'),
+                            ['page_name']);
+                    } else {
+                        return $parent->redirectWithMessage(
+                            tl('social_component_no_flagged_item_deleted'),
+                            ['page_name']);
+                    }
+                    break;
                 case "downvote":
                     if (!isset($_REQUEST['group_id']) || !$_REQUEST['group_id']
                         ||!isset($_REQUEST['post_id']) ||
@@ -2702,6 +2772,15 @@ class SocialComponent extends Component implements CrawlConstants
             }
             $page['USER_ICON'] = $user_model->getUserIconUrl($page['USER_ID']);
             $page[self::TITLE] = $page['TITLE'];
+            //functionality for moderation group
+            if($page['GROUP_ID'] == 5){
+               $parent_group_id = $group_model->getParentGroupId($page['ID']);
+               $page[self::TITLE] = sprintf("Bad Post group %d",
+                    $parent_group_id);
+               $page['FLAG'] = $group_model->getFlagCount($page['ID']);
+               $page['PARENT_ITEM_ID'] =
+                    $group_model->getParentIdOfParentPost($page['ID']);
+            }
             unset($page['TITLE']);
             $description = $page['DESCRIPTION'];
             //start code for sharing crawl mixes
diff --git a/src/models/GroupModel.php b/src/models/GroupModel.php
index 04d51a89c..b7bac7974 100644
--- a/src/models/GroupModel.php
+++ b/src/models/GroupModel.php
@@ -951,6 +951,47 @@ class GroupModel extends Model implements MediaConstants
         }
         return $id;
     }
+    /**
+     * Adds a flagged group item to the moderation group
+     *
+     * @param int $parent_id thread id to use for the item
+     * @param int $group_id what group the item should be added to
+     * @param int $user_id of user making the post
+     * @param string $title title of the group feed item
+     * @param string $description actual content of the post
+     * @param int $type flag saying what kind of group item this is. One of
+     *      STANDARD_GROUP_ITEM, WIKI_GROUP_ITEM (used for threads discussing
+     *      a wiki page)
+     * @param int $post_time timstamp for when this group item was created
+     *      default to the current time
+     * @param string $url a url associated with this group item (mainly for
+     *      search group)
+     * @return int $id of item added
+     */
+    public function addGroupItemModeration($parent_id, $group_id, $user_id,
+        $title, $description, $flag, $parent_item_id, $parent_group_id,
+        $type= C\STANDARD_GROUP_ITEM, $post_time = 0, $url = "")
+    {
+        $db = $this->db;
+        if ($post_time == 0) {
+            $post_time = time();
+        }
+        if($this->isGroupEncrypted($parent_group_id)){
+            $key = $this->getGroupKey($parent_group_id);
+            $title = $this->decrypt($title, $key);
+            $description = $this->decrypt($description, $key);
+        }
+        $sql = "INSERT INTO GROUP_ITEM (PARENT_ID, GROUP_ID, USER_ID, URL,
+             TITLE, DESCRIPTION, PUBDATE, EDIT_DATE, FLAG, PARENT_ITEM_ID, TYPE)
+             VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+        $db->execute($sql, [$parent_id, $group_id, $user_id, $url, $title,
+             $description, $post_time, $post_time, $flag, $parent_item_id,
+             $type]);
+        $id = $db->insertID("GROUP_ITEM");
+        $update_sql = "UPDATE GROUP_ITEM SET PARENT_ID = ? WHERE ID = ?";
+        $db->execute($update_sql, [$id, $id]);
+        return $id;
+    }
     /**
      * Encrypts  data based on provided key.
      *
@@ -992,6 +1033,47 @@ class GroupModel extends Model implements MediaConstants
         }
         return $out_data;
     }
+    /**
+     * Encrypts flag value based on provided key.
+     *
+     * @param string $data what data to encrypt
+     * @param string $key what key to use to encrypt
+     * @param string $cipher_method a cipher encrypt/decrypt method
+     *      supported by OpenSSL
+     * @return string $out_data the encrypted string
+     */
+    public function encryptFlag($data, $key, $cipher_method = 'AES-256-ECB')
+    {
+        $block_length = 16;
+        $padding = $block_length - (strlen($data) % $block_length);
+        $data = $data . str_repeat(chr($padding), $padding);
+        $key = pack('H*', $key);
+        $cipher_method = strtolower($cipher_method);
+        $encrypted = openssl_encrypt($data, $cipher_method, $key,
+                     OPENSSL_RAW_DATA|OPENSSL_NO_PADDING);
+        return base64_encode($encrypted);
+    }
+    /**
+     * Decrypts flag value based on provided key.
+     *
+     * @param string $data what data to decrypt
+     * @param string $key what key to use to decrypt
+     * @param string $cipher_method a cipher encrypt/decrypt method
+     *      supported by OpenSSL
+     * @return string $out_data the decrypted string
+     */
+    public function decryptFlag($encrypted_data, $key,
+    $cipher_method = 'AES-256-ECB')
+    {
+        $encrypted_data = base64_decode($encrypted_data);
+        $key = pack('H*', $key);
+        $cipher_method = strtolower($cipher_method);
+        $out_data = openssl_decrypt($encrypted_data, $cipher_method, $key,
+                     OPENSSL_RAW_DATA|OPENSSL_NO_PADDING);
+        $padding = ord(substr($out_data, -1));
+        $out_data = substr($out_data, 0, -$padding);
+        return $out_data;
+    }
     /**
      * Updates a group feed item's title and description. This assumes
      * the given item already exists.
@@ -1044,6 +1126,213 @@ class GroupModel extends Model implements MediaConstants
         }
         return $affected_rows;
     }
+    /**
+     * Flags a group feed item in the GROUP_ITEM table.
+     *
+     * @param int $post_id of item to be flagged
+     * @param int $user_id the id of the person trying to flag the item
+     * If user does not belong to the group, the item won't be flagged
+     */
+    public function flagGroupItem($post_id, $user_id)
+    {
+        $db = $this->db;
+        $sql = "SELECT * FROM GROUP_ITEM WHERE ID = ?";
+        $stmt = $db->execute($sql, [$post_id]);
+        $result = $stmt->fetch();
+        $group_id = $result['GROUP_ID'];
+        $flag_count = $this->getFlagCount($post_id);
+        if ($this->isGroupEncrypted($group_id))
+        {
+            $threshold = $this->getThresholdValue($group_id);
+            $key = $this->getGroupKey($group_id);
+            if($flag_count == 0){
+                $flag_count = $this->encryptFlag(strval($flag_count), $key);
+            }
+            if ($flag_count === $threshold) {
+                $flag_count_decrypt = $this->decryptFlag(
+                base64_decode($flag_count), $key);
+                $this->addGroupItemModeration(0, 5, $result['USER_ID'],
+                       $result['TITLE'],$result['DESCRIPTION'],
+                       $flag_count_decrypt, $post_id, $group_id,
+                       C\STANDARD_GROUP_ITEM, 0, "");
+            }else{
+                $flag_count = $this->encryptFlag(strval($flag_count), $key);
+            }
+        }
+        else{
+            $flag_count = $flag_count + 1;
+            $threshold = 3;
+            if($flag_count > $threshold){
+                return false;
+            }
+            if($flag_count ===  $threshold){
+                $this->addGroupItemModeration(0, 5, $result['USER_ID'],
+                       $result['TITLE'],$result['DESCRIPTION'], $flag_count,
+                       $post_id, $group_id, C\STANDARD_GROUP_ITEM, 0, "");
+            }
+        }
+        $sql = "INSERT INTO GROUP_ITEM_FLAG VALUES (?, ?)";
+        $this->db->execute($sql, [$user_id, $post_id]);
+        $update_sql = "UPDATE GROUP_ITEM SET FLAG = ? WHERE ID = ?
+                AND GROUP_ID IN (SELECT GROUP_ID FROM USER_GROUP
+                WHERE USER_ID = ?)";
+        $db->execute($update_sql, [$flag_count, $post_id, $user_id]);
+        $affected_rows = $db->affectedRows();
+        return  $affected_rows;
+    }
+    /**
+     * Retrieves flag column value from the database
+     *
+     * @param int $post_id of thread whose flag value needs to be fetched
+     */
+    public function getFlagCount($post_id)
+    {
+        $db = $this->db;
+        $sql = "SELECT FLAG FROM GROUP_ITEM WHERE ID = ?";
+        $stmt = $db->execute($sql, [$post_id]);
+        if (!$stmt) {
+            return false;
+        }
+        if (!($row = $db->fetchArray($stmt))) {
+            return false;
+        }
+        return $row['FLAG'];
+    }
+    /**
+     * Checks if post is already flagged
+     *
+     * @param int $post_id to check if the thread is already flagged
+     */
+    public function alreadyFlagged($user_id, $post_id)
+    {
+        $db = $this->db;
+        $sql = "SELECT COUNT(*) AS NUM FROM GROUP_ITEM_FLAG WHERE USER_ID = ?
+                AND ITEM_ID = ?";
+        $result = $db->execute($sql, [$user_id, $post_id]);
+        if (!$result) {
+             return false;
+        }
+        $row = $db->fetchArray($result);
+        if (!$row || !isset($row['NUM'])) {
+            return false;
+        }
+        return ($row['NUM'] > 0);
+    }
+    /**
+     * Get the group id of the parent thread
+     *
+     * @param int $post_id denotes the thread
+     */
+    public function getParentGroupId($post_id)
+    {
+       $db = $this->db;
+       $sql = "SELECT GROUP_ID FROM GROUP_ITEM WHERE ID =
+               (SELECT PARENT_ITEM_ID FROM GROUP_ITEM WHERE ID = ?)";
+       $stmt = $db->execute($sql, [$post_id]);
+        if (!$stmt) {
+            return false;
+        }
+        if (!($row = $db->fetchArray($stmt))) {
+            return false;
+        }
+        return $row['GROUP_ID'];
+    }
+    /**
+     * Get the minimum number of times a post has to be flagged to be reviewed
+     * by the moderator
+     *
+     * @param int $group_id to get threshold value for the given group
+     */
+    public function getThresholdValue($group_id)
+    {
+       $key = $this->getGroupKey($group_id);
+       $x = 3;
+       $res = 0;
+       while($x > 0){
+         $res = $this->encryptFlag(strval($res), $key);
+         $x--;
+       }
+       return $res;
+    }
+    /**
+     * Get the id of the parent thread of a post
+     *
+     * @param int $post_id denotes the thread
+     */
+    public function getParentPostById($post_id)
+    {
+        $db = $this->db;
+        $sql = "SELECT ID FROM GROUP_ITEM WHERE ID =
+                (SELECT PARENT_ITEM_ID FROM GROUP_ITEM WHERE ID = ?)";
+        $stmt = $db->execute($sql, [$post_id]);
+        if (!$stmt) {
+            return false;
+        }
+        if (!($row = $db->fetchArray($stmt))) {
+            return false;
+        }
+        return $row['ID'];
+    }
+    /**
+     * Get parent_id of the parent thread of a given post
+     *
+     * @param int $post_id denotes the post
+     */
+    public function getParentIdOfParentPost($post_id)
+    {
+        $db = $this->db;
+        $sql = "SELECT PARENT_ID FROM GROUP_ITEM WHERE ID =
+                (SELECT PARENT_ITEM_ID FROM GROUP_ITEM WHERE ID = ?)";
+        $stmt = $db->execute($sql, [$post_id]);
+        if (!$stmt) {
+            return false;
+        }
+        if (!($row = $db->fetchArray($stmt))) {
+            return false;
+        }
+        return $row['PARENT_ID'];
+
+    }
+    /**
+     * Moderator approves a flagged post
+     *
+     * @param int $post_id to fetch the flagged post
+     */
+    public function approveFlaggedPost($post_id)
+    {
+         //get parent_item_id and go to the original post
+         $item_id= $this->getParentPostById($post_id);
+         //reset flag column value to zero
+         $db = $this->db;
+         $update_sql = "UPDATE GROUP_ITEM SET FLAG = 0 WHERE ID = ?";
+         $db->execute($update_sql, [$item_id]);
+         $reset_sql = "UPDATE GROUP_ITEM SET FLAG = -1 WHERE ID = ?";
+         $db->execute($reset_sql, [$post_id]);
+         $affected_rows = $db->affectedRows();
+         return  $affected_rows;
+    }
+    /**
+     * Moderator deletes a flagged post
+     *
+     * @param int $post_id to fetch the flagged post
+     */
+    public function deleteFlaggedPost($post_id)
+    {
+         $description = "This post has been flagged";
+         $parent_group_id = $this->getParentGroupId($post_id);
+         if($this->isGroupEncrypted($parent_group_id)){
+            $key = $this->getGroupKey($parent_group_id);
+            $description = $this->encrypt($description, $key);
+         }
+         $item_id = $this->getParentPostById($post_id);
+         $db = $this->db;
+         $update_sql = "UPDATE GROUP_ITEM SET DESCRIPTION =? WHERE ID = ?";
+         $db->execute($update_sql, [$description, $item_id]);
+         $reset_sql = "UPDATE GROUP_ITEM SET FLAG = -1 WHERE ID = ?";
+         $db->execute($reset_sql, [$post_id]);
+         $affected_rows = $db->affectedRows();
+         return  $affected_rows;
+    }
     /**
      * Gets the group feed items visible to a user with $user_id
      * and which match the supplied search criteria found in $search_array,
diff --git a/src/models/ProfileModel.php b/src/models/ProfileModel.php
index 946a5222a..c3c91579c 100755
--- a/src/models/ProfileModel.php
+++ b/src/models/ProfileModel.php
@@ -195,6 +195,8 @@ class ProfileModel extends Model
                 C\TIMESTAMP_LEN . "),
                 EDIT_DATE NUMERIC(" . C\TIMESTAMP_LEN . "),
                 UPS $integer DEFAULT 0, DOWNS $integer DEFAULT 0,
+                FLAG $integer DEFAULT 0,
+                PARENT_ITEM_ID $integer DEFAULT 0,
                 TYPE $integer DEFAULT " . C\STANDARD_GROUP_ITEM . ")",
             "GI_GROUP_ID_INDEX" => "CREATE INDEX GI_GROUP_ID_INDEX ON
                 GROUP_ITEM (GROUP_ID)",
@@ -210,6 +212,8 @@ class ProfileModel extends Model
                 GI_PARENT_ID_PUBDATE_INDEX ON GROUP_ITEM (PARENT_ID, PUBDATE)",
             "GROUP_ITEM_VOTE" => "CREATE TABLE GROUP_ITEM_VOTE(
                 USER_ID $integer, ITEM_ID $integer)",
+            "GROUP_ITEM_FLAG" => "CREATE TABLE GROUP_ITEM_FLAG(
+                USER_ID $integer, ITEM_ID $integer)",
             "GROUP_ITEM_RECOMMENDATION" => "CREATE TABLE
                 GROUP_ITEM_RECOMMENDATION (USER_ID $integer NOT NULL,
                 ITEM_ID $integer NOT NULL, ITEM_TYPE $integer NOT NULL,
diff --git a/src/views/elements/GroupfeedElement.php b/src/views/elements/GroupfeedElement.php
index a4c9e1a08..df5e6316b 100644
--- a/src/views/elements/GroupfeedElement.php
+++ b/src/views/elements/GroupfeedElement.php
@@ -202,6 +202,10 @@ class GroupfeedElement extends Element implements CrawlConstants
         $is_thread = !empty($data['JUST_THREAD']);
         $is_group = !empty($data['JUST_GROUP_ID']);
         $is_user = !empty($data['JUST_USER_ID']);
+        $parent_item_id = (!empty($data['WIKI_PARENT_ID'])) ?
+            $data['WIKI_PARENT_ID'] : (
+            (empty($data['PAGES'][0]["PARENT_ITEM_ID"])) ?
+            -1 : $data['PAGES'][0]["PARENT_ITEM_ID"]);
         $is_page_with_comments = ($data['ELEMENT'] == 'wiki');
         $is_all_feed = !$is_thread && !$is_group && !$is_page_with_comments &&
             !$is_user;
@@ -366,6 +370,21 @@ class GroupfeedElement extends Element implements CrawlConstants
                     tl('groupfeed_element_delete') ?>" >🗑<span class="none"><?=
                     tl('groupfeed_element_delete') ?></span></span></a>
                 </div><?php
+            }else if(in_array($member_access, $can_comment) && $is_thread){
+            ?>
+            <div class="float-opposite button-container">
+            <?php
+            $flag_url = (empty($data['WIKI_FEED_BASE'])) ?
+                          $paging_query : $data['WIKI_FEED_BASE'];
+            ?>
+            <a onclick="return confirm('<?=
+             tl('groupfeed_element_confirm_flag') ?>');"
+             href="<?= $flag_url .'&amp;arg=flagpost&amp;' . "post_id=" .
+             $page['ID'] ?>">
+             <?php $this->view->helper("iconlink")->renderButton(
+             $flag_url .'&amp;arg=flagpost&amp;' . "post_id=" . $page['ID'],
+             "flag");?></a>
+            </div><?php
             }
             $title_class = "";
             if (!empty($data['DISCUSS_THREAD'])) {
@@ -410,7 +429,21 @@ class GroupfeedElement extends Element implements CrawlConstants
             </div>
             <div class="<?=$feed_item_body ?>" <?=$feed_item_body_first
              ?> ><?php
-            if (!$is_thread || $first_page) {?>
+            if (!$is_thread && $first_page && isset($page['GROUP_ID'])
+             && $page['GROUP_ID'] == 5 && isset($page['FLAG']) &&
+             $page['FLAG'] == -1){
+             ?>
+                <h2><a href="<?= $this->formatHref(B\feedsUrl('thread',
+                $page['PARENT_ID'], true, $data['CONTROLLER']) . $token) ?>"
+                <?= $no_follow ?> <?=$title_class ?> id='title<?=$page['ID']?>'
+                <?php
+                if ($open_in_tabs) {
+                     ?> target="_blank" rel="noopener"<?php
+                }
+                ?> style="color: green;"><?= $page[self::TITLE] ?></a></h2>
+                <?php
+             }
+            else if (!$is_thread || $first_page) {?>
                 <h2><a href="<?= $this->formatHref(B\feedsUrl('thread',
                     $page['PARENT_ID'], true, $data['CONTROLLER']) .
                     $token) ?>" <?= $no_follow ?> <?=$title_class ?>
@@ -419,7 +452,7 @@ class GroupfeedElement extends Element implements CrawlConstants
                         ?> target="_blank" rel="noopener"<?php
                     }
                     ?>><?= $page[self::TITLE] ?></a><?php
-                    if (!$is_page_with_comments) {
+                    if (!$is_page_with_comments && $page['GROUP_ID'] != 5) {
                         if (isset($page['NUM_POSTS'])) {
                             e(" (");
                             e(tl('groupfeed_element_num_posts',
@@ -502,6 +535,7 @@ class GroupfeedElement extends Element implements CrawlConstants
                     $page["MEMBER_ACCESS"], $can_comment)) {
                 $this->drawCommentForm($data['JUST_THREAD'], $data["GROUP_ID"],
                     $data);
+                $this->showOriginalPost($data["GROUP_ID"], $data, $parent_item_id);
             }
         }
     }
@@ -619,6 +653,30 @@ class GroupfeedElement extends Element implements CrawlConstants
             "for=\"comment-add-comment\" ><?=
             tl('groupfeed_element_comment') ?></label></button>");
         </script>
+        <?php if ($group_id == 5) {
+                $flag_url = C\BASE_URL . "/group?" .
+                C\CSRF_TOKEN . "=" . $data[C\CSRF_TOKEN];
+           ?>
+
+           <button class="button-box"
+            onclick="if(confirm('Are you sure you want to approve ' +
+            'this flagged post?')) {
+            window.location.href='<?php echo $flag_url .
+            '&amp;arg=approveflagged&amp;'."post_id=" . $thread_id ?>';
+            }">
+            <label>Approve</label>
+          </button>
+
+           <button class="button-box"
+            onclick="if(confirm('Are you sure you want to delete ' +
+            'this flagged post?')) {
+            window.location.href='<?php echo $flag_url .
+            '&amp;arg=deleteflagged&amp;'."post_id=" . $thread_id ?>';
+            }">
+           <label>Delete</label>
+           </button>
+
+          <?php } ?>
         <noscript>
         <h2><label for="comment-add-comment" ><b><?=
             tl('groupfeed_element_comment') ?></b></label></h2>
@@ -668,6 +726,30 @@ class GroupfeedElement extends Element implements CrawlConstants
         </div>
         <?php
     }
+    /**
+    * Used to display the original thread of the flagged comment
+    *
+    * @param int $group_id of group to draw form for
+    * @param array $data containing other field needed to draw the form
+    * @param int $parent_item retrieves the original thread of flagged comment
+    */
+     private function showOriginalPost($group_id, $data, $parent_item_id)
+     {
+        ?>
+        <div class="button-group-result">
+        <?php if($group_id == 5){
+           $original_post_url = C\BASE_URL . "/thread" .
+           "/" . $parent_item_id . "?" . C\CSRF_TOKEN . "=" .
+           $data[C\CSRF_TOKEN];
+        ?>
+        <button class="button-box"
+        onclick="window.location.href='<?php echo $original_post_url ?>';">
+        <label> Original Post</label>
+        </button>
+        <?php } ?>
+        </div>
+        <?php
+    }
     /**
      * Used to render the dropdown for paths within the top group feed
      * drop down
diff --git a/src/views/helpers/IconlinkHelper.php b/src/views/helpers/IconlinkHelper.php
index 723e05d4b..d2d3b8686 100644
--- a/src/views/helpers/IconlinkHelper.php
+++ b/src/views/helpers/IconlinkHelper.php
@@ -61,6 +61,8 @@ class IconlinkHelper extends Helper
             "crawls" => [tl('iconlink_helper_manage_crawls'),  '🕸️'],
             "delete" => [tl('iconlink_helper_delete'), '🗑️',
                 tl('iconlink_helper_confirm')],
+            "flag" => [tl('iconlink_helper_flag'), '🏳️',
+                tl('iconlink_helper_flag_confirm')],
             "discuss_page" => [
                 tl('iconlink_helper_discuss_page'), '🗣️'],
             "edit" => [ tl('iconlink_helper_edit'),  '✏️'],
-- 
2.37.1 (Apple Git-137.1)


From 60dc61c198466bb15ffd323aac8918ca4a8c5957 Mon Sep 17 00:00:00 2001
From: Prajna Puranik <prajnagp@gmail.com>
Date: Sun, 30 Apr 2023 18:49:44 -0700
Subject: [PATCH 2/9] Added localized strings

Signed-off-by: Prajna Puranik <prajnagp@gmail.com>
---
 src/views/elements/GroupfeedElement.php | 14 +++++++-------
 src/views/helpers/IconlinkHelper.php    |  2 +-
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/views/elements/GroupfeedElement.php b/src/views/elements/GroupfeedElement.php
index df5e6316b..8f39d883b 100644
--- a/src/views/elements/GroupfeedElement.php
+++ b/src/views/elements/GroupfeedElement.php
@@ -659,17 +659,17 @@ class GroupfeedElement extends Element implements CrawlConstants
            ?>
 
            <button class="button-box"
-            onclick="if(confirm('Are you sure you want to approve ' +
-            'this flagged post?')) {
-            window.location.href='<?php echo $flag_url .
-            '&amp;arg=approveflagged&amp;'."post_id=" . $thread_id ?>';
-            }">
+             onclick="if (confirm('<?php echo
+             tl('iconlink_helper_approve'); ?>')) {
+               window.location.href='<?php echo $flag_url ?>
+               &amp;arg=approveflagged&amp;post_id=<?php echo $thread_id ?>';
+             }">
             <label>Approve</label>
           </button>
 
            <button class="button-box"
-            onclick="if(confirm('Are you sure you want to delete ' +
-            'this flagged post?')) {
+            onclick="if(confirm('<?php echo
+            tl('iconlink_helper_delete_confirm'); ?>')) {
             window.location.href='<?php echo $flag_url .
             '&amp;arg=deleteflagged&amp;'."post_id=" . $thread_id ?>';
             }">
diff --git a/src/views/helpers/IconlinkHelper.php b/src/views/helpers/IconlinkHelper.php
index d2d3b8686..3543e9c68 100644
--- a/src/views/helpers/IconlinkHelper.php
+++ b/src/views/helpers/IconlinkHelper.php
@@ -60,7 +60,7 @@ class IconlinkHelper extends Helper
                 tl('iconlink_helper_combined_discussions'), '📃'],
             "crawls" => [tl('iconlink_helper_manage_crawls'),  '🕸️'],
             "delete" => [tl('iconlink_helper_delete'), '🗑️',
-                tl('iconlink_helper_confirm')],
+                tl('iconlink_helper_delete_confirm')],
             "flag" => [tl('iconlink_helper_flag'), '🏳️',
                 tl('iconlink_helper_flag_confirm')],
             "discuss_page" => [
-- 
2.37.1 (Apple Git-137.1)


From 18ad862fdec9c35aa1dd15751e5b97caf67a4a26 Mon Sep 17 00:00:00 2001
From: Prajna Puranik <prajnagp@gmail.com>
Date: Sun, 30 Apr 2023 18:54:11 -0700
Subject: [PATCH 3/9] Fixed strings to be localized

Signed-off-by: Prajna Puranik <prajnagp@gmail.com>
---
 src/views/elements/GroupfeedElement.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/views/elements/GroupfeedElement.php b/src/views/elements/GroupfeedElement.php
index 8f39d883b..bf94b51d3 100644
--- a/src/views/elements/GroupfeedElement.php
+++ b/src/views/elements/GroupfeedElement.php
@@ -662,7 +662,7 @@ class GroupfeedElement extends Element implements CrawlConstants
              onclick="if (confirm('<?php echo
              tl('iconlink_helper_approve'); ?>')) {
                window.location.href='<?php echo $flag_url ?>
-               &amp;arg=approveflagged&amp;post_id=<?php echo $thread_id ?>';
+               &amp;arg=approveflagged&amp;'."post_id=" . $thread_id ?>';
              }">
             <label>Approve</label>
           </button>
-- 
2.37.1 (Apple Git-137.1)


From 7d97941f9c0c44ba2f4c7cec4202a50754753e15 Mon Sep 17 00:00:00 2001
From: Prajna Puranik <prajnagp@gmail.com>
Date: Sun, 30 Apr 2023 21:01:27 -0700
Subject: [PATCH 4/9] Fixed Moderator Approve button

Signed-off-by: Prajna Puranik <prajnagp@gmail.com>
---
 src/views/elements/GroupfeedElement.php | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/views/elements/GroupfeedElement.php b/src/views/elements/GroupfeedElement.php
index bf94b51d3..94a40fef6 100644
--- a/src/views/elements/GroupfeedElement.php
+++ b/src/views/elements/GroupfeedElement.php
@@ -661,8 +661,8 @@ class GroupfeedElement extends Element implements CrawlConstants
            <button class="button-box"
              onclick="if (confirm('<?php echo
              tl('iconlink_helper_approve'); ?>')) {
-               window.location.href='<?php echo $flag_url ?>
-               &amp;arg=approveflagged&amp;'."post_id=" . $thread_id ?>';
+               window.location.href='<?php echo $flag_url .
+               '&amp;arg=approveflagged&amp;'."post_id=" . $thread_id ?>';
              }">
             <label>Approve</label>
           </button>
-- 
2.37.1 (Apple Git-137.1)


From 103fa5154ddf29ea066b48e16a81138b0d0e6725 Mon Sep 17 00:00:00 2001
From: Prajna Puranik <prajnagp@gmail.com>
Date: Tue, 23 May 2023 20:16:42 -0700
Subject: [PATCH 5/9] Secret sharing for encryption key

---
 src/configs/Createdb.php                      |  24 +-
 .../components/AccountaccessComponent.php     |   2 +-
 .../components/SocialComponent.php            |  67 +++--
 src/library/VersionFunctions.php              |  34 +++
 src/models/GroupModel.php                     | 268 +++++++++++++++---
 src/models/ProfileModel.php                   |  11 +-
 6 files changed, 332 insertions(+), 74 deletions(-)

diff --git a/src/configs/Createdb.php b/src/configs/Createdb.php
index 8b5529bf2..871c2aa33 100755
--- a/src/configs/Createdb.php
+++ b/src/configs/Createdb.php
@@ -164,7 +164,7 @@ $db->execute($sql);
 $creation_time = L\microTimestamp();
 $sql = "INSERT INTO SOCIAL_GROUPS VALUES(".PUBLIC_GROUP_ID.",'Public','" .
     $creation_time . "','".ROOT_ID."', '"  .PUBLIC_JOIN . "', '" . GROUP_READ .
-    "', " . NON_VOTING_GROUP.", " . FOREVER . ", 0)";
+    "', " . NON_VOTING_GROUP.", " . FOREVER . ", 0, 0)";
 $db->execute($sql);
 $now = time();
 $db->execute("INSERT INTO ROLE VALUES (" . ADMIN_ROLE . ", 'Admin' )");
@@ -173,44 +173,44 @@ $db->execute("INSERT INTO ROLE VALUES (".BOT_ROLE.", 'Bot User' )");
 $db->execute("INSERT INTO USER_ROLE VALUES (" . ROOT_ID . ", " . ADMIN_ROLE .
     ")");
 $db->execute("INSERT INTO USER_GROUP VALUES (" . ROOT_ID . ", ".
-    PUBLIC_GROUP_ID.", " . ACTIVE_STATUS . ", $now)");
+    PUBLIC_GROUP_ID.", " . ACTIVE_STATUS . ", $now, 0, 0)");
 $db->execute("INSERT INTO USER_GROUP VALUES (".PUBLIC_USER_ID.", ".
-    PUBLIC_GROUP_ID.", " . ACTIVE_STATUS . ", $now)");
+    PUBLIC_GROUP_ID.", " . ACTIVE_STATUS . ", $now, 0, 0)");
 //Create a Group for Wiki HELP.
 $sql = "INSERT INTO SOCIAL_GROUPS VALUES (" . HELP_GROUP_ID . ",'Help','" .
     $creation_time . "','" . ROOT_ID . "',
     '" . PUBLIC_BROWSE_REQUEST_JOIN . "', '" . GROUP_READ_WIKI .
-    "', " . UP_DOWN_VOTING_GROUP . ", " . FOREVER . ", 0)";
+    "', " . UP_DOWN_VOTING_GROUP . ", " . FOREVER . ", 0, 0)";
 $db->execute($sql);
 $now = time();
 $db->execute("INSERT INTO USER_GROUP VALUES (" . ROOT_ID . ", " .
-    HELP_GROUP_ID . ", " . ACTIVE_STATUS . ", $now)");
+    HELP_GROUP_ID . ", " . ACTIVE_STATUS . ", $now, 0, 0)");
 $db->execute("INSERT INTO USER_GROUP VALUES (" . PUBLIC_USER_ID . ", " .
-    HELP_GROUP_ID . ", " . ACTIVE_STATUS . ", $now)");
+    HELP_GROUP_ID . ", " . ACTIVE_STATUS . ", $now, 0, 0)");
 //Create a Group for Search Result Edits
 $sql = "INSERT INTO SOCIAL_GROUPS VALUES (" . SEARCH_GROUP_ID . ",'Search','" .
     $creation_time . "','" . ROOT_ID . "',
     '" . PUBLIC_BROWSE_REQUEST_JOIN . "', '" . GROUP_READ_WIKI .
-    "', " . UP_DOWN_VOTING_GROUP . ", " . FOREVER . ", 0)";
+    "', " . UP_DOWN_VOTING_GROUP . ", " . FOREVER . ", 0, 0)";
 $db->execute($sql);
 $now = time();
 $db->execute("INSERT INTO USER_GROUP VALUES (" . ROOT_ID . ", " .
-    SEARCH_GROUP_ID . ", " . ACTIVE_STATUS . ", $now)");
+    SEARCH_GROUP_ID . ", " . ACTIVE_STATUS . ", $now, 0, 0)");
 $db->execute("INSERT INTO USER_GROUP VALUES (" . PUBLIC_USER_ID . ", " .
-    SEARCH_GROUP_ID . ", " . ACTIVE_STATUS . ", $now)");
+    SEARCH_GROUP_ID . ", " . ACTIVE_STATUS . ", $now, 0, 0)");
 /* we insert 1 by 1 rather than comma separate as sqlite
    does not support comma separated inserts
  */
  //Create a group for moderator users
   $sql = "INSERT INTO SOCIAL_GROUPS VALUES(".MODERATION_GROUP_ID.",'Moderation','" .
       $creation_time . "','".ROOT_ID."', '"  .INVITE_ONLY_JOIN . "', '" . GROUP_READ .
-      "', " . NON_VOTING_GROUP.", " . FOREVER . ", 0)";
+      "', " . NON_VOTING_GROUP.", " . FOREVER . ", 0, 0)";
   $db->execute($sql);
   $now = time();
   $db->execute("INSERT INTO USER_GROUP VALUES (" . ROOT_ID . ", " .
-      MODERATION_GROUP_ID . ", " . ACTIVE_STATUS . ", $now)");
+      MODERATION_GROUP_ID . ", " . ACTIVE_STATUS . ", $now, 0, 0)");
   $db->execute("INSERT INTO USER_GROUP VALUES (" . PUBLIC_USER_ID . ", " .
-      MODERATION_GROUP_ID . ", " . ACTIVE_STATUS . ", $now)");
+      MODERATION_GROUP_ID . ", " . ACTIVE_STATUS . ", $now, 0, 0)");
 $locales = [
     ['ar', 'العربية', 'rl-tb'],
     ['bn', 'বাংলা', 'lr-tb', ''],
diff --git a/src/controllers/components/AccountaccessComponent.php b/src/controllers/components/AccountaccessComponent.php
index e4cc0055e..e1fdd8458 100644
--- a/src/controllers/components/AccountaccessComponent.php
+++ b/src/controllers/components/AccountaccessComponent.php
@@ -188,7 +188,7 @@ class AccountaccessComponent extends Component
                 $data['GROUPS'][$i]['STATUS'];
             $data['GROUPS'][$i]["MOST_RECENT_VIEW"] =
                 $most_recent_views[$group_id] ?? 0;
-            $item = $group_model->getMostRecentGroupPost($group_id);
+            $item = $group_model->getMostRecentGroupPost($group_id, $user_id);
             $data['GROUPS'][$i]['NUM_POSTS'] =
                 $group_model->getGroupPostCount($group_id);
             $data['GROUPS'][$i]['NEW_POSTS'] =
diff --git a/src/controllers/components/SocialComponent.php b/src/controllers/components/SocialComponent.php
index da4ae4f82..087f99310 100644
--- a/src/controllers/components/SocialComponent.php
+++ b/src/controllers/components/SocialComponent.php
@@ -349,12 +349,23 @@ class SocialComponent extends Component implements CrawlConstants
                                 $_REQUEST[$field] = $info[1];
                             }
                         }
-                        $group_model->addGroup($name,
+                        $isEncrypted = $parent->clean($_REQUEST['encryption'],
+                                                    "int");
+                        if($isEncrypted != 0){
+                            $group_model->addGroupEncrypted($name,
                             $_SESSION['USER_ID'], $_REQUEST['register'],
                             $_REQUEST['member_access'],
                             $_REQUEST['vote_access'],
                             $_REQUEST['post_lifetime'],
                             $_REQUEST['encryption']);
+                        }else{
+                            $group_model->addGroup($name,
+                            $_SESSION['USER_ID'], $_REQUEST['register'],
+                            $_REQUEST['member_access'],
+                            $_REQUEST['vote_access'],
+                            $_REQUEST['post_lifetime'],
+                            $_REQUEST['encryption']);
+                        }
                         //one exception to setting $group_id
                         $group_id = $group_model->getGroupId($name);
                         return $parent->redirectWithMessage(
@@ -501,15 +512,23 @@ class SocialComponent extends Component implements CrawlConstants
                             "string");
                         $pre_user_names = preg_split("/\s+|\,/", $users_string);
                         $users_invited = false;
+                        $isEncrypted = $group_model->isGroupEncrypted($group_id);
                         foreach ($pre_user_names as $user_name) {
                             $user_name = trim($user_name);
                             $user = $parent->model("user")->getUser($user_name);
                             if ($user) {
                                 if (!$group_model->checkUserGroup(
-                                    $user['USER_ID'], $group_id)) {
+                                    $user['USER_ID'], $group_id))
+                                {
+                                    if($isEncrypted){
+                                    $group_model->addUserGroupEncrypted(
+                                        $user['USER_ID'], $group_id,
+                                        C\INVITED_STATUS);
+                                    }else{
                                     $group_model->addUserGroup(
                                         $user['USER_ID'], $group_id,
                                         C\INVITED_STATUS);
+                                    }
                                     $users_invited = true;
                                 }
                             }
@@ -902,7 +921,9 @@ class SocialComponent extends Component implements CrawlConstants
                             if ($field == C\THREAD_IMPRESSION &&
                                 $group_model->isGroupEncrypted($group_id)) {
                                 // Decrypt thread's title
-                                $key = $group_model->getGroupKey($group_id);
+                                //$key = $group_model->getGroupKey($group_id);
+                                $key =
+                                $group_model->generateKey($group_id, $user_id);
                                 $decrypted_item_name = $group_model->decrypt(
                                     $item_name, $key);
                                 $data['STATISTICS'][$field][$period][
@@ -1389,7 +1410,8 @@ class SocialComponent extends Component implements CrawlConstants
                             ['page_name']);
                     }
                     if ($parent_id >= 0) {
-                        $parent_item = $group_model->getGroupItem($parent_id);
+                        $parent_item =
+                        $group_model->getGroupItem($user_id, $parent_id);
                         if (!$parent_item) {
                             return $parent->redirectWithMessage(
                                 tl('social_component_no_post_access'),
@@ -1493,7 +1515,8 @@ class SocialComponent extends Component implements CrawlConstants
                         break;
                     }
                     $post_id = $parent->clean($_REQUEST['post_id'], "int");
-                    $group_item = $group_model->getGroupItem($post_id);
+                    $group_item =
+                    $group_model->getGroupItem($user_id, $post_id);
                     $success = false;
                     if ($group_item) {
                         // this method checks if user can delete post
@@ -1531,7 +1554,8 @@ class SocialComponent extends Component implements CrawlConstants
                         break;
                     }
                     $post_id = $parent->clean($_REQUEST['post_id'], "int");
-                    $group_item = $group_model->getGroupItem($post_id);
+                    $group_item =
+                        $group_model->getGroupItem($user_id, $post_id);
                     if ($group_model->alreadyFlagged($user_id, $post_id)) {
                         return $parent->redirectWithMessage(
                             tl('social_component_already_flagged'));
@@ -1557,7 +1581,8 @@ class SocialComponent extends Component implements CrawlConstants
                           tl('social_component_no_approve_access'));
                     }
                     $post_id = $parent->clean($_REQUEST['post_id'], "int");
-                    $group_item = $group_model->getGroupItem($post_id);
+                    $group_item =
+                        $group_model->getGroupItem($user_id, $post_id);
                     $success = false;
                     if ($group_item) {
                        $success = $group_model->approveFlaggedPost($post_id);
@@ -1578,10 +1603,12 @@ class SocialComponent extends Component implements CrawlConstants
                           tl('social_component_no_delete_access'));
                     }
                     $post_id = $parent->clean($_REQUEST['post_id'], "int");
-                    $group_item = $group_model->getGroupItem($post_id);
+                    $group_item =
+                        $group_model->getGroupItem($user_id, $post_id);
                     $success = false;
                     if ($group_item) {
-                        $success = $group_model->deleteFlaggedPost($post_id);
+                        $success =
+                        $group_model->deleteFlaggedPost($post_id, $user_id);
                     }
                     if ($success) {
                         return $parent->redirectWithMessage(
@@ -1610,7 +1637,8 @@ class SocialComponent extends Component implements CrawlConstants
                         return $parent->redirectWithMessage(
                             tl('social_component_no_vote_access'));
                     }
-                    $post_item = $group_model->getGroupItem($post_id);
+                    $post_item =
+                        $group_model->getGroupItem($user_id, $post_id);
                     if (!$post_item || $post_item['GROUP_ID'] != $group_id) {
                         return $parent->redirectWithMessage(
                             tl('social_component_no_post_access'));
@@ -1763,7 +1791,7 @@ class SocialComponent extends Component implements CrawlConstants
                         break;
                     }
                     $group_model->updateGroupItem($post_id, $title,
-                        $description);
+                        $description, $user_id);
                     $result = $this->handleResourceUploads(
                         $group_id, "post" . $post_id);
                     if ($result == self::UPLOAD_FAILED) {
@@ -1792,7 +1820,8 @@ class SocialComponent extends Component implements CrawlConstants
                         return $parent->redirectWithMessage(
                             tl('social_component_no_vote_access'));
                     }
-                    $post_item = $group_model->getGroupItem($post_id);
+                    $post_item =
+                        $group_model->getGroupItem($user_id, $post_id);
                     if (!$post_item || $post_item['GROUP_ID'] != $group_id) {
                         return $parent->redirectWithMessage(
                             tl('social_component_no_post_access'));
@@ -1866,7 +1895,8 @@ class SocialComponent extends Component implements CrawlConstants
         $pub_clause = ['pub_date', "=", "", "DESC"];
         $sort = "krsort";
         if ($just_thread) {
-            $thread_parent = $group_model->getGroupItem($just_thread);
+            $thread_parent =
+                $group_model->getGroupItem($user_id, $just_thread);
             $group_id = $thread_parent['GROUP_ID'] ?? false;
             if (isset($thread_parent["TYPE"]) &&
                 $thread_parent["TYPE"] == C\WIKI_GROUP_ITEM) {
@@ -1916,7 +1946,8 @@ class SocialComponent extends Component implements CrawlConstants
         $type = "";
         $type_id = "";
         if (!empty($just_thread) ) {
-            $thread_start_item = $group_model->getGroupItem($just_thread);
+            $thread_start_item =
+                   $group_model->getGroupItem($user_id, $just_thread);
             if (empty($thread_start_item) ||
                 empty($thread_start_item["TITLE"])) {
                 $data['NO_POSTS_IN_THREAD'] = true;
@@ -2858,8 +2889,8 @@ class SocialComponent extends Component implements CrawlConstants
         if (!empty($thread_ids)) {
             $data['RECENT_THREADS'] = [];
             foreach ($thread_ids as $recent_thread_id) {
-                $thread_start_item = $group_model->getGroupItem(
-                    $recent_thread_id);
+                $thread_start_item =
+                    $group_model->getGroupItem($user_id, $recent_thread_id);
                 $thread_name = $thread_start_item['TITLE'] ?? "";
                 if (!empty($thread_name) &&
                     (empty($data['JUST_THREAD']) ||
@@ -3037,7 +3068,7 @@ class SocialComponent extends Component implements CrawlConstants
         for ($i = 0; $i < $num_shown; $i++) {
             $group = $data['GROUPS'][$i];
             $group_id = $group['GROUP_ID'];
-            $item = $group_model->getMostRecentGroupPost($group_id);
+            $item = $group_model->getMostRecentGroupPost($group_id, $user_id);
             $most_recent_views = $impression_model->mostRecentGroupViews(
                 $user_id, [$group_id]);
             $group["MOST_RECENT_VIEW"] =
@@ -3954,7 +3985,7 @@ EOD;
         } else if ($data["HEAD"]['page_type'] == 'page_and_feedback') {
             $just_thread = $data['DISCUSS_THREAD'];
             $thread_parent =
-                $group_model->getGroupItem($just_thread);
+                $group_model->getGroupItem($user_id, $just_thread);
             $edit_or_source = ($data["CAN_EDIT"]) ? "edit" : "source";
             $search_array = [
                 ["parent_id", "=", $just_thread, ""],
diff --git a/src/library/VersionFunctions.php b/src/library/VersionFunctions.php
index 49371d82a..8093b4384 100644
--- a/src/library/VersionFunctions.php
+++ b/src/library/VersionFunctions.php
@@ -2116,3 +2116,37 @@ function upgradeDatabaseVersion78(&$db)
         AUX_HASH VARCHAR(". C\TIMESTAMP_LEN . ") DEFAULT ''");
     $db->execute("CREATE INDEX QI_AUX_HASH_INDEX ON QUERY_ITEM (AUX_HASH)");
 }
+
+function upgradeDatabaseVersion79(&$db)
+{
+    $dbinfo = ["DBMS" => C\DBMS, "DB_HOST" => C\DB_HOST,
+        "DB_NAME" => C\DB_NAME, "DB_PASSWORD" => C\DB_PASSWORD];
+
+    $db->execute("ALTER TABLE SOCIAL_GROUPS ADD COLUMN
+    RANDOM_NUM VARCHAR(". C\LONG_NAME_LEN . ") DEFAULT ''");
+
+    $db->execute("ALTER TABLE USER_GROUP ADD COLUMN
+    HASH VARCHAR(". C\LONG_NAME_LEN . ") DEFAULT ''",
+    SHARE VARCHAR(". C\LONG_NAME_LEN . ") DEFAULT ''");
+
+    $db->execute("ALTER TABLE GROUP_ITEM ADD COLUMN
+    FLAG $integer DEFAULT 0,
+    PARENT_ITEM_ID $integer DEFAULT 0);
+
+    $db->execute("CREATE TABLE GROUP_ITEM_FLAG" .
+    "(USER_ID $integer NOT NULL, ITEM_ID $integer NOT NULL)");
+
+    $private_db_class = C\NS_DATASOURCES . ucfirst(C\PRIVATE_DBMS)."Manager";
+    $private_dbinfo = ["DBMS" => C\PRIVATE_DBMS, "DB_HOST" => C\PRIVATE_DB_HOST,
+        "DB_NAME" => C\PRIVATE_DB_NAME, "DB_PASSWORD" => C\PRIVATE_DB_PASSWORD];
+    $private_db = new $private_db_class();
+    $private_db->connect(C\PRIVATE_DB_HOST, C\PRIVATE_DB_USER,
+    C\PRIVATE_DB_PASSWORD, C\PRIVATE_DB_NAME);
+
+    $private_db->execute("CREATE TABLE GROUP_KEYS" .
+    "(GROUP_ID $integer PRIMARY KEY,
+    RANDOM_NUM_X VARCHAR(". C\LONG_NAME_LEN . ") DEFAULT ''"
+    RANDOM_NUM_Y VARCHAR(". C\LONG_NAME_LEN . ") DEFAULT ''")");
+
+    $private_db->execute("DROP TABLE TYPE_KEYS");
+}
\ No newline at end of file
diff --git a/src/models/GroupModel.php b/src/models/GroupModel.php
index b7bac7974..d09b1c435 100644
--- a/src/models/GroupModel.php
+++ b/src/models/GroupModel.php
@@ -294,14 +294,177 @@ class GroupModel extends Model implements MediaConstants
             $db);
         ImpressionModel::initWithDb(C\PUBLIC_GROUP_ID, $last_id,
             C\GROUP_IMPRESSION, $db);
-        if ($encryption != 0) {
-            $sql = "INSERT INTO TYPE_KEYS (TYPE_ID, KEY_NAME) VALUES (?, ?)";
-            //AES 256 is 32 bytes long (8*32 bits)
-            $encrypt_key = base64_encode(openssl_random_pseudo_bytes(32));
-            $private_db->execute($sql, [$last_id, $encrypt_key]);
-        }
         return $last_id;
     }
+    /**
+     * Add a groupname to the database using provided string
+     *
+     * @param string $group_name  the groupname to be added
+     * @param int $user_id user identifier of who owns the group
+     * @param int $register flag that says what kinds of registration are
+     *      allowed for this group INVITE_ONLY_JOIN, REQUEST_JOIN, PUBLIC_JOIN,
+     *      or some group fee amount in credits 100, 200, 500, 1000, 2000
+     * @param int $member flag that says how members other than the owner can
+     *      access this group GROUP_READ, GROUP_READ_COMMENT (can comment
+     *      on threads but not start. i.e., a blog), GROUP_READ_WRITE,
+     *      (can read, comment, start threads), GROUP_READ_WIKI, (can read,
+     *      comment, start threads, and edit the wiki)
+     * @param int $voting flag that says how members can vote on each others
+     *      posts: NON_VOTING_GROUP, UP_VOTING_GROUP, UP_DOWN_VOTING_GROUP
+     * @param int $post_lifetime specifies the time in seconds that posts should
+     *      live before they expire and are deleted
+     * @param int $encryption 0 means don't encrypt group, 1 means encrypt
+     *      group
+     * @return int id of group added
+     */
+    public function addGroupEncrypted($group_name, $user_id,
+        $register = C\REQUEST_JOIN, $member = C\GROUP_READ,
+        $voting = C\NON_VOTING_GROUP, $post_lifetime = C\FOREVER,
+        $encryption = 0)
+    {
+        $db = $this->db;
+        $private_db = $this->private_db;
+        $timestamp = L\microTimestamp();
+
+        $randomNum1 = $this->generateRandomNumbers();
+        $randomNum2 = $this->generateRandomNumbers();
+        $randomNum3 = $this->generateRandomNumbers();
+        $randomNum4 = $this->generateRandomNumbers();
+
+        $sql = "INSERT INTO SOCIAL_GROUPS (GROUP_NAME, CREATED_TIME, OWNER_ID,
+                REGISTER_TYPE, MEMBER_ACCESS, VOTE_ACCESS, POST_LIFETIME,
+                ENCRYPTION, RANDOM_NUM) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
+        $db->execute($sql, [$group_name, $timestamp, $user_id,
+                $register, $member, $voting, $post_lifetime, $encryption,
+                $randomNum1]);
+        $sql = "SELECT G.GROUP_ID AS GROUP_ID FROM ".
+                " SOCIAL_GROUPS G WHERE G.GROUP_NAME = ?";
+        $result = $db->execute($sql, [$group_name]);
+        if (!$row = $db->fetchArray($result)) {
+                $last_id = -1;
+        }
+        $last_id = $row['GROUP_ID'];
+
+        $hash = hash('sha256', $user_id.$last_id);
+        $now = time();
+        $status =  C\ACTIVE_STATUS;
+        $sql = "INSERT INTO USER_GROUP (USER_ID, GROUP_ID, STATUS,
+                JOIN_DATE, USER_HASH, SHARE) VALUES (?, ? , ?, ?, ?, ?)";
+        $db->execute($sql, [$user_id, $last_id, $status, $now,
+        $hash, $randomNum2]);
+
+        $sql = "INSERT INTO GROUP_KEYS (GROUP_ID, RANDOM_NUM_X, RANDOM_NUM_Y)
+        VALUES (?, ?, ?)";
+        $private_db->execute($sql, [$last_id, $randomNum3, $randomNum4]);
+
+        ImpressionModel::initWithDb($user_id, $last_id, C\GROUP_IMPRESSION,
+                    $db);
+        ImpressionModel::initWithDb(C\PUBLIC_GROUP_ID, $last_id,
+                    C\GROUP_IMPRESSION, $db);
+
+       return $last_id;
+    }
+    /**
+     * Generate random numbers for key generation
+     */
+    public function generateRandomNumbers()
+    {
+        $randomBytes = random_bytes(32);
+        $hex = bin2hex($randomBytes);
+        return $hex;
+    }
+    /**
+     * Fetch the user share for user in the group
+     * @param int $group_id to get key for the group
+     * @param int $user_id to get ID of the user
+    */
+    public function getUserShare($group_id, $user_id)
+    {
+        $db = $this->db;
+        $sql = "SELECT SHARE FROM USER_GROUP WHERE GROUP_ID = ? AND USER_ID = ?";
+        $stmt = $db->execute($sql, [$group_id, $user_id]);
+        if (!$stmt) {
+            return false;
+        }
+        if (!($row = $db->fetchArray($stmt))) {
+            return false;
+        }
+        return $row['SHARE'];
+    }
+    /**
+     * Generate share for user in the group
+     * @param array $point1 to get a point on the line for key generation
+     * @param array $point2 to get second point on the line
+     * @param $userX to denote X coordinate, for which share is the Y
+     *
+    */
+    public function generateShare($point1, $point2, $userX)
+    {
+        $point1_x = gmp_strval(gmp_init($point1[0], 32));
+        $point1_y = gmp_strval(gmp_init($point1[1], 32));
+
+        $point2_x = gmp_init($point2[0], 32);
+        $point2_y = gmp_strval(gmp_init($point2[1], 32));
+
+        $x = gmp_init($userX, 32);
+
+        $sub1 = bcsub($point2_y, $point1_y);
+        $sub2 = bcsub($point2_x, $point1_x);
+
+        $m = bcdiv($sub1, $sub2);
+        $c = bcsub($point1_y, bcmul($m, $point1_x));
+
+        $y = bcadd(bcmul($m, $x), $c);
+        return $y;
+    }
+    /**
+     * Generate a key for a user of a private/encrypted group
+     *
+     * @param int $group_id to get key for the group
+     * @return int $user_id of the user in the group
+     */
+    public function generateKey($group_id, $user_id)
+    {
+        $db = $this->private_db;
+        $sql = "SELECT RANDOM_NUM_X, RANDOM_NUM_Y FROM GROUP_KEYS
+        WHERE GROUP_ID = ?";
+        $stmt = $db->execute($sql, [$group_id]);
+        $row = $db->fetchArray($stmt);
+        $x1 = gmp_init($row['RANDOM_NUM_X'], 32);
+        $y1 = gmp_init($row['RANDOM_NUM_Y'], 32);
+
+        $db = $this->db;
+        $sql = "SELECT USER_HASH FROM USER_GROUP
+        WHERE GROUP_ID = ? AND USER_ID = ?";
+        $stmt = $db->execute($sql, [$group_id, $user_id]);
+        $row = $db->fetchArray($stmt);
+        $storedHash = $row['USER_HASH'];
+        $calculatedHash = hash('sha256', $user_id.$group_id);
+
+        $sql = "SELECT OWNER_ID, RANDOM_NUM FROM SOCIAL_GROUPS
+        WHERE GROUP_ID = ?";
+        $stmt = $db->execute($sql, [$group_id]);
+        $row = $db->fetchArray($stmt);
+        $x = gmp_init($row['RANDOM_NUM'], 32);
+        $owner_id = $row['OWNER_ID'];
+
+        if(hash_equals($storedHash, $calculatedHash)){
+            $x2 = gmp_init($storedHash, 32);
+            if($user_id == $owner_id){
+               $owner_share = $this->getUserShare($group_id, $user_id);
+               $y2 = gmp_init($owner_share, 32);
+            }else{
+               $y2 = $this->getUserShare($group_id, $user_id);
+            }
+            $m = bcdiv(bcsub($y2, $y1), bcsub($x2, $x1));
+            $y = bcadd($y1, bcmul($m, bcsub($x, $x1)));
+
+            $y_hex = gmp_strval($y, 16);
+            $padded_y_hex = str_pad($y_hex, 64, '0', STR_PAD_LEFT);
+            $y_bin = hex2bin($padded_y_hex);
+            return $y_bin;
+        }
+    }
     /**
      * Takes the passed associated array $group representing changes
      * fields of a SOCIAL_GROUPS row, and executes an UPDATE statement to
@@ -741,23 +904,6 @@ class GroupModel extends Model implements MediaConstants
         $subtract_personal = ($include_personal) ? 0 : -1;
         return ($row['NUM'] ?? 0)  + $subtract_personal;
     }
-    /**
-     * Get the key of a private/encrypted group.
-     *
-     * @param int $group_id to get key for
-     * @return string key of the group
-     */
-    public function getGroupKey($group_id)
-    {
-        $private_db = $this->private_db;
-        $sql = "SELECT KEY_NAME FROM TYPE_KEYS WHERE TYPE_ID=?";
-        $result = $private_db->execute($sql, [$group_id]);
-        if ($result) {
-            $row = $private_db->fetchArray($result);
-        }
-        return empty($row['KEY_NAME']) ? false :
-            base64_decode($row['KEY_NAME']);
-    }
     /**
      * To update the OWNER_ID of a group
      *
@@ -783,9 +929,49 @@ class GroupModel extends Model implements MediaConstants
     {
         $join_date = time();
         $db = $this->db;
-        $sql = "INSERT INTO USER_GROUP VALUES (?, ?, ?, ?)";
+        $sql = "INSERT INTO USER_GROUP(USER_ID, GROUP_ID, STATUS, JOIN_DATE)
+        VALUES (?, ?, ?, ?)";
         $db->execute($sql, [$user_id, $group_id, $status, $join_date]);
     }
+
+    public function addUserGroupEncrypted($user_id, $group_id,
+    $status = C\ACTIVE_STATUS)
+    {
+        $join_date = time();
+
+        $db = $this->private_db;
+        $sql = "SELECT RANDOM_NUM_X, RANDOM_NUM_Y FROM GROUP_KEYS WHERE
+        GROUP_ID = ?";
+        $stmt = $db->execute($sql, [$group_id]);
+        $row = $db->fetchArray($stmt);
+        $point1 =  [$row['RANDOM_NUM_X'], $row['RANDOM_NUM_Y']];
+
+        $db = $this->db;
+        $sql = "SELECT OWNER_ID FROM SOCIAL_GROUPS WHERE GROUP_ID=?";
+        $result = $db->execute($sql, [$group_id]);
+        if ($result) {
+           $row = $db->fetchArray($result);
+        }
+        $owner_id = $row['OWNER_ID'];
+
+        $sql = "SELECT USER_HASH, SHARE FROM USER_GROUP WHERE USER_ID=?
+        AND GROUP_ID=?";
+        $result = $db->execute($sql, [$owner_id, $group_id]);
+        if ($result) {
+           $row = $db->fetchArray($result);
+        }
+        $hash = $row['USER_HASH'];
+        $share = $row['SHARE'];
+        $point2 = [$hash, $share];
+
+        $userHash = hash('sha256', $user_id.$group_id);
+        $share = $this->generateShare($point1, $point2, $userHash);
+
+        $sql = "INSERT INTO USER_GROUP(USER_ID, GROUP_ID, STATUS, JOIN_DATE,
+        USER_HASH, SHARE) VALUES (?, ?, ?, ?, ?, ?)";
+        $db->execute($sql, [$user_id, $group_id, $status, $join_date, $userHash,
+        $share]);
+    }
     /**
      * Checks if a user belongs to a group but is not the owner of that group
      * Such a user could be deleted from the group
@@ -827,7 +1013,7 @@ class GroupModel extends Model implements MediaConstants
      * @param bool whether the item_id is a group_id or not
      * @return array row from GROUP_FEED table
      */
-    public function getGroupItem($item_id, $is_group_id = false)
+    public function getGroupItem($user_id, $item_id, $is_group_id = false)
     {
         $db = $this->db;
         if ($is_group_id) {
@@ -844,7 +1030,7 @@ class GroupModel extends Model implements MediaConstants
         if (!empty($row['GROUP_ID']) &&
             $this->isGroupEncrypted($row['GROUP_ID'])) {
             // Decrypt group's title and description
-            $key = $this->getGroupKey($row['GROUP_ID']);
+            $key = $this->generateKey($row['GROUP_ID'], $user_id);
             $row['TITLE'] = $this->decrypt($row['TITLE'], $key);
             $row['DESCRIPTION'] = $this->decrypt($row['DESCRIPTION'], $key);
         }
@@ -933,7 +1119,7 @@ class GroupModel extends Model implements MediaConstants
             $post_time = time();
         }
         if ($this->isGroupEncrypted($group_id)) {
-            $key = $this->getGroupKey($group_id);
+            $key = $this->generateKey($group_id, $user_id);
             $title = $this->encrypt($title, $key);
             $description = $this->encrypt($description, $key);
         }
@@ -977,7 +1163,7 @@ class GroupModel extends Model implements MediaConstants
             $post_time = time();
         }
         if($this->isGroupEncrypted($parent_group_id)){
-            $key = $this->getGroupKey($parent_group_id);
+            $key = $this->generateKey($parent_group_id, $user_id);
             $title = $this->decrypt($title, $key);
             $description = $this->decrypt($description, $key);
         }
@@ -1082,13 +1268,13 @@ class GroupModel extends Model implements MediaConstants
      * @param string $title the new title
      * @param string $description the new description
      */
-    public function updateGroupItem($id, $title, $description)
+    public function updateGroupItem($id, $title, $description, $user_id)
     {
         $db = $this->db;
         $edit_date = time();
-        $group_item = $this->getGroupItem($id);
+        $group_item = $this->getGroupItem($user_id, $id);
         if ($this->isGroupEncrypted($group_item['GROUP_ID'])) {
-            $key = $this->getGroupKey($group_item['GROUP_ID']);
+            $key = $this->generateKey($group_item['GROUP_ID'], $user_id);
             $title = $this->encrypt($title, $key);
             $description = $this->encrypt($description, $key);
         }
@@ -1143,14 +1329,14 @@ class GroupModel extends Model implements MediaConstants
         $flag_count = $this->getFlagCount($post_id);
         if ($this->isGroupEncrypted($group_id))
         {
-            $threshold = $this->getThresholdValue($group_id);
-            $key = $this->getGroupKey($group_id);
+            $threshold = $this->getThresholdValue($group_id, $user_id);
+            $key = $this->generateKey($group_id, $user_id);
             if($flag_count == 0){
                 $flag_count = $this->encryptFlag(strval($flag_count), $key);
             }
             if ($flag_count === $threshold) {
                 $flag_count_decrypt = $this->decryptFlag(
-                base64_decode($flag_count), $key);
+                $flag_count, $key);
                 $this->addGroupItemModeration(0, 5, $result['USER_ID'],
                        $result['TITLE'],$result['DESCRIPTION'],
                        $flag_count_decrypt, $post_id, $group_id,
@@ -1243,9 +1429,9 @@ class GroupModel extends Model implements MediaConstants
      *
      * @param int $group_id to get threshold value for the given group
      */
-    public function getThresholdValue($group_id)
+    public function getThresholdValue($group_id, $user_id)
     {
-       $key = $this->getGroupKey($group_id);
+       $key = $this->generateKey($group_id, $user_id);
        $x = 3;
        $res = 0;
        while($x > 0){
@@ -1316,12 +1502,12 @@ class GroupModel extends Model implements MediaConstants
      *
      * @param int $post_id to fetch the flagged post
      */
-    public function deleteFlaggedPost($post_id)
+    public function deleteFlaggedPost($post_id, $user_id)
     {
          $description = "This post has been flagged";
          $parent_group_id = $this->getParentGroupId($post_id);
          if($this->isGroupEncrypted($parent_group_id)){
-            $key = $this->getGroupKey($parent_group_id);
+            $key = $this->generateKey($parent_group_id, $user_id);
             $description = $this->encrypt($description, $key);
          }
          $item_id = $this->getParentPostById($post_id);
@@ -1468,7 +1654,7 @@ class GroupModel extends Model implements MediaConstants
         $i = 0;
         foreach ($groups as $group_key => $group_value) {
             if ($this->isGroupEncrypted($group_value['GROUP_ID'])) {
-                $keys = $this->getGroupKey($group_value['GROUP_ID']);
+                $keys = $this->generateKey($group_value['GROUP_ID'], $user_id);
                 $groups[$i]['TITLE'] = $this->decrypt(
                     $group_value['TITLE'], $keys);
                 $groups[$i]['DESCRIPTION'] = $this->decrypt(
@@ -1550,7 +1736,7 @@ class GroupModel extends Model implements MediaConstants
      * @param int $group_id id of the group to get the most recent post for
      * @return array associate array of post details
      */
-    public function getMostRecentGroupPost($group_id)
+    public function getMostRecentGroupPost($group_id, $user_id)
     {
         $db = $this->db;
         $sql = "SELECT MAX(GI.PUBDATE) AS PUBDATE
@@ -1583,7 +1769,7 @@ class GroupModel extends Model implements MediaConstants
         if (!empty($group_id) &&
             $this->isGroupEncrypted($group_id)) {
             // Decrypt group's title and description
-            $key = $this->getGroupKey($group_id);
+            $key = $this->generateKey($group_id, $user_id);
             $row['TITLE'] = $this->decrypt($row['TITLE'], $key);
             $row['DESCRIPTION'] = $this->decrypt($row['DESCRIPTION'], $key);
         }
diff --git a/src/models/ProfileModel.php b/src/models/ProfileModel.php
index c3c91579c..70426375c 100755
--- a/src/models/ProfileModel.php
+++ b/src/models/ProfileModel.php
@@ -261,7 +261,8 @@ class ProfileModel extends Model
                 OWNER_ID $integer, REGISTER_TYPE $integer,
                 MEMBER_ACCESS $integer, VOTE_ACCESS $integer DEFAULT ".
                 C\NON_VOTING_GROUP . ", POST_LIFETIME $integer DEFAULT ".
-                C\FOREVER . ", ENCRYPTION $integer DEFAULT 0)",
+                C\FOREVER . ", ENCRYPTION $integer DEFAULT 0,
+                RANDOM_NUM VARCHAR(" . C\LONG_NAME_LEN . "))",
             /* NOTE: We are not using singular name GROUP for SOCIAL_GROUPS as
                GROUP is a reserved SQL keyword and the table used to be
                called GROUPS until Mysql reserved that word too
@@ -394,6 +395,8 @@ class ProfileModel extends Model
             "USER_GROUP" => "CREATE TABLE USER_GROUP (USER_ID $integer,
                 GROUP_ID $integer, STATUS $integer,
                 JOIN_DATE NUMERIC(" . C\TIMESTAMP_LEN . "),
+                USER_HASH VARCHAR(" . C\LONG_NAME_LEN . "),
+                SHARE VARCHAR(" . C\LONG_NAME_LEN . "),
                 PRIMARY KEY (GROUP_ID, USER_ID) )",
             "USER_ROLE" => "CREATE TABLE USER_ROLE (USER_ID $integer,
                 ROLE_ID $integer, PRIMARY KEY (ROLE_ID, USER_ID))",
@@ -434,7 +437,11 @@ class ProfileModel extends Model
         $this->private_create_statements = ["TYPE_KEYS" =>
             "CREATE TABLE TYPE_KEYS (KEY_ID $serial PRIMARY KEY " .
             "$auto_increment, TYPE_ID $integer, KEY_NAME VARCHAR(" .
-            C\LONG_NAME_LEN . "))"];
+            C\LONG_NAME_LEN . "))",
+            "GROUP_KEYS" =>
+            "CREATE TABLE GROUP_KEYS (GROUP_ID $integer PRIMARY KEY, " .
+            "RANDOM_NUM_X VARCHAR(" . C\LONG_NAME_LEN . "), " .
+            "RANDOM_NUM_Y VARCHAR(" . C\LONG_NAME_LEN . "))"];
     }
     /**
      * Creates a folder to be used to maintain local information about this
-- 
2.37.1 (Apple Git-137.1)


From 4216cef2cbea71bc6fb5a24216701ec2e0103f78 Mon Sep 17 00:00:00 2001
From: Prajna Puranik <prajnagp@gmail.com>
Date: Wed, 24 May 2023 17:26:17 -0700
Subject: [PATCH 6/9] Fixed UpgradeFunctionsv79

---
 src/library/VersionFunctions.php | 43 +++++++++++++++-----------------
 1 file changed, 20 insertions(+), 23 deletions(-)

diff --git a/src/library/VersionFunctions.php b/src/library/VersionFunctions.php
index 8093b4384..bd8b20944 100644
--- a/src/library/VersionFunctions.php
+++ b/src/library/VersionFunctions.php
@@ -2116,37 +2116,34 @@ function upgradeDatabaseVersion78(&$db)
         AUX_HASH VARCHAR(". C\TIMESTAMP_LEN . ") DEFAULT ''");
     $db->execute("CREATE INDEX QI_AUX_HASH_INDEX ON QUERY_ITEM (AUX_HASH)");
 }
-
 function upgradeDatabaseVersion79(&$db)
 {
-    $dbinfo = ["DBMS" => C\DBMS, "DB_HOST" => C\DB_HOST,
-        "DB_NAME" => C\DB_NAME, "DB_PASSWORD" => C\DB_PASSWORD];
+    $dbinfo = [
+        "DBMS" => C\DBMS,
+        "DB_HOST" => C\DB_HOST,
+        "DB_NAME" => C\DB_NAME,
+        "DB_PASSWORD" => C\DB_PASSWORD
+    ];
 
-    $db->execute("ALTER TABLE SOCIAL_GROUPS ADD COLUMN
-    RANDOM_NUM VARCHAR(". C\LONG_NAME_LEN . ") DEFAULT ''");
+    $db->execute("ALTER TABLE SOCIAL_GROUPS ADD COLUMN RANDOM_NUM VARCHAR(" . C\LONG_NAME_LEN . ") DEFAULT ''");
 
-    $db->execute("ALTER TABLE USER_GROUP ADD COLUMN
-    HASH VARCHAR(". C\LONG_NAME_LEN . ") DEFAULT ''",
-    SHARE VARCHAR(". C\LONG_NAME_LEN . ") DEFAULT ''");
+    $db->execute("ALTER TABLE USER_GROUP ADD COLUMN HASH VARCHAR(" . C\LONG_NAME_LEN . ") DEFAULT '', SHARE VARCHAR(" . C\LONG_NAME_LEN . ") DEFAULT ''");
 
-    $db->execute("ALTER TABLE GROUP_ITEM ADD COLUMN
-    FLAG $integer DEFAULT 0,
-    PARENT_ITEM_ID $integer DEFAULT 0);
+    $db->execute("ALTER TABLE GROUP_ITEM ADD COLUMN FLAG $integer DEFAULT 0, PARENT_ITEM_ID $integer DEFAULT 0");
 
-    $db->execute("CREATE TABLE GROUP_ITEM_FLAG" .
-    "(USER_ID $integer NOT NULL, ITEM_ID $integer NOT NULL)");
+    $db->execute("CREATE TABLE GROUP_ITEM_FLAG (USER_ID $integer NOT NULL, ITEM_ID $integer NOT NULL)");
 
-    $private_db_class = C\NS_DATASOURCES . ucfirst(C\PRIVATE_DBMS)."Manager";
-    $private_dbinfo = ["DBMS" => C\PRIVATE_DBMS, "DB_HOST" => C\PRIVATE_DB_HOST,
-        "DB_NAME" => C\PRIVATE_DB_NAME, "DB_PASSWORD" => C\PRIVATE_DB_PASSWORD];
+    $private_db_class = C\NS_DATASOURCES . ucfirst(C\PRIVATE_DBMS) . "Manager";
+    $private_dbinfo = [
+        "DBMS" => C\PRIVATE_DBMS,
+        "DB_HOST" => C\PRIVATE_DB_HOST,
+        "DB_NAME" => C\PRIVATE_DB_NAME,
+        "DB_PASSWORD" => C\PRIVATE_DB_PASSWORD
+    ];
     $private_db = new $private_db_class();
-    $private_db->connect(C\PRIVATE_DB_HOST, C\PRIVATE_DB_USER,
-    C\PRIVATE_DB_PASSWORD, C\PRIVATE_DB_NAME);
+    $private_db->connect(C\PRIVATE_DB_HOST, C\PRIVATE_DB_USER, C\PRIVATE_DB_PASSWORD, C\PRIVATE_DB_NAME);
 
-    $private_db->execute("CREATE TABLE GROUP_KEYS" .
-    "(GROUP_ID $integer PRIMARY KEY,
-    RANDOM_NUM_X VARCHAR(". C\LONG_NAME_LEN . ") DEFAULT ''"
-    RANDOM_NUM_Y VARCHAR(". C\LONG_NAME_LEN . ") DEFAULT ''")");
+    $private_db->execute("CREATE TABLE GROUP_KEYS (GROUP_ID $integer PRIMARY KEY, RANDOM_NUM_X VARCHAR(" . C\LONG_NAME_LEN . ") DEFAULT '', RANDOM_NUM_Y VARCHAR(" . C\LONG_NAME_LEN . ") DEFAULT '')");
 
     $private_db->execute("DROP TABLE TYPE_KEYS");
-}
\ No newline at end of file
+}
-- 
2.37.1 (Apple Git-137.1)


From 1ee510e515d22c85555dad8ed0693a493a2bedb3 Mon Sep 17 00:00:00 2001
From: Prajna Puranik <prajnagp@gmail.com>
Date: Wed, 24 May 2023 17:40:47 -0700
Subject: [PATCH 7/9] UpgradeFunctionWithDifferentVersions

---
 src/configs/Config.php | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/configs/Config.php b/src/configs/Config.php
index 0246ab1eb..68a8f1ef6 100755
--- a/src/configs/Config.php
+++ b/src/configs/Config.php
@@ -162,7 +162,7 @@ nsconddefine('GENERATOR_STRING', "Yioop");
  * Version number for upgrade database function
  * @var int
  */
-nsdefine('DATABASE_VERSION', 79);
+nsdefine('DATABASE_VERSION', 78);
 /**
  * Minimum Version fo Yioop for which keyword ad script
  * still works with this version
-- 
2.37.1 (Apple Git-137.1)


From d47d9d35202f3c397d76e6e8e488e8fd68a18ce9 Mon Sep 17 00:00:00 2001
From: Prajna Puranik <prajnagp@gmail.com>
Date: Wed, 24 May 2023 19:18:25 -0700
Subject: [PATCH 8/9] Upgrade VersionFunction v80

---
 src/configs/Config.php           |  2 +-
 src/library/VersionFunctions.php | 50 +++++++++++++++++++++++---------
 2 files changed, 37 insertions(+), 15 deletions(-)

diff --git a/src/configs/Config.php b/src/configs/Config.php
index 68a8f1ef6..36bbc013f 100755
--- a/src/configs/Config.php
+++ b/src/configs/Config.php
@@ -162,7 +162,7 @@ nsconddefine('GENERATOR_STRING', "Yioop");
  * Version number for upgrade database function
  * @var int
  */
-nsdefine('DATABASE_VERSION', 78);
+nsdefine('DATABASE_VERSION', 80);
 /**
  * Minimum Version fo Yioop for which keyword ad script
  * still works with this version
diff --git a/src/library/VersionFunctions.php b/src/library/VersionFunctions.php
index bd8b20944..b54e61eec 100644
--- a/src/library/VersionFunctions.php
+++ b/src/library/VersionFunctions.php
@@ -2116,22 +2116,41 @@ function upgradeDatabaseVersion78(&$db)
         AUX_HASH VARCHAR(". C\TIMESTAMP_LEN . ") DEFAULT ''");
     $db->execute("CREATE INDEX QI_AUX_HASH_INDEX ON QUERY_ITEM (AUX_HASH)");
 }
-function upgradeDatabaseVersion79(&$db)
+/**
+ * Upgrades Yioop database to a Version 80
+ * @param object $db datasource to use to upgrade
+ */
+function upgradeDatabaseVersion80(&$db)
 {
-    $dbinfo = [
-        "DBMS" => C\DBMS,
-        "DB_HOST" => C\DB_HOST,
-        "DB_NAME" => C\DB_NAME,
-        "DB_PASSWORD" => C\DB_PASSWORD
-    ];
+    $dbinfo = ["DBMS" => C\DBMS, "DB_HOST" => C\DB_HOST,
+    "DB_NAME" => C\DB_NAME, "DB_PASSWORD" => C\DB_PASSWORD];
+
+    $integer = $db->integerType($dbinfo);
+
+    $db->execute("ALTER TABLE GROUP_ITEM ADD COLUMN FLAG $integer");
 
-    $db->execute("ALTER TABLE SOCIAL_GROUPS ADD COLUMN RANDOM_NUM VARCHAR(" . C\LONG_NAME_LEN . ") DEFAULT ''");
+    $db->execute("ALTER TABLE GROUP_ITEM ADD COLUMN PARENT_ITEM_ID $integer");
 
-    $db->execute("ALTER TABLE USER_GROUP ADD COLUMN HASH VARCHAR(" . C\LONG_NAME_LEN . ") DEFAULT '', SHARE VARCHAR(" . C\LONG_NAME_LEN . ") DEFAULT ''");
+    $db->execute("ALTER TABLE SOCIAL_GROUPS
+    ADD COLUMN RANDOM_NUM VARCHAR(" . C\LONG_NAME_LEN . ") DEFAULT ''");
 
-    $db->execute("ALTER TABLE GROUP_ITEM ADD COLUMN FLAG $integer DEFAULT 0, PARENT_ITEM_ID $integer DEFAULT 0");
+    $db->execute("ALTER TABLE USER_GROUP
+        ADD COLUMN USER_HASH VARCHAR(" . C\LONG_NAME_LEN . ") DEFAULT ''");
 
-    $db->execute("CREATE TABLE GROUP_ITEM_FLAG (USER_ID $integer NOT NULL, ITEM_ID $integer NOT NULL)");
+    $db->execute("ALTER TABLE USER_GROUP
+        ADD COLUMN SHARE VARCHAR(" . C\LONG_NAME_LEN . ") DEFAULT ''");
+
+    $db->execute("CREATE TABLE
+    GROUP_ITEM_FLAG (USER_ID $integer NOT NULL, ITEM_ID $integer NOT NULL)");
+
+    $creation_time = microTimestamp();
+    $db->execute("INSERT INTO SOCIAL_GROUPS VALUES(". C\MODERATION_GROUP_ID. ",'Moderation','" .
+    $creation_time . "','".C\ROOT_ID."', '"  . C\INVITE_ONLY_JOIN . "',
+    '" . C\GROUP_READ . "', " . C\NON_VOTING_GROUP.", " . C\FOREVER . ", 0, 0)");
+
+    $now = time();
+    $db->execute("INSERT INTO USER_GROUP VALUES (" . C\ROOT_ID . ", " .
+              C\MODERATION_GROUP_ID . ", " . C\ACTIVE_STATUS . ", $now, 0, 0)");
 
     $private_db_class = C\NS_DATASOURCES . ucfirst(C\PRIVATE_DBMS) . "Manager";
     $private_dbinfo = [
@@ -2141,9 +2160,12 @@ function upgradeDatabaseVersion79(&$db)
         "DB_PASSWORD" => C\PRIVATE_DB_PASSWORD
     ];
     $private_db = new $private_db_class();
-    $private_db->connect(C\PRIVATE_DB_HOST, C\PRIVATE_DB_USER, C\PRIVATE_DB_PASSWORD, C\PRIVATE_DB_NAME);
+    $private_db->connect(C\PRIVATE_DB_HOST,
+    C\PRIVATE_DB_USER, C\PRIVATE_DB_PASSWORD, C\PRIVATE_DB_NAME);
 
-    $private_db->execute("CREATE TABLE GROUP_KEYS (GROUP_ID $integer PRIMARY KEY, RANDOM_NUM_X VARCHAR(" . C\LONG_NAME_LEN . ") DEFAULT '', RANDOM_NUM_Y VARCHAR(" . C\LONG_NAME_LEN . ") DEFAULT '')");
+    $private_db->execute("CREATE TABLE GROUP_KEYS (GROUP_ID $integer
+    PRIMARY KEY, RANDOM_NUM_X VARCHAR(" . C\LONG_NAME_LEN . ") DEFAULT '',
+    RANDOM_NUM_Y VARCHAR(" . C\LONG_NAME_LEN . ") DEFAULT '')");
 
     $private_db->execute("DROP TABLE TYPE_KEYS");
-}
+}
\ No newline at end of file
-- 
2.37.1 (Apple Git-137.1)


From 742f5fe20496774b47cec6aab55c06a7cc0a9d43 Mon Sep 17 00:00:00 2001
From: Prajna Puranik <prajnagp@gmail.com>
Date: Wed, 24 May 2023 21:21:56 -0700
Subject: [PATCH 9/9] bug fix in encrypted group flagging

---
 src/models/GroupModel.php | 23 ++++++++++++-----------
 1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/src/models/GroupModel.php b/src/models/GroupModel.php
index d09b1c435..2943d170b 100644
--- a/src/models/GroupModel.php
+++ b/src/models/GroupModel.php
@@ -1327,23 +1327,24 @@ class GroupModel extends Model implements MediaConstants
         $result = $stmt->fetch();
         $group_id = $result['GROUP_ID'];
         $flag_count = $this->getFlagCount($post_id);
-        if ($this->isGroupEncrypted($group_id))
-        {
+        if ($this->isGroupEncrypted($group_id)) {
             $threshold = $this->getThresholdValue($group_id, $user_id);
             $key = $this->generateKey($group_id, $user_id);
-            if($flag_count == 0){
-                $flag_count = $this->encryptFlag(strval($flag_count), $key);
+
+            if (is_null($flag_count)) {
+                $flag_count = $this->encryptFlag(strval(0), $key);
             }
+
             if ($flag_count === $threshold) {
-                $flag_count_decrypt = $this->decryptFlag(
-                $flag_count, $key);
+                $flag_count_decrypt = $this->decryptFlag($flag_count, $key);
                 $this->addGroupItemModeration(0, 5, $result['USER_ID'],
-                       $result['TITLE'],$result['DESCRIPTION'],
-                       $flag_count_decrypt, $post_id, $group_id,
-                       C\STANDARD_GROUP_ITEM, 0, "");
-            }else{
-                $flag_count = $this->encryptFlag(strval($flag_count), $key);
+                    $result['TITLE'], $result['DESCRIPTION'],
+                    $flag_count_decrypt, $post_id, $group_id,
+                    C\STANDARD_GROUP_ITEM, 0, "");
+            }else if ($flag_count === $this->encryptFlag($threshold, $key)) {
+                return false;
             }
+            $flag_count = $this->encryptFlag(strval($flag_count), $key);
         }
         else{
             $flag_count = $flag_count + 1;
-- 
2.37.1 (Apple Git-137.1)

