Журнал пользователя движка phpFOX 3

18:21 21.06.2017
Сам я плохо знаком с движком phpFOX, но решил внести свои изменения в движок phpFOX, так что буду наступать на грабли, изобретать велосипеды, много косячить, но уверен, что со временем выйдет толк (толк выйдет, а дурь останется!).

Уже точно не помню, но, скорее всего был 2014 год, когда я купил лицензию на движок phpFox 3. Купленную версию тоже точно не помню, то ли 3.6.x, а может быть 3.5.x. Да это и не важно, важно было то, что у меня появился полноценный движок социальной сети. Я тогда не задумывался о том, как я буду его раскручивать или наполнять, я вообще не задумывался - зачем мне вообще социальная сеть, я просто хотел ее заполучить, и чтобы она обязательно была лицензированная.

Перед приобретением лицензии, я рассматривал много движков социальной сети, были варианты, как бесплатные, так и платные. phpFox я рассматривал вместе с движком Oxwall (https://www.oxwall.com), который несмотря на то, что он бесплатный, он довольно мощный. Но, все же мне больше понравился phpFox, который вскоре я и купил... Кстати, возможно будет интересно: Сравнение движков социальных сетей (phpFox | Oxwall).

Движок изначально идет с одним установленным языком (английский), остальные языки надо либо устанавливать, либо переводить самостоятельно. Я решил, что буду переводить сам. Кстати, до сих пор так его и не доперевел.

Немного попользовавшись движком заметил, что он оставляет много мусора, в основном это заметки, которые были удаленны, но, на самом деле остались либо в БД, либо на сервере. Такое я называю "мусором" так как эти данные ни где не используются. Проблемы с мусором я решаю путем исправления кода, который удаляет файлы с сервера и записи с БД. К тому же, написал несколько инструментов для поиска и удаления мусора, на случай если во время удаления вдруг появится ошибка.

Кроме мусора, что оставляет движок, есть еще куча других багов, которые я по возможности исправляю, переписываю код, полностью методы и даже полностью классы движка. По началу я писал об ошибках разработчикам движка, но мне казалось, что они сильно долго исправляют их, либо просто "кормят" обещанием, что будет исправлено, но, исправление не выходит... И я решил заняться исправлением сам.

Во многих местах кода напрочь отсутствует логика, присутствуют функции, которые ни где не используются, стиль кода заметно меняется, видимо программу писали разные люди. Код напичкан "костылями", из за которых в других местах разработчикам пришлось лепить еще костыли, и "костылять" дальше.

Несмотря на все косяки, что имеет движок, я не хочу его забросить, я хочу его исправить, возможно даже довести до ума! Мной уже не мало было исправлено багов (и продолжаю исправлять), добавлено самопальных модификаций, такие как: Последние статьи, Последние видео, Карусель фотографий пользователя, Карта сайта, Обработка URL и еще какие то (мелкие).

О некоторых изменениях на сайте, рассказываю тут: Новостная тема DANFA.
11:36 26.06.2017
Знакомый предложил использовать кириллицу и для этого взять код из четвертой версии движка.
Отличная идея, ведь там разработчики исправили код, и движок работает с кириллице.

Делаю так: открываю: include/library/phpfox/parse/output.class.php, нахожу функцию: shorten и полностью меняю ее на три функции сразу:
    public function shorten($html, $maxLength, $sSuffix = null, $bHide = false)
    {
        mb_internal_encoding('utf-8');

        if (defined('PHPFOX_LANGUAGE_SHORTEN_BYPASS') || $maxLength === 0 || $this->hasReachedMaxLine() || Phpfox::getParam('language.no_string_restriction'))
        {
            return $html;
        }

        $sNewString = $this->truncate($html, $maxLength);

		if ($sSuffix !== null)
		{
		    $sCountHtml = strip_tags($html);
		    $sCountHtml = preg_replace('/&#?[a-zа-я0-9]+;/iu', 'A', $sCountHtml);			
			
			if (mb_strlen($sCountHtml) > $maxLength)
			{
				$this->shortened = true;

				if ($bHide === true)
				{
					if (defined('PHPFOX_IS_THEATER_MODE'))
					{
						$sNewString  = '<span class="js_view_more_parent"><span class="js_view_more_part">' . $this->closeAllHtmlTags($sNewString) . '...<div class="item_view_more"><a href="#" onclick="$(this).parents(\'.js_view_more_parent:first\').find(\'.js_view_more_part\').hide(); $(this).parents(\'.js_view_more_parent:first\').find(\'.js_view_more_full\').show(); return false;">' . Phpfox::getPhrase($sSuffix) . '</a></div></span>';
						$sNewString .= '<span class="js_view_more_full" style="display: none; position: absolute; z-index: 10000; background:#fff; border: 1px #dfdfdf solid;">';
						$sNewString .= '<div style="max-height: 200px; overflow: auto; padding: 5px;">' . $html . '</div>';
						$sNewString .= '</span>';
						$sNewString .= '</span>';
					}
					else
					{
						$sNewString  = '<span class="js_view_more_parent"><span class="js_view_more_part">' . $this->closeAllHtmlTags($sNewString) . '...<div class="item_view_more"><a href="#" onclick="$(this).parents(\'.js_view_more_parent:first\').find(\'.js_view_more_part\').hide(); $(this).parents(\'.js_view_more_parent:first\').find(\'.js_view_more_full\').show(); return false;">' . Phpfox::getPhrase($sSuffix) . '</a></div></span>';
						$sNewString .= '<span class="js_view_more_full" style="display: none;">';
						$sNewString .= $html;
						$sNewString .= '</span>';
						$sNewString .= '</span>';
					}
				}
				else
				{
					$sNewString .= $sSuffix;
				}
			}
		}
		
    	return $sNewString;    	
    }

	public function truncate($text, $length)
	{
        $html  = true;
        $ending = '';
        $exact = true;

        if ($html)
		{
            if (mb_strlen(preg_replace('/<.*?>/', '', $text)) <= $length)
			{
                return $text;
            }

            $totalLength = mb_strlen(strip_tags($ending));
            $openTags    = array();
            $truncate    = '';

            preg_match_all('/(<\/?([\w+]+)[^>]*>)?([^<>]*)/', $text, $tags, PREG_SET_ORDER);
            foreach ($tags as $tag)
			{
                if (!preg_match('/img|br|input|hr|area|base|basefont|col|frame|isindex|link|meta|param/s', $tag[2]))
				{
                    if (preg_match('/<[\w]+[^>]*>/s', $tag[0]))
					{
                        array_unshift($openTags, $tag[2]);
                    }
					else if (preg_match('/<\/([\w]+)[^>]*>/s', $tag[0], $closeTag))
					{
                        $pos = array_search($closeTag[1], $openTags);
                        if ($pos !== false)
						{
                            array_splice($openTags, $pos, 1);
                        }
                    }
                }

                $truncate .= $tag[1];

                $contentLength = mb_strlen(preg_replace('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', ' ', $tag[3]));
                if ($contentLength + $totalLength > $length)
				{
                    $left = $length - $totalLength;
                    $entitiesLength = 0;
                    if (preg_match_all('/&[0-9a-z]{2,8};|&#[0-9]{1,7};|&#x[0-9a-f]{1,6};/i', $tag[3], $entities, PREG_OFFSET_CAPTURE))
					{
                        foreach ($entities[0] as $entity)
						{
                            if ($entity[1] + 1 - $entitiesLength <= $left)
							{
                                $left--;
                                $entitiesLength += mb_strlen($entity[0]);
                            }
							else
							{
                                break;
                            }
                        }
                    }

                    $truncate .= mb_substr($tag[3], 0 , $left + $entitiesLength);
                    break;
                }
				else
				{
                    $truncate .= $tag[3];
                    $totalLength += $contentLength;
                }

                if ($totalLength >= $length)
				{
                    break;
                }
            }
        }
		else
		{
            if (mb_strlen($text) <= $length)
			{
                return $text;
            }
			else
			{
                $truncate = mb_substr($text, 0, $length - mb_strlen($ending));
            }
        }

        if (!$exact)
		{
            $spacepos = mb_strrpos($truncate, ' ');
            if (isset($spacepos))
			{
                if ($html)
				{
                    $bits = mb_substr($truncate, $spacepos);
                    preg_match_all('/<\/([a-z]+)>/', $bits, $droppedTags, PREG_SET_ORDER);
                    if (!empty($droppedTags))
					{
                        foreach ($droppedTags as $closingTag)
						{
                            if (!in_array($closingTag[1], $openTags))
							{
                                array_unshift($openTags, $closingTag[1]);
                            }
                        }
                    }
                }

                $truncate = mb_substr($truncate, 0, $spacepos);
            }
        }

        $truncate .= $ending;

        if ($html)
		{
            foreach ($openTags as $tag)
			{
                $truncate .= '</'.$tag.'>';
            }
        }

        return $truncate;
    }

    public function closeAllHtmlTags($html)
	{
        #put all opened tags into an array
        preg_match_all('#<([a-z]+)(?: .*)?(?<![/|/ ])>#iU', $html, $result);
        $openedtags = $result[1];   #put all closed tags into an array

        preg_match_all('#</([a-z]+)>#iU', $html, $result);

        $closedtags = $result[1];

        $len_opened = count($openedtags);

        # all tags are closed
        if (count($closedtags) == $len_opened)
		{
            return $html;
        }

        $openedtags = array_reverse($openedtags);

        # close tags
        for ($i=0; $i < $len_opened; $i++)
		{
            if (!in_array($openedtags[$i], $closedtags))
			{
                $html .= '</'.$openedtags[$i].'>';

            }
			else
			{
                unset($closedtags[array_search($openedtags[$i], $closedtags)]);
            }
        }

        return $html;
    }

Теперь работаю над удалением всех функций, которые работали с Юникодом.
11:28 6.07.2017
Я уже делал ранее конвертирование локальных URL форума в названия тем, на которую ведет URL. Сегодня решил продолжить разработку и сделал конвертирование URL для блога. Код конвертора пока что выглядит так:
		# Блог
		if (preg_match('~(?<!=)' . $path . 'blog\/[0-9]+\/~is', $value))
		{
			preg_match_all('~(?<!=)' . $path . 'blog\/([0-9]+)\/~is', $value, $blog_id);
			$blog_id = array_unique($blog_id[1]);
			foreach ($blog_id as $id)
			{
				$blog = $db->select('title')
				    ->from(Phpfox::getT('blog'))
			        ->where('blog_id = ' . (int) $id)
					->execute('getRow');

				$url    = $path . 'blog/' . $id . '/';
				$anchor = $blog['title'];

				$value = preg_replace('~(?<!=)' . $path . 'blog\/' . $id . '\/~is', '[url=' . trim($url) . ']' . trim($anchor) . '[/url]', $value);
			}
		} # Блог END

Возможно я вернусь к нему и сделаю лучше...
URL конвертируется в любой модификации - форум, блог, комментарии и так далее.
9:23 8.12.2017
Немного повозился с системой лайков (на этом сайте Плюс | Минус). Я исключил возможно лайкать свои записи, мне показалось, что лайкать свою запись не есть хорошо.

Изначально в движке перед каждым выводом кнопки лайка или дизлайка вставляется следующий код:
<script type="text/javascript">
if (typeof $Core.Like == 'undefined') {
	$Core.Like = {};
};

$Core.Like.Actions = {
	doLike : function(isCustom, itemTypeId, itemId, parentId, obj) {
		if ($(obj).closest('.comment_mini_link_like').find('.like_action_unmarked').is(':visible')) {
			$(obj).closest('.comment_mini_link_like').find('.like_action_marked').show();
			$(obj).closest('.comment_mini_link_like').find('.like_action_unmarked').hide();
		};

		$(obj).parent().find('.js_like_link_unlike:first').show();
		$(obj).hide();
		$.ajaxCall('like.add', 'type_id=' + itemTypeId + '&item_id=' + itemId + '&parent_id=' + parentId + '&custom_inline=' + isCustom, 'GET');
	}
};
</script>

Не смог понять для чего выводить этот код каждый раз и сделал вывод всего один раз. Теперь этот код находится внизу страницы (исходный код), больше ни где он не встречается.
22:09 8.02.2018
Создавая страницу пользователь становится ее администратором и может редактировать и изменять сообщения форума своей страницы. По началу мне это показалось "плохо" и я лишил администратора страницы редактировать и удалять посты, потом я пересмотрел свое решение и пришел к выводу, что админ должен управлять всем. Я вернул возможно редактирования постов, но не так, как это сделали разработчики, я внес свои правки в эту возможность. Разработчики движка дали возможность (как я понял) управлять чужими записями на своей странице только в моде форум, всем остальным управлять админ не может, а это блог, фото, видео, комменты и так далее. Хочу добавить возможность управлять этим всем.

Делаю вывод ссылок на страницах. Ссылки ведут на другие страницы пользователя, схожие по категории (тематики). Например, на странице Восьмибитная реальность, появился блок с ссылкой на страницу Игровая вселенная. Страницы "Восьмибитная реальность" и "Игровая вселенная" находятся в одной и той же категории "Компьютерные игры" и были созданы одним человеком (мной), по этому страницы ссылаются друг на друга. На данный момент работа не завершена полностью и можно наблюдать нерелевантные ссылки, это временно.
18:27 21.03.2018
Разработчики связали многие моды с модом страниц (pages), однако они забыли, или намерено не добавили возможность добавлять опросы на страницах, что мне кажется это полным бредом. Я считаю, что возможность добавлять опросы на страницах, куда важнее, чем добавлять опрос в свой профиль... В общем я решил это исправить, и вроде у меня получается! На данный момент я еще допиливаю связку, но в целом опросы на страницах уже вполне функциональны. Теперь опрос можно создать непосредственно со стены страницы или зайдя в раздел опросов (страницы) и создать там.
Администратор страницы имеет выбор, от чьего имени создавать опрос, от своего имени или от имени страницы.

Пока, опрос нельзя добавить в тему форума, что принадлежит странице, как это сделано на общем форуме. Думаю, что в ближайшее время, я исправлю и это.

Пока я не планирую выкладывать правки связки опроса со страницами, так как я не помню, что куда вносил / редактировал, много кода перебрал за последние два дня, но если кому то понадобиться, я постараюсь вспомнить все и написать. В общем если надо, напишите (в отдельной теме).
9:03 28.03.2018
Теперь опросы можно добавлять в темы, что принадлежат страницам. Поправил этот "недосмотр" разработчиков практически сразу, как написал предыдущий пост, в этой теме. Оказалось, там пару строк надо было изменить.

С модификацией "Опросы" продолжаю возиться. Исправил баг - добавление варианта между уже существующих вариантов при редактировании или создании. Варианты, что добавляются между уже существующих вариантов, например, при редактировании опроса, не добавляются в базу и собственно их нет при голосовании. Данный баг замечен на версии 3.9.0. Исправлено.

Сегодня сайт перевел на php 7.2.
26.03.2018 мой хостер добавил новую версию php, сегодня я перешел на нее. Сайт еще тестирую, пока все нормально.
16:59 5.05.2018
Движок перенес на VPS (новость: Новостная тема DANFA (Сообщение отдельно: #2162)). Сервер уже переведен на PHP 7.2. Переезд был не очень удачным, но большая часть ошибок уже исправлена, исправляются по мере обнаружения, думаю, что есть еще.

Процентов на 40% переписал модификацию "Like". Я подумал, что аватарки лайкнувших и количество под каждой записью занимают место, которое занимать не стоит и удалил аватарки и перенес количество лайкнувших. Теперь количество лакнувших запись пользователей находится в одном ряду с кнопками Плюс, Коммент, Репост и Жалоба, радом с кнопкой "Плюс".

Ускорил ответ от сервера, примерно на о.5 секунд. Как это делал, рассказал тут: Долгий ответ от сервера. Думаю, моя инструкция может быть полезна многим владельцам сайта. Продолжаю искать места, которые могут тормозить генерацию страницы.
17:06 1.07.2018
Исправил несколько недочетов в модификации "mail", за одно переключил личные сообщения в форумный вид, теперь читать и писать личные сообщения гораздо удобнее. Хочу полностью вырезать просмотр ЛС по отдельности. Как мне кажется ужасная была идея настроить ЛС - по одному сообщению на страницу, форумный вид, в разы удобнее.
16:49 1.09.2018
Немного поработал со списком страниц, что находится на главной странице авторизованного пользователя (контролер "core.index-member"). Теперь список может быть разбит на две части: "Мои страницы" и "Подписки". В первый список (Мои страницы) помещаются страницы, которые были созданы непосредственно самим пользователем. Во второй список (Подписки) помещаются страницы других пользователях, но к которым пользователь "подключился".

На данный момент лимит на количество отображаемых ссылок (страниц) установлено 15, что для первого списка, что для второго. Сортируется в обратном порядке.
16:47 6.04.2019
Дабы найти то, чем не пользуюсь, но это работает на сайте и удалить, чтобы снизить нагрузку на сервер и ускорить ответ от сервера, отследил сколько делается запросов в БД на страницах ленты новостей. Оказалось порядка ~350 запросов в базу делает одна страница, со стеной... Это бешенное количество для одной страницы, даже такой, как стена... Такое количество запросов добавляет 0.05 секунд к генерации страницы.

Я выявил, куда больше всего идут запросы, они шли в таблицу ban, осуществляется проверка запрещенных слов, в каждой отдельной записи, то есть: коммент, статья, описание фотографии, и так далее, от туда и столько запросов. Конечно, кроме проверки на стоп слова, есть и другие запросы, но это чуть позже... Таблица "ban" у меня пустая абсолютно, и я просто убрал эту проверку. Количество запросов снизилось до ~100 штук. Неплохо так получилось! Но мне все ровно кажется, что это много...

После таблицы в "ban", по количеству запросов, следуют запросы для генерации меню, их было около 20 штук. Я попытался понять зачем столько раз получать список ссылок, но так и не понял. Блоков с меню на одной странице гораздо меньше. На данный момент уменьшил обращение в БД, за получением меню, путем сохранения меню в кэш.

На данный момент на странице со стеной, выполняется около 50 запросов в БД. Хочу эту цифру сделать еще меньше... Сайт стал работать быстрее!
20:00 23.04.2019
Разработчики движка написали пагинацию таким образом, что на одной странице можно вывести только одну пагинацию. Казалось бы, а зачем больше? Больше и не надо! Но, на странице стены под объектами, такие, как: фото, коммент страницы, ссылка и так далее, располагаются комментарии пользователей, которых, под одним объектом может быть очень много. Комментарии, благодаря опциям движка отображаются не все, а количеством установленным админом, остальные комменты можно увидеть, нажав на ссылку "Предыдущие комменты", но и тут при большом количестве комментов, мы не увидим всех комментов... Попытаюсь объяснить по другому. Отображение комментов, я настроил так: на стене отображается (для одного объекта) не более трех комментов, пагинация разворачивает по десять комментов за один раз. То есть мы видим объект, который имеет, скажем, 34 коммента, на экране всего три (последних), нажимаем ссылку "Предыдущие комменты" и видим десять последних, а еще 24 коммента, на стене, мы не можем увидеть, так как пагинация не может обрабатывать несколько объектов на одной странице. Чтобы увидеть все комменты, придется переходить на страницы самого объекта. Жутко не удобно...

Я решил, что данная недоработка - это большая проблема. Я потратил целый день, чтобы исправить проблемы, и как говориться: если долго мучиться, что нибудь получиться. Получилось! Теперь, чтобы читать все комментарии, под любым объектом на стена, не надо ни куда переходить, пагинация разворачивает все комменты, по десять штук (+10, +10, +10 и так далее).
11:16 5.01.2020
Столкнулся с проблемой... Сам не уверен, что смогу понятно объяснить суть проблемы. В общем, есть блок комментариев (Он находится практически под каждым объектом на стенке + на странице самого объекта), отображаются последние 10 комментов, чтобы просмотреть предыдущие 10 надо нажать на кнопку Предыдущие комментарии 20 / 89 (цифры 20 / 89 могут быть другие). Комментарии постоянно добавляются и некоторые удаляются, как спам или прочий хлам. Сценарий выполняется отлично, все комменты выводятся по порядку, пока не добавишь или удалишь коммент. После добавления или удаления коммента, нажимая на кнопку Предыдущие комментарии 20 / 89 некоторые комменты могут потеряться или повториться дважды. Добавлением/Удалением коммента я нарушаю корректность пагинации.

Я пробовал добавлять/отнимать количество добавленных/удаленных комментов к лимиту в запрос, это нарушает цепочку пагинации еще больше.

Тема о данной проблеме и ее решении: Пагинация комментариев.
19:44 7.01.2020
Отловил глюк. Возможно, что этот глюк был только на моем движке... В общем, если пользователь добавляет ссылку на стене какой либо страницы (группы), не от имени админа страницы, то выходит белиберда, на странице само ссылки ("https://site/profile-XXX/link-id_XXX/"). Контент страницы выглядит очень криво...

Чтобы решить проблему, я удалил часть кода в функции getActivityFeed, в файле: "module/link/include/service/callback.class.php":
		if ($aRow['module_id'] == 'pages' && empty($aRow['parent_user_name']) && Phpfox::getLib('request')->get('link-id') > 0)
		{
			$sLink = Phpfox::getLib('url')->makeUrl('pages', $aRow['parent_user_id']);
			$aReturn['feed_mini'] = true;
			$aReturn['feed_mini_content'] = Phpfox::getPhrase('friend.full_name_posted_a_href_link_a_link_a_on_a_href_parent_user_name', 
				array(
					'full_name'        => Phpfox::getService('user')->getFirstName($aItem['full_name']), 
					'link'             => $sLink,
					'parent_user_name' => $sLink, 
					'parent_full_name' => $aRow['parent_full_name']
				)
			);

			unset($aReturn['feed_status'], $aReturn['feed_image'], $aReturn['feed_title'], $aReturn['feed_content']);
		}
		else if (!PHPFOX_IS_AJAX && defined('PHPFOX_IS_USER_PROFILE') && !empty($aRow['parent_user_name']) && $aRow['parent_user_id'] != Phpfox::getService('profile')->getProfileUserId())
		{
			$aReturn['feed_mini'] = true;
			$aReturn['feed_mini_content'] = Phpfox::getPhrase('friend.full_name_posted_a_href_link_a_link_a_on_a_href_parent_user_name', array('full_name' => Phpfox::getService('user')->getFirstName($aItem['full_name']), 'link' => $sLink, 'parent_user_name' => Phpfox::getLib('url')->makeUrl($aRow['parent_user_name']), 'parent_full_name' => $aRow['parent_full_name']));

			unset($aReturn['feed_status'], $aReturn['feed_image'], $aReturn['feed_title'], $aReturn['feed_content']);
			}
		else 
		{
			if ($aRow['has_embed'])
			{
			$aReturn['feed_image_onclick'] = '$Core.box(\'link.play\', 700, \'id=' . $aRow['link_id'] . '&amp;feed_id=' . $aItem['feed_id'] . '&amp;popup=true\', \'GET\'); return false;';
			}
		}

Проблема была из за первой части условия:
if ($aRow['module_id'] == 'pages' && empty($aRow['parent_user_name']) && Phpfox::getLib('request')->get('link-id') > 0)

Я удалил все условие, так как не понял, где это мне может понадобиться.

Помните, что мои переменные могут отличаются от стандарта phpFox.
19:13 9.02.2020
Немного допилил модуль "Видео", теперь ссылки на Ютюб можно добавлять прямо на стене. На стене своих страниц можно добавлять видео от имени сообщества (страницы).

Увеличил превьюшки видеороликов в разделе видео.
Журнал пользователя движка phpFOX 3
19:04 3.03.2020
Долгое время не замечал, заметил совсем недавно, что кнопки "Жалоб" в ленте новостей нет у таких каналов как Ссылка и Коммент стены страницы. Покопавшись, нашел причину отсутствия кнопки, просто не было необходимых методов в файлах модов: Стены и Ссылки. Написал методы, кнопки появились.
7:35 9.03.2020
Внес небольшие изменения в ленте новостей, теперь описание фото и видео находится над описываемым предметом, а не под ним.

Увеличил шрифт в мобильной версии сайта.
19:01 15.03.2020
С левой стороны, внизу страницы, закрепил маленький блок "Контакты", где отображаются администраторы страницы. Блок появляется после того, как будет поднят вверх, за рамку экрана, и так же исчезает.
Липкий блок
Пока это черновая версия липкого блока Контакты, возможно немного переделаю.