COVID2019 и это вот все. Друзья, вся эта история начинает плохо пахнет. Мойте руки, не ходите в люди. Отложите все плановые покупки и положите в носок заначку. Заприте ваших родителей, бабушек-дедушек на даче. Лучше перебдеть чем недобдеть. Берегите себя!

Лайфхаки от BuslikDrev


Recommended Posts

  • 2 months later...
  • 4 weeks later...

6/ Появилась необходимость авторизоваться в своём аккаунте через php для парсинга нужных мне данных из сайта opencarforum
Код работает у меня на open server 5.2.2, может кому-то пригодиться для написания своего модуля и т.д.:

Скрытый текст

В корне каталога домена создать файл index.php с кодом и ввести логин и пароль в неём:


<?php
// Log in to Google account and go to account page
$site_login = 'https://opencartforum.com/login/';
$user_agent = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:49.0) Gecko/20100101 Firefox/49.0';
//$csrfKey = '21bd4226b66a3bd1abba64f4c5a87d51';
$USERNAME = 'user@opencart.pro'; // логин
$PASSWORD = '123456'; // пароль
$remember_me = 1;
$anonymous = 0;
$processLogin = 'usernamepassword';
$COOKIEFILE = 'cookies.txt';

// initialize curl handle used for all requests
$ch = curl_init($site_login);

// set some options on the handle
curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_MAXREDIRS, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_COOKIEJAR, $COOKIEFILE);
curl_setopt($ch, CURLOPT_COOKIEFILE, $COOKIEFILE);
curl_setopt($ch, CURLOPT_HEADER, 0);  
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_URL, $site_login);

$data = curl_exec($ch);
//$data = file_get_contents($site_login);
//$data = curl_exec_follow($ch);

// extract form fields from account login page
$formFields = getFormFields($data);

// inject email and password into form
$formFields['csrfKey'] = $formFields['csrfKey'];
$formFields['auth']  = $USERNAME;
$formFields['password'] = $PASSWORD;
$formFields['remember_me'] = $remember_me;
$formFields['anonymous'] = $anonymous;
$formFields['_processLogin'] = $processLogin;

$post_string = http_build_query($formFields, null, '&', PHP_QUERY_RFC3986); // build urlencoded POST string for login
$post_string = str_replace('%40', '@', $post_string);

curl_setopt($ch, CURLOPT_URL, $site_login);
curl_setopt($ch, CURLOPT_POST, 1);
//curl_setopt($ch, CURLOPT_SAFE_UPLOAD, false);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string);

// execute login request
$result = curl_exec($ch);

if (!$result) {
    die("Логин или парль введён неверно.");
}

// check for "Redirecting" message in title to indicate success
// based on your language - you may need to change this to match some other string
if (strpos(curl_exec($ch), 'ipsBox_alt') === false) {
    die("Авторизация больше не доступна, скорее всего был изменён стиль страницы, либо метод авторизации.");
}

if (strpos(curl_exec($ch), 'ipsMessage_error') == true) {
    die("Отображаемое имя или email адрес, введённый вами, не принадлежит ни к одному аккаунту. Убедитесь в правильности введённой информации.<br>Ваш логин: " . $USERNAME . "<br>Ваш пароль:" . $PASSWORD);
}

// вывод на экран
echo $result;

function getFormFields($data) {
    if (preg_match('/(<form.*?class=.*?ipsBox_alt.*?<\/form>)/is', $data, $matches)) {
		//var_dump($matches[0]);
        $inputs = getInputs($matches[0]);
		//var_dump($inputs);
        return $inputs;
    } else {
        die('Авторизация больше не доступна, скорее всего был изменён метод авторизации.');
    }
}

// extract all <input fields from a form
function getInputs($form) {
    $inputs = array();

    $elements = preg_match_all('/(<input[^>]+>)/is', $form, $matches);

    if ($elements > 0) {
        for($i = 0; $i < $elements; $i++) {
            $el = preg_replace('/\s{2,}/', ' ', $matches[1][$i]);

            if (preg_match('/name=(?:["\'])?([^"\'\s]*)/i', $el, $name)) {
                $name  = $name[1];
                $value = '';

                if (preg_match('/value=(?:["\'])?([^"\'\s]*)/i', $el, $value)) {
                    $value = $value[1];
                }

                $inputs[$name] = $value;
            }
        }
    }

    return $inputs;
}

// чтобы не применять curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); и не отключать open_basedir
function curl_exec_follow(/*resource*/ $ch, /*int*/ &$maxredirect = null) {
    $mr = $maxredirect === null ? 5 : intval($maxredirect);
    if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off')) {
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $mr > 0);
        curl_setopt($ch, CURLOPT_MAXREDIRS, $mr);
    } else {
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
        if ($mr > 0) {
            $newurl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
            $rch = curl_copy_handle($ch);
            curl_setopt($rch, CURLOPT_HEADER, true);
            curl_setopt($rch, CURLOPT_NOBODY, true);
            curl_setopt($rch, CURLOPT_FORBID_REUSE, false);
            curl_setopt($rch, CURLOPT_RETURNTRANSFER, true);
            do {
                curl_setopt($rch, CURLOPT_URL, $newurl);
                $header = curl_exec($rch);

                if (curl_errno($rch)) {
                    $code = 0;
                } else {
                    $code = curl_getinfo($rch, CURLINFO_HTTP_CODE);

                    if ($code == 301 || $code == 302 || $code == 403) {
                        preg_match('/Location:(.*?)\n/', $header, $matches);
                        $newurl = trim(array_pop($matches));
                    } else {
                        $code = 0;
                    }
                }
            } while ($code && --$mr);

            curl_close($rch);

            if (!$mr) {
                if ($maxredirect === null) {
                    trigger_error('Too many redirects. When following redirects, libcurl hit the maximum amount.', E_USER_WARNING);
                } else {
                    $maxredirect = 0;
                } 
 
                return false;
            } 

            curl_setopt($ch, CURLOPT_URL, $newurl);
        }
    }

    return curl_exec($ch);
}

 

 

Ссылка на сообщение
Поделиться на других сайтах
  • 3 weeks later...

7/ Бывают такие хостинги, что при постоянно большом количестве запросов может упасть Mysqli. В OpenCart можно легко сделать много запросов пройдя по ссылке: /index.php?route=product/search&search=&limit=100000 - загружаются все товары.


Чтобы это убрать и ограничить, например до 100 товаров.

Скрытый текст

Для oc2.3
В catalog/controller/product/search.php
Найти (здесь мы запрещаем вывод товара при пустом параметре &search= или &tag=):


		if (isset($this->request->get['search'])) {
			$search = $this->request->get['search'];
		} else {
			$search = '';
		}

		if (isset($this->request->get['tag'])) {
			$tag = $this->request->get['tag'];
		} elseif (isset($this->request->get['search'])) {
			$tag = $this->request->get['search'];
		} else {
			$tag = '';
		}

Заменить на:


		if (!empty($this->request->get['search'])) {
			$search = ($this->request->get['search'] > '' ? $this->request->get['search'] : '');
		} else {
			$search = '';
		}

		if (isset($this->request->get['tag'])) {
			$tag = ($this->request->get['tag'] > '' ? $this->request->get['tag'] : '');
		} elseif (isset($this->request->get['search'])) {
			$tag = ($this->request->get['search'] > '' ? $this->request->get['search'] : '');
		} else {
			$tag = '';
		}

 

Найти (этот пункт выполнить везде категории, производители и т.д. - это мы ограничиваем подсчёт товара, а вывод товара в моделях):


		if (isset($this->request->get['limit'])) {
			$limit = (int)$this->request->get['limit'];
		} else {
			$limit = $this->config->get($this->config->get('config_theme') . '_product_limit');
		}

Заменить на:


		if (isset($this->request->get['limit'])) {
			$limit = ((int)$this->request->get['limit'] > 100 ? 100 : (int)$this->request->get['limit']);
		} else {
			$limit = $this->config->get($this->config->get('config_theme') . '_product_limit');
		}

 

Найти (здесь мы запрещаем вывод товара при пустом параметре &search= или &tag=):


if (isset($this->request->get['search']) || isset($this->request->get['tag'])) {

Заменить на:


if ($search || $tag) {

 

В catalog/model/catalog/product.php

Найти (здесь мы ограничиваем вывод товара):


			if ($data['limit'] < 1) {
				$data['limit'] = 20;
			}

Вставить выше:


			if ($data['limit'] > 100 && $data['limit'] > $this->config->get('config_product_limit')) { // fix Buslikdrev
				$data['limit'] = 100;
			}

 


Также есть ограничение на индексацию страниц в robots.txt

Crawl-delay: 0.1

Ставить после Disallow:

0.1 - это секунды запрета посещать боту любую страницу сайта.

Подробности здесь.

 

В google этой задержки нет, только скорость сканирования.

с 2018 не работает уже.

Ссылка на сообщение
Поделиться на других сайтах
  • 1 month later...
  • 3 weeks later...

9/ Регистронезависимый поиск изображения


10/ узнаём примерную скорость выполнения php кода.

Скрытый текст

Вариант 1


define('ST_T', microtime()); // старт времени.
// тут наш код
printf('Время выполнения скрипта: %.5f сек.', microtime()-ST_T); // стоп время и вычисление разницы.


Вариант 2


$start = microtime(true); // старт времени.
// тут наш код
echo 'Время выполнения скрипта: ' . round(microtime(true) - $start, 4) . ' сек.'; // стоп время и вычисление разницы.

 

 

Ссылка на сообщение
Поделиться на других сайтах
13 часа назад, Yoda сказал:

Сделай отдельный скрипт!

Разделил

 

13 часа назад, Yoda сказал:

Во вторых за crawl-delay 120, необходимо голову откусывать по пятки!

Это считай убить индексацию магазина.

Каждый выбирает сам сколько ему секунд устанавливать и устанавливать-ли это ограничение вообще. Добавил ссылки на подробную информацию.

 

Спасибо за помощь.

Ссылка на сообщение
Поделиться на других сайтах
2 часа назад, Yoda сказал:

Такого делать нельзя категорически. И вешать ответсвенность типа каждый сам выбирает на не сведущих пользователей - это как винить в жидком стуле Дональда Трампа.

 

Ответственность не вешаю и никого не понуждаю. Ваше замечание было учтено (добавлены дополнительные ссылки на изучение) - оно видно всем. Также для вас и др. сменил значение на 0.1 сек. Оптимального значения для crawl-delay от вас не было озвучено, т.к. вы вообще против использования crawl-delay. 

 

2 часа назад, Yoda сказал:

Есть один единственный способ сделать нормальную индексацию - это правильная настройка роботса и быстрый магазин. Все....


Будем считать, что robots настроен правильно в сборке.pro, а если магазин не быстрый (к примеру финансов нет)?

Ссылка на сообщение
Поделиться на других сайтах
  • 7 months later...

10/ при генерации ссылок от названия, добавляйте id к ссылке, чтобы исключить их дубли.
Пример: http://site.ru/25-category/2544-tovar

Ссылка на сообщение
Поделиться на других сайтах
  • 2 months later...

11/ полезная статья для определения какие кавычки использовать - разница есть и нет такого: "кому какие нравятся".

 

Ссылка на сообщение
Поделиться на других сайтах

12/ доступ в админку через фтп:

Скрытый текст

Помещаем это код, например, в controller/common/header.php


$this->session->data['token'] = token(32);
$this->session->data['user_id'] = 1; // порядковый номер админа
echo $this->session->data['token'];

Открываем сайт, копируем токен и подставляем к нашей админке:
http://site.ru/admin/index.php?route=common/dashboard&token=сюда

Опосайтесь такого рода кода в коде вашего сайта.

 

 

13/ Совет по модулю строения для уменьшения лишних запросов к БД:

Скрытый текст

Для моего модуля не требуется выполнение стандартных запросов: выборка настроек из БД, языка, сеопро и т.д. OpenCart.pro при загрузки пустого контроллера делает около 12 sql-запросов к БД, а это около 25мс к загрузке и естественно лишняя нагрузка на сервер, когда, например, 1000 пользователей одновременно будут юзать ваш модуль, который загружается на отдельной странице, например, какая-нибудь карта сайта или подгружается через метод ajax.

 

Так вот, рекомендую устанавливать условия для загрузки нужных вам контроллеров и не создавать модули как отдельный php файл со своим подключением к БД:


<?xml version="1.0" encoding="utf-8"?>
<modification>
	<code>BuslikDrev</code>
    <name>.Правки от BuslikDrev</name>
    <version>0.1</version>
    <author>BuslikDrev</author>
    <link>https://liveopencart.ru/buslikdrev</link>
    <file path="system/engine/action.php">
        <operation skip="error">
            <search index="0">
			<![CDATA[function execute($registry]]>
            </search>
            <add position="after">
			<![CDATA[		if (isset($_POST['route']) && isset($_POST['ajax'])) {
			if ($_POST['route'] == 'extension/module/mymodule') {
				$stop = false;

				if ($this->route == 'startup/session') {
					//$stop = true;
				}
				if ($this->route == 'startup/startup') {
					$stop = true;
				}
				if ($this->route == 'startup/error') {
					$stop = true;
				}
				if ($this->route == 'startup/event') {
					$stop = true;
				}
				if ($this->route == 'startup/maintenance') {
					$stop = true;
				}
				if ($this->route == 'startup/seo_url') {
					$stop = true;
				}
				if ($this->route == 'startup/seo_pro') {
					$stop = true;
				}

				if ($stop) {
				    return;
				}
			}
		}

		if (isset($_GET['route']) && isset($_POST['ajax'])) {
			if ($_GET['route'] == 'extension/module/mymodule') {
				$stop = false;

				if ($this->route == 'startup/session') {
					//$stop = true;
				}
				if ($this->route == 'startup/startup') {
					$stop = true;
				}
				if ($this->route == 'startup/error') {
					$stop = true;
				}
				if ($this->route == 'startup/event') {
					$stop = true;
				}
				if ($this->route == 'startup/maintenance') {
					$stop = true;
				}
				if ($this->route == 'startup/seo_url') {
					$stop = true;
				}
				if ($this->route == 'startup/seo_pro') {
					$stop = true;
				}

				if ($stop) {
				    return;
				}
			}
		}]]>
            </add>
        </operation>
    </file>
    <file path="system/engine/loader.php">
        <operation skip="error">
            <search index="0">
			<![CDATA[function library(]]>
            </search>
            <add position="after">
			<![CDATA[		if (isset($_POST['route']) && isset($_POST['ajax'])) {
			if ($_POST['route'] == 'extension/module/mymodule') {
				return;
			}
		}

		if (isset($_GET['route']) && isset($_POST['ajax'])) {
			if ($_GET['route'] == 'extension/module/mymodule') {
				return;
			}
		}]]>
            </add>
        </operation>
    </file>
  </modification>

 

 

Ссылка на сообщение
Поделиться на других сайтах
  • 2 months later...

14/ для тех, кому лень адаптировать модули или шаблоны под твиг, то делаем так:

Скрытый текст

<?xml version="1.0" encoding="utf-8"?>
<modification>
    <code>TPL</code>
    <name>TPL</name>
    <version>1.0</version>
    <author>BuslikDrev</author>
    <link></link>
	<file path="system/engine/loader.php">
        <operation error="skip">
            <search index="0">
			<![CDATA[$template = new Template($this->registry->get('config')->get('template_engine'));]]>
            </search>
            <add position="before">
			<![CDATA[			if (strpos($route, 'mymodule') !== false) {
				$this->registry->get('config')->set('template_engine', 'template');
			}]]>
            </add>
        </operation>
    </file>
</modification>

 

 

или так в контроллере:

Скрытый текст

				if (version_compare(VERSION, '3.0.0', '>=')) {
					$template_engine = $this->registry->get('config')->get('template_engine');
					$template_directory = $this->registry->get('config')->get('template_directory');
					$this->registry->get('config')->set('template_engine', 'template');
					if (!file_exists(DIR_TEMPLATE . $template_directory . 'extension/module/mymodule' . '.tpl')) {
						$this->registry->get('config')->set('template_directory', 'default/template/');
					}
				}

				$template = $this->load->view('extension/module/mymodule', $data);

				if (version_compare(VERSION, '3.0.0', '>=')) {
					$this->registry->get('config')->set('template_engine', $template_engine);
					$this->registry->get('config')->set('template_directory', $template_directory);
				}

				$this->response->setOutput($template);

 

 

 

Ссылка на сообщение
Поделиться на других сайтах
  • 1 month later...

15/ Для ускорения работы модуля и работы над модулем при написании языков вы можете загружать их так:

Скрытый текст

В самом верху функции


		$data = $this->load->language('module/mymodule');

 

 

или так:

Скрытый текст

в любом месте перед шаблоном


	foreach ($this->load->language('module/mymodule') as $key => $lang) {
		$data[$key] = $lang;
	}

 

 

 

16/ Чтобы проверить включён ли на сайте вывод ошибок, то нужно попробовать открыть скрипт по прямому пути, например:

https://demo23.opencart.pro/catalog/controller/tool/upload.php

 

17/ полезная базовая информация по-сео:

https://q-seo.com.ua/blog/noindex-nofollow/

 

18/ Если кому-то нужно изменить название класса, например, при построении универсального модуля, то  нужно сделать так:

Скрытый текст

// адаптация с 2.3 OC под 2.1 OC
if (version_compare(VERSION, '2.2.0', '<')) {
	class ControllerModuleMyModule extends ControllerExtensionModuleMyModule {}
}

class ControllerExtensionModuleMyModule extends Controller {
	/* тут основной код модуля */
}

 

 

Ссылка на сообщение
Поделиться на других сайтах
  • 1 month later...

auto.thumb.png.903c86d134486fcc230075b9f509b55b.png

 

19/ Авто установка/удаление модуля + общая адаптация

 

Account v1.0 Full OpenCart 2.2.X-2.3.X.ocmod.zip

 

Для работы на 3.0 нужно добавить от условия добавление префикса module_ чтобы в макетах отобразило.

А для 2.1 нужно только расположение файлов поменять.

 

Желаю всем (кроме сосайта) улучшить свои модули и этот код!

 

20/ полезная статья для настройки .htaccess

 

21/ полезные статьи для работы с событиями (Event)

OpenCart 2.0-2.1

Opencart 2.2-3.X

Скрытый текст

 

Ссылка на сообщение
Поделиться на других сайтах
  • 5 months later...
  • 1 month later...

23/ обходим ограничение max_Input_vars в своих модулях.

Скрытый текст

Если кто-то желает можете создать отдельный модуль, как тут: https://opencartforum.com/files/file/2831-knopka-primenit-v-adminke/

 

Как узнать информацию об ограничении параметров max_input_vars подсмотрите тут: https://opencartforum.com/topic/98360-podderzhka-kontrol-prevysheniya-max_input_vars-v-formah/

Также это пример для модулей данные которых сохраняются в таблице oc_module, а не oc_setting, значит нам нужно вывести module_id в шаблон.

Ещё нужно вывести языковую переменную для кнопки: $button_apply_piecemeal и обратить внимание на наличие пути модуля <?php echo $action; ?>

 

Далее в контроллере модуля ищем:


public function index () {

 

Примечание: Если в инпутах у вас многомерные массивы, то в зависимости от ступеней многомерности добавляйте дополнительную проверку цикла.

Вставить после:


		if (($this->request->server['REQUEST_METHOD'] == 'POST') && $this->validate() && isset($this->request->post['apply'])) {
			if ($this->request->post['apply'] == 2 || $this->request->post['apply'] == 3) {
				$post_data = array();

				$posts = $this->cache->get('apply_piecemeal');

				if ($posts) {
					foreach ($posts as $key => $post) {
						if (is_array($post)) {
							foreach ($post as $k => $p) {
								$post_data[$key][$k] = $p;
							}
						} else {
							$post_data[$key] = $post;
						}
					}

					foreach ($this->request->post as $key => $post) {
						if (is_array($post)) {
							foreach ($post as $k => $p) {
								$post_data[$key][$k] = $p;
							}
						} else {
							$post_data[$key] = $post;
						}
					}
				} else {
					$post_data = $this->request->post;
				}

				if ($this->request->post['apply'] == 3) {
					$this->request->post = $post_data;
					if (isset($this->request->post['module_id']) && $this->request->post['module_id']) {
						$this->request->get['module_id'] = $this->request->post['module_id'];
					}
					$this->cache->delete('apply_piecemeal');
				} else {
					$cache = new Cache('file', 60);
					$cache->set('apply_piecemeal', $post_data);
					$this->response->addHeader('Content-Type: application/json');
					$this->response->setOutput(json_encode(array(), JSON_HEX_AMP));
					exit();
				}
			}
		}

 

В шаблоне модуля перед кнопкой сохранения или любое место вставить ( не забываем изменить id формы на свой ) :


<button type="button" onclick="savePiecemeal('form-mymodule');" data-toggle="tooltip" title="<?php echo $button_apply_piecemeal; ?>" class="btn btn-default"><i class="fa fa-save"></i></button>

 

В нужную форму нужно добавить input:


<input type="hidden" id="apply" name="apply" value="0">

 

В шаблоне модуля перед <?php echo $footer; ?> вставить:


<script type="text/javascript"><!--
	function savePiecemeal(id) {
		var form = document.querySelectorAll('form[id="'+ id + '"] input, form[id="'+ id + '"] select, form[id="'+ id + '"] textarea');
		//var formData = new FormData(document.forms[id]);

		if (form.length >= <?php echo $max_input_vars; ?>) {
			var savePiecemealAjax = function(stage, plus) {
				text = document.createElement('i');
				text.innerHTML = '<i>' + stage + '</i>';
				document.querySelector('button[title="<?php echo $button_apply_piecemeal; ?>"] i, button[data-original-title="<?php echo $button_apply_piecemeal; ?>"] i').replaceWith(text);

				data = new FormData();

				if (!plus) {
					var plus = 0;
				}

				for (var i = (0 + plus); i < (<?php echo $max_input_vars-2; ?> + plus); i++) {
					if (form[i] && form[i].getAttribute('disabled') != 'true') {
						data.append(form[i].name, form[i].value);
					}
				}

				data.append('module_id', <?php echo $module_id; ?>);
				if (stage > 2) {
					plus = plus + <?php echo $max_input_vars-2; ?>;
					data.append('apply', '2');
				} else if (stage > 1) {
					plus = plus + form.length - <?php echo $max_input_vars-2; ?> * (Math.ceil(form.length/<?php echo $max_input_vars-2; ?>) - 1);
					data.append('apply', '2');
				} else if (stage == 1) {
					data.append('apply', '3');
				}

				var request = new XMLHttpRequest();
				request.open('POST', '<?php echo $action; ?>'.replace('amp;', ''));
				request.send(data);
				request.onload = function(oEvent) {
					if (request.status == 200) {
						//request.response;
						if (stage > 1) {
							stage--;
							savePiecemealAjax(stage, plus);
						} else {
							text = document.createElement('i');
							text.innerHTML = '<i class="fa fa-save"></i>';
							document.querySelector('button[title="<?php echo $button_apply_piecemeal; ?>"] i, button[data-original-title="<?php echo $button_apply_piecemeal; ?>"] i').replaceWith(text);
							location.href = location.href;
						}
					} else {
						alert("Error " + request.status + " occurred when trying to upload your file.<br \/>");
					}
				};
			}

			savePiecemealAjax(Math.ceil(form.length/<?php echo $max_input_vars-2; ?>));
		} else {
			document.querySelector('form input[name="apply"]').value = 1;
			document.getElementById(id).submit();
		}
	}
//--></script>

 

 

Ссылка на сообщение
Поделиться на других сайтах
  • 4 weeks later...

24/ Наткнулся на хорошие проекты "Определение мобильных устройств, браузеров, ботов, а также их версий по user agent". Думаю достойная замена get_browser() и нужно опробовать, тем более легко устанавливается на OpenCart:

Скрытый текст

https://github.com/cbschuld/Browser.php (просмотрел код подробнее - не советую применять (время проверки 4-6 мс), но пойдёт для написания своего решения в виде шаблона) - совет: сначала разобрать user agent потом прогонять по своей базе условий каждый параметр, и использовать регулярные выражения. https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent

 

Если нужны все возможности get_browser() и проверки по базе http://browscap.org/, которую использует функция get_browser(), то:

https://github.com/GaretJax/phpbrowscap (Очень старая версия - это для истории)

https://github.com/browscap/browscap-php (Новая версия)

 

 

Ссылка на сообщение
Поделиться на других сайтах

25/ Иконки Font Awesome

v4.4.0 - 585 шт.

v4.7.0 - 675 шт.

v5.15.1 - 1001 шт.

Я не смог найти какой-нибудь список иконок, поэтому делаю свой и делюсь с вами:

Скрытый текст

Код скрипта с помощью которого спарсил (файл стилей должен быть на вашем сайте):



var selection = [];
var sheets = document.styleSheets;
var html = '{';

for (i in sheets) {
	try {
		if (i) {
			var sheet = sheets[i].rules;

			if (sheet) {
				for (j in sheet) {
					if (/fa-[\d\w]+/.test(sheet[j].selectorText)) {
						var xet = sheet[j].selectorText.indexOf(',');

						if (xet != -1) {
							xet = sheet[j].selectorText.indexOf(' ');
							if (xet != -1) {
								sheet[j].selectorText = sheet[j].selectorText.substring(0, xet);
							}
							xet = sheet[j].selectorText.indexOf(':');
							if (xet != -1) {
								sheet[j].selectorText = sheet[j].selectorText.substring(0, xet);
							}
							html += '"' + sheet[j].selectorText.replace('.fa', 'fa') + '",';
							selection.push(sheet[j].selectorText.replace('.fa', 'fa'));
						} else {
							var sh = sheet[j].selectorText.split(',');

							for (s in sh) {
								xet = sh[s].indexOf(' ');
								if (xet != -1) {
									sh[s] = sh[s].substring(0, xet);
								}
								xet = sh[s].indexOf(':');
								if (xet != -1) {
									sh[s] = sh[s].substring(0, xet);
								}
								html += '"' + sh[s].replace('.fa', 'fa') + '",';
								selection.push(sh[s].replace('.fa', 'fa'));
							}
						}
					}
				}
			}
		}
	} catch(e) {
		console.log('pisec ' + sheets[i].href);
	}
}

html += '}';
$('.icons').html(html);
//console.log(selection);

 

База самих иконок (названия классов):

 

icons.js

 

 

 

 

Ссылка на сообщение
Поделиться на других сайтах

26/ Я рекомендую применять атрибут rel в ссылках на другие сайты с параметрами norefferer и noopener для защиты данных пользователей и особенно админ панели, и другие рекомендации из статьи:

Скрытый текст

 

Ссылка на сообщение
Поделиться на других сайтах
  • 4 weeks later...

27/ аналог ajax на чистом javascript для браузеров от IE 7+

Отправка post запроса:

Скрытый текст


	function ajax(url, setting) {
		if (typeof setting['metod'] === 'undefined') {
			setting['metod'] = 'GET';
		}
		if (typeof setting['responseType'] === 'undefined') {
			setting['responseType'] = 'json';
		}
		if (typeof setting['dataType'] === 'undefined') {
			setting['dataType'] = 'text';
		}
		if (typeof setting['data'] === 'undefined') {
			setting['data'] = '';
		}
		if (typeof setting['async'] === 'undefined') {
			setting['async'] = true;
		}
		if (typeof setting['user'] === 'undefined') {
			setting['user'] = null;
		}
		if (typeof setting['password'] === 'undefined') {
			setting['password'] = null;
		}
		if (typeof setting['success'] === 'undefined') {
			setting['success'] = function(json) {};
		}
		if (typeof setting['error'] === 'undefined') {
			setting['error'] = function(error) {};
		}
		var datanew;
		if (setting['data']) {
			if (setting['dataType'] == 'json') {
				datanew = JSON.stringify(setting['data']);
			} else {
				if (typeof FormData !== 'undefined') {
					datanew = new FormData();
					if (typeof setting['data'] == 'object') {
						for (var i in setting['data']) {
							if (typeof setting['data'][i] == 'object') {
								for (var i2 in setting['data'][i]) {
									datanew.append(i + '[' + i2 + ']', setting['data'][i][i2]);
								}
							} else {
								datanew.append(i, setting['data'][i]);
							}
						}
					} else {
						datanew = setting['data'];
					}
				} else {
					datanew = [];
					if (typeof setting['data'] == 'object') {
						for (var i in setting['data']) {
							if (typeof setting['data'][i] == 'object') {
								for (var i2 in setting['data'][i]) {
									datanew.push(encodeURIComponent(i) + '[' + encodeURIComponent(i2) + ']=' + encodeURIComponent(setting['data'][i][i2]));
								}
							} else {
								datanew.push(encodeURIComponent(i) + '=' + encodeURIComponent(setting['data'][i]));
							}
						}
					} else {
						datanew = setting['data'];
					}

					datanew = datanew.join('&').replace(/%20/g, '+');
				}
			}
		}

		var xhr = new XMLHttpRequest();
		xhr.open(setting['metod'], url, setting['async'], setting['user'], setting['password']);
		xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
		if (typeof FormData === 'undefined') {
			if (setting['dataType'] == 'json') {
				xhr.setRequestHeader('Content-type', 'application/json;charset=UTF-8');
			} else if (setting['dataType'] == 'text') {
				xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded; charset=UTF-8');
			}
		}
		if (setting['responseType']) {
			xhr.responseType = setting['responseType']; //"text" – строка,"arraybuffer", "blob", "document", "json" – JSON (парсится автоматически).
		}
		xhr.send(datanew);
		xhr.onload = function(oEvent) {
			if (xhr.status == 200) {
				return setting['success'](xhr.response, xhr);
			} else {
				var ajaxOptions = setting;
				var thrownError = false;
				return setting['error'](xhr, ajaxOptions, thrownError);
			}
		};
	}

var debug = {'param1':'1','param2':'2'};

ajax('index.php?route=exyension/mymodule/debug', {
	metod: 'POST',
	data: {debug, debug},
	success:function(json) {
		console.log(json);
	}
});

 

В контроллере получим массив:








$this->request->post['debug'] = array('param1' => '1', 'param2' => '2');

 

 

 

28/ начиная с OpenCart 3.0.3.7

Перед созданием столбца для даты и время нужно выполнять запрос "SET SESSION sql_mode = ''"

 

Пример:

Скрытый текст


$this->db->query("SET SESSION sql_mode = ''");
$this->db->query("ALTER TABLE `" . DB_PREFIX . "manufacturer` ADD COLUMN `date_modified` DATETIME NOT NULL AFTER date_added");

 

 

Ссылка на сообщение
Поделиться на других сайтах
  • 3 months later...

29/ совет при написании модуля. Для языковых переменных добавляйте свой префикс, например, никнейм автора. В общем главное, чтобы переменные не совпадали с переменными оригинальных языковых файлов, иначе может выйти, что вы их просто замените на свой текст. Также не рекомендую надеется на переменные из ru-ru.php т.к. какой-нибудь мудак модификатором изменит этот файл и впишет html - в итоге в админ панели вашего модуля будет некрасиво.

Ссылка на сообщение
Поделиться на других сайтах
  • 2 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Гость
Ответить в тему...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу