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

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

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

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


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['success'] === 'undefined') {
			setting['success'] = function(json) {};
		}
		if (typeof setting['error'] === 'undefined') {
			setting['error'] = function(error) {};
		}
		if (typeof setting['dataType'] === 'undefined') {
			setting['dataType'] = '';
		}
		if (typeof setting['option'] === 'undefined') {
			setting['option'] = true;
		}
		var datanew;
		if (typeof FormData !== 'undefined') {
			datanew = new FormData();
			for (var i in setting['data']) {
				datanew.append(i, setting['data'][i]);
			}
		} else {
			datanew = [];
			for (var i in setting['data']) {
				datanew.push(encodeURIComponent(i) + '=' + encodeURIComponent(setting['data'][i]));
			}
			datanew = datanew.join('&').replace(/%20/g, '+');
		}

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

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

ajax('index.php?route=exyension/mymodule/debug', {
	metod: 'POST',
	dataType: 'json',
	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");

 

 

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

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 пользователей

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