<commit_before><?php

/**
 * @file src/Model/Item.php
 */

namespace Friendica\Model;

use Friendica\Core\Worker;
use Friendica\Model\Term;
use Friendica\Model\Contact;
use Friendica\Database\DBM;
use dba;

require_once 'include/tags.php';
require_once 'include/threads.php';
require_once 'include/items.php';

class Item
{
	/**
	 * @brief Update existing item entries
	 *
	 * @param array $fields The fields that are to be changed
	 * @param array $condition The condition for finding the item entries
	 *
	 * In the future we may have to change permissions as well.
	 * Then we had to add the user id as third parameter.
	 *
	 * A return value of "0" doesn't mean an error - but that 0 rows had been changed.
	 *
	 * @return integer|boolean number of affected rows - or "false" if there was an error
	 */
	public static function update(array $fields, array $condition)
	{
		if (empty($condition) || empty($fields)) {
			return false;
		}

		$success = dba::update('item', $fields, $condition);

		if (!$success) {
			return false;
		}

		$rows = dba::affected_rows();

		// We cannot simply expand the condition to check for origin entries
		// The condition needn't to be a simple array but could be a complex condition.
		$items = dba::select('item', ['id', 'origin'], $condition);
		while ($item = dba::fetch($items)) {
			// We only need to notfiy others when it is an original entry from us
			if (!$item['origin']) {
				continue;
			}

			create_tags_from_item($item['id']);
			Term::createFromItem($item['id']);
			update_thread($item['id']);

			Worker::add(PRIORITY_HIGH, "Notifier", 'edit_post', $item['id']);
		}

		return $rows;
	}

	public static function delete(array $condition, $priority = PRIORITY_HIGH)
	{
/*
		// locate item to be deleted
		$item = dba::selectFirst('item', [], $condition);
		if (!DBM::is_result($item)) {
			return false;
		}

		if ($item['deleted']) {
			return false;
		}

		$owner = $item['uid'];

		$contact_id = 0;

		logger('delete item: ' . $item['id'], LOGGER_DEBUG);

		// clean up categories and tags so they don't end up as orphans

		$matches = false;
		$cnt = preg_match_all('/<(.*?)>/', $item['file'], $matches, PREG_SET_ORDER);
		if ($cnt) {
			foreach ($matches as $mtch) {
				file_tag_unsave_file($item['uid'], $item['id'], $mtch[1],true);
			}
		}

		$matches = false;

		$cnt = preg_match_all('/\[(.*?)\]/', $item['file'], $matches, PREG_SET_ORDER);
		if ($cnt) {
			foreach ($matches as $mtch) {
				file_tag_unsave_file($item['uid'], $item['id'], $mtch[1],false);
			}
		}

		//*
		// * If item is a link to a photo resource, nuke all the associated photos
		// * (visitors will not have photo resources)
		// * This only applies to photos uploaded from the photos page. Photos inserted into a post do not
		// * generate a resource-id and therefore aren't intimately linked to the item.
		// *
		if (strlen($item['resource-id'])) {
			dba::delete('photo', ['resource-id' => $item['resource-id'], 'uid' => $item['uid']]);
		}

		// If item is a link to an event, nuke the event record.
		if (intval($item['event-id'])) {
			dba::delete('event', ['id' => $item['event-id'], 'uid' => $item['uid']]);
		}

		// If item has attachments, drop them
		foreach (explode(", ", $item['attach']) as $attach) {
			preg_match("|attach/(\d+)|", $attach, $matches);
			dba::delete('attach', ['id' => $matches[1], 'uid' => $item['uid']]);
		}

		// Set the item to "deleted"
		// Don't delete it here, since we have to send delete messages
		dba::update('item', ['deleted' => true, 'title' => '', 'body' => '',
					'edited' => datetime_convert(), 'changed' => datetime_convert()],
				['id' => $item['id']]);

		create_tags_from_item($item['id']);
		Term::createFromItem($item['id']);
		delete_thread($item['id'], $item['parent-uri']);

		// Creating list of parents
		$r = q("SELECT `id` FROM `item` WHERE `parent` = %d AND `uid` = %d",
			intval($item['id']),
			intval($item['uid'])
		);

		$parentid = "";

		foreach ($r as $row) {
			if ($parentid != "") {
				$parentid .= ", ";
			}

			$parentid .= $row["id"];
		}

		// Now delete them
		if ($parentid != "") {
			q("DELETE FROM `sign` WHERE `iid` IN (%s)", dbesc($parentid));
		}

		// If it's the parent of a comment thread, kill all the kids
		if ($item['uri'] == $item['parent-uri']) {
			dba::update('item', ['deleted' => true, 'title' => '', 'body' => '',
					'edited' => datetime_convert(), 'changed' => datetime_convert()],
				['parent-uri' => $item['parent-uri'], 'uid' => $item['uid']]);

			create_tags_from_itemuri($item['parent-uri'], $item['uid']);
			Term::createFromItemURI($item['parent-uri'], $item['uid']);
			delete_thread_uri($item['parent-uri'], $item['uid']);
			// ignore the result
		} else {
			// ensure that last-child is set in case the comment that had it just got wiped.
			dba::update('item', ['last-child' => false, 'changed' => datetime_convert()],
					['parent-uri' => $item['parent-uri'], 'uid' => $item['uid']]);

			// who is the last child now?
			$r = q("SELECT `id` FROM `item` WHERE `parent-uri` = '%s' AND `type` != 'activity' AND `deleted` = 0 AND `uid` = %d ORDER BY `edited` DESC LIMIT 1",
				dbesc($item['parent-uri']),
				intval($item['uid'])
			);
			if (DBM::is_result($r)) {
				dba::update('item', ['last-child' => true], ['id' => $r[0]['id']]);
			}
		}

		// send the notification upstream/downstream
		Worker::add(['priority' => $priority, 'dont_fork' => true], "Notifier", "drop", intval($item['id']));

		return true;
*/
	}

	/**
	 * @brief Add a shadow entry for a given item id that is a thread starter
	 *
	 * We store every public item entry additionally with the user id "0".
	 * This is used for the community page and for the search.
	 * It is planned that in the future we will store public item entries only once.
	 *<commit_msg>Delete could work now, needs testing<commit_after><?php

/**
 * @file src/Model/Item.php
 */

namespace Friendica\Model;

use Friendica\Core\Worker;
use Friendica\Model\Term;
use Friendica\Model\Contact;
use Friendica\Database\DBM;
use dba;

require_once 'include/tags.php';
require_once 'include/threads.php';
require_once 'include/items.php';

class Item
{
	/**
	 * @brief Update existing item entries
	 *
	 * @param array $fields The fields that are to be changed
	 * @param array $condition The condition for finding the item entries
	 *
	 * In the future we may have to change permissions as well.
	 * Then we had to add the user id as third parameter.
	 *
	 * A return value of "0" doesn't mean an error - but that 0 rows had been changed.
	 *
	 * @return integer|boolean number of affected rows - or "false" if there was an error
	 */
	public static function update(array $fields, array $condition)
	{
		if (empty($condition) || empty($fields)) {
			return false;
		}

		$success = dba::update('item', $fields, $condition);

		if (!$success) {
			return false;
		}

		$rows = dba::affected_rows();

		// We cannot simply expand the condition to check for origin entries
		// The condition needn't to be a simple array but could be a complex condition.
		$items = dba::select('item', ['id', 'origin'], $condition);
		while ($item = dba::fetch($items)) {
			// We only need to notfiy others when it is an original entry from us
			if (!$item['origin']) {
				continue;
			}

			create_tags_from_item($item['id']);
			Term::createFromItem($item['id']);
			update_thread($item['id']);

			Worker::add(PRIORITY_HIGH, "Notifier", 'edit_post', $item['id']);
		}

		return $rows;
	}

	public static function delete($item_id, $priority = PRIORITY_HIGH)
	{
		// locate item to be deleted
		$item = dba::selectFirst('item', [], ['id' => $item_id]);
		if (!DBM::is_result($item)) {
			return false;
		}

		if ($item['deleted']) {
			return false;
		}

		logger('delete item: ' . $item['id'], LOGGER_DEBUG);

		// clean up categories and tags so they don't end up as orphans

		$matches = false;
		$cnt = preg_match_all('/<(.*?)>/', $item['file'], $matches, PREG_SET_ORDER);
		if ($cnt) {
			foreach ($matches as $mtch) {
				file_tag_unsave_file($item['uid'], $item['id'], $mtch[1],true);
			}
		}

		$matches = false;

		$cnt = preg_match_all('/\[(.*?)\]/', $item['file'], $matches, PREG_SET_ORDER);
		if ($cnt) {
			foreach ($matches as $mtch) {
				file_tag_unsave_file($item['uid'], $item['id'], $mtch[1],false);
			}
		}

		/*
		 * If item is a link to a photo resource, nuke all the associated photos
		 * (visitors will not have photo resources)
		 * This only applies to photos uploaded from the photos page. Photos inserted into a post do not
		 * generate a resource-id and therefore aren't intimately linked to the item.
		 */
		if (strlen($item['resource-id'])) {
			dba::delete('photo', ['resource-id' => $item['resource-id'], 'uid' => $item['uid']]);
		}

		// If item is a link to an event, nuke the event record.
		if (intval($item['event-id'])) {
			dba::delete('event', ['id' => $item['event-id'], 'uid' => $item['uid']]);
		}

		// If item has attachments, drop them
		foreach (explode(", ", $item['attach']) as $attach) {
			preg_match("|attach/(\d+)|", $attach, $matches);
			dba::delete('attach', ['id' => $matches[1], 'uid' => $item['uid']]);
		}

		// When it is our item we don't delete it here, since we have to send delete messages
		if ($item['origin']) {
			// Set the item to "deleted"
			dba::update('item', ['deleted' => true, 'title' => '', 'body' => '',
						'edited' => datetime_convert(), 'changed' => datetime_convert()],
					['id' => $item['id']]);

			create_tags_from_item($item['id']);
			Term::createFromItem($item['id']);
			delete_thread($item['id'], $item['parent-uri']);

			// If it's the parent of a comment thread, kill all the kids
			if ($item['id'] == $item['parent']) {
				$items = dba::select('item', ['id'], ['parent' => $item['parent']]);
				while ($row = dba::fetch($items)) {
					self::delete($row['id'], $priority);
				}
			}

			// send the notification upstream/downstream
			Worker::add(['priority' => $priority, 'dont_fork' => true], "Notifier", "drop", intval($item['id']));
		} else {
			// delete it immediately. All related children will be deleted as well.
			dba::delete('item', ['id' => $item['id']]);
		}

		if ($item['id'] != $item['parent']) {
			// ensure that last-child is set in case the comment that had it just got wiped.
			dba::update('item', ['last-child' => false, 'changed' => datetime_convert()],
					['parent' => $item['parent']]);

			// who is the last child now?
			$r = q("SELECT `id` FROM `item` WHERE `parent-uri` = '%s' AND `type` != 'activity' AND `deleted` = 0 AND `uid` = %d ORDER BY `edited` DESC LIMIT 1",
				dbesc($item['parent-uri']),
				intval($item['uid'])
			);
			if (DBM::is_result($r)) {
				dba::update('item', ['last-child' => true], ['id' => $r[0]['id']]);
			}
		}

		return true;
	}

	/**
	 * @brief Add a shadow entry for a given item id that is a thread starter
	 *
	 * We store every public item entry additionally with the user id "0".
	 * This is used for the community page and for the search.
	 * It is planned that in the future we will store public item entries only once.
	 *
{'fraction_non_alphanumeric': 0.18719611021069693, 'fraction_numerical': 0.0017107869620025212, 'mean_word_length': 3.6687683900798653, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 54, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2180059', 'n_tokens_mistral': 4085, 'n_tokens_neox': 3619, 'n_words': 1144}