Joomla

’рубрика’

Remember Me для Админ зоны в Joomla 3.2.3

Апрель 5, 2014

Вот это самое интересное и с бубном пришлось плясать очень долго. Но цель достигнута - можно хоть выключить браузер и компьютер, при следующем включении вы будете авторизованы в Администраторе, как если бы вы были на пользовательской части сайта.

Сразу отмечу, что ни один плагин в Интернете не позволяет достигнуть аналогичного результата. Причем в моем случае почему-то даже стандартный KeepAlive не работал, сессия слетала и я переключался на форму логина каждые 15 минут (время жизни сессии по настройкам конфига). Сейчас даже если и сессия слетела, она восстановится. ОК, к делу.

Все правки в коде ищите по комментарию "// cay127".

Файл plugins/authentication/cookie/cookie.php

Правки ниже:

public function onUserAuthenticate($credentials, $options, &$response)
{
	// No remember me for admin
	if ($this->app->isAdmin())
	{
		// cay127
		// return false;
	}

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

Это не все, но объяснять уже не буду, слишком долго.

public function onUserAfterLogout($options)
{
	// No remember me for admin
	if ($this->app->isAdmin())
	{
		// cay127
		// return false;
	}

Далее еще. Кусок кода большой, нужно развернуть:

public function onUserAfterLogin($options)
{
	// No remember me for admin
	if ($this->app->isAdmin())
	{
		// cay127
		$options[ 'remember' ] = 1;
		// return false;
	}

	if (isset($options['responseType']) && $options['responseType'] == 'Cookie')
	{
		// Logged in using a cookie
		$cookieName		= JUserHelper::getShortHashedUserAgent();

		// We need the old data to get the existing series
		$cookieValue	= $this->app->input->cookie->get($cookieName);
		$cookieArray	= explode('.', $cookieValue);
		// Filter series since we're going to use it in the query
		$filter			= new JFilterInput;
		$series			= $filter->clean($cookieArray[1], 'ALNUM');
	}
	elseif (!empty($options['remember']))
	{
		// Remember checkbox is set
		$cookieName		= JUserHelper::getShortHashedUserAgent();

		// Create an unique series which will be used over the lifespan of the cookie
		$unique = false;
		do
		{
			$series = JUserHelper::genRandomPassword(20);

			$query = $this->db->getQuery(true)
				->select($this->db->quoteName('series'))
				->from($this->db->quoteName('#__user_keys'))
				->where($this->db->quoteName('series') . ' = ' . $this->db->quote($series));

			$results = $this->db->setQuery($query)->loadResult();

			if (is_null($results))
			{
				$unique = true;
			}
		}
		while ($unique === false);
	}
	else
	{
		return false;
	}

	// Get the parameter values
	$lifetime	= $this->params->get('cookie_lifetime', '60') * 24 * 60 * 60;
	$length		= $this->params->get('key_length', '16');

	// Generate new cookie
	$token		= JUserHelper::genRandomPassword($length);
	$cookieValue = $token . '.' . $series;

	// Overwrite existing cookie with new value
	$this->app->input->cookie->set(
		$cookieName, $cookieValue, time() + $lifetime, $this->app->get('cookie_path', '/'), $this->app->get('cookie_domain'), $this->app->isSSLConnection()
	);

	$query = $this->db->getQuery(true);

	// cay127
	$query = 'SELECT ' . $this->db->quoteName('id');
	$query .= ' FROM ' . $this->db->quoteName('#__user_keys');
	$query .= ' WHERE ' . $this->db->quoteName('series') . ' = ' . $this->db->quote($series);
	$query .= ' AND ' . $this->db->quoteName('user_id') . ' = ' . $this->db->quote($options['user']->username);

	$results = $this->db->setQuery($query)->loadObjectList();
	if (count($results) !== 0) {
		$b_update = true;
	} else {
		$b_update = false;
	}

	$query = $this->db->getQuery(true);
	// cay127 #

	// cay127
	// if (!empty($options['remember']))
	if ($b_update == false AND !empty($options['remember']))
	{
		// Create new record
		$query
			->insert($this->db->quoteName('#__user_keys'))
			->set($this->db->quoteName('user_id') . ' = ' . $this->db->quote($options['user']->username))
			->set($this->db->quoteName('series') . ' = ' . $this->db->quote($series))
			->set($this->db->quoteName('uastring') . ' = ' . $this->db->quote($cookieName))
			->set($this->db->quoteName('time') . ' = ' . (time() + $lifetime));
	}
	else
	{
		// Update existing record with new token
		$query
			->update($this->db->quoteName('#__user_keys'))
			->where($this->db->quoteName('user_id') . ' = ' . $this->db->quote($options['user']->username))
			->where($this->db->quoteName('series') . ' = ' . $this->db->quote($series))
			->where($this->db->quoteName('uastring') . ' = ' . $this->db->quote($cookieName));
	}

	$hashed_token	= JUserHelper::hashPassword($token);
	$query
		->set($this->db->quoteName('token') . ' = ' . $this->db->quote($hashed_token));

	$this->db->setQuery($query)->execute();

	return true;
}

Файл libraries/cms/application/administrator.php

И теперь самое, что может показаться странным, но джумла почему-то делает язык английским после окончания срока действия временной куки на час. Исправляем и это.

protected function initialiseApp($options = array())
{
	// cay127
	if ( $options[ 'language' ] == 'en-GB' ) {
		$options[ 'language' ] = '';
	}

В заключение

Все, можно наконец-то заняться кодингом, просмотром авто-обновлений статистики в режиме live и не переживать ни о чем.

Update 2014-04-06 

Что-то включил сегодня компьютер после сна и попал на форму логина:( Надеюсь не последнее добавление с языком повлияло таким образом, что меня стало выкидывать. Подумаю еще...

Update 2014-08-25 

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

plugins/system/remember/remember.php

1-е изменение:

public function onAfterInitialise()
{
	// No remember me for admin.
	if ($this->app->isAdmin())
	{
		// cay127
		// return;
	}

и 2-е:

public function onUserLogout($user, $options)
{
	// No remember me for admin
	if ($this->app->isAdmin())
	{
		// cay127
		// return true;

Короче всегда ищем "No remember me for admin" в консоли через grep и спасибо разработчикам за комментарии.

Tags: , , ,
Записано в Joomla, Программирование    |    Постоянная ссылка

Решение проблем с NGINX, IP и HTTPS в Joomla 3.2.3

Апрель 5, 2014

Этот пост будет первым из серии "Танцы с бубном в Joomla". Почему так? Несмотря на все достоинства среды, возникают проблемы, которые приходится внедрять с помощью внедрения своего кода в начинку самой джумлы, что не есть хорошо, но все же. Собственно это и есть одна из причин поста - сохранить изменения. В коде помечаю такие места с помощью комментария "// cay127" и команда grep мне всегда поможет быстро найти все, что было изменено вручную.

И еще пару слов - да, где-то может плагин был бы лучшим решением, может где-то не докопал до истины, но просто экономил время. Плагин отмечу - не всегда применим и это скоро станет понятно.

После вступления

Проблема вот в чем - джумла строит ссылки следующим образом (автоматически):

$theURI = 'http' . $https . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];

Но у меня переменная $_SERVER['HTTP_HOST'] содержит IP адрес, а не название домена вовсе. Т.о. быстрое решение стало следующим: правим файл:

libraries/joomla/uri/uri.php

точка опоры:

public static function getInstance($uri = 'SERVER')

Без лишних подробностей изменения (помним о комментарии):

public static function getInstance($uri = 'SERVER')
{
	if (empty(self::$instances[$uri]))
	{
		// Are we obtaining the URI from the server?
		if ($uri == 'SERVER')
		{
			// Determine if the request was over SSL (HTTPS).
			// cay127
			if (true OR sset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && (strtolower($_SERVER['HTTPS']) != 'off'))
			{
				$https = 's://';
			}
			else
			{
				$https = '://';
			}

			/*
			 * Since we are assigning the URI from the server variables, we first need
			 * to determine if we are running on apache or IIS.  If PHP_SELF and REQUEST_URI
			 * are present, we will assume we are running on apache.
			 */

			if (!empty($_SERVER['PHP_SELF']) && !empty($_SERVER['REQUEST_URI']))
			{
				// To build the entire URI we need to prepend the protocol, and the http host
				// to the URI string.

				// cay127
				// $theURI = 'http' . $https . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
				// [HTTP_HOST] => 212.XX.XX.XX
				$theURI = 'http' . $https . 'www.site.ru' . $_SERVER['REQUEST_URI'];
			}

Реальный IP и домен скрыты, но суть ясна. Плюс прописал условие всегда использовать только HTTPS.

Tags: , , , , , ,
Записано в Joomla, Программирование    |    Постоянная ссылка

О выходе книги "Joomla! Programming" (Joomla! Press) на русском

Декабрь 9, 2012

Ожидалось, что книга выйдет в 4-м квартале 2012 года. Сейчас на сайте издательства "Вильямс" срок обозначен, как 1-й квартал 2013. Печально, срок переносится, а в live уже выходит 3 версия Jooml'ы. В книге речь пойдет о принципах программирования под версию 2.5. Таким образом книга уже отстает на одно поколение, хотя и будет единственной переведенной в тематике "программирование под Jooml'у".

Несмотря на перенос сроков, книга стоящая, оригинал я прочитал почти дважды.

Записано в Joomla    |    Постоянная ссылка

Делаем умный alt у изображений в Virtuemart 2.0.12f

Ноябрь 14, 2012

Как я отметил, если "Alt текст изображения" в товаре для изображения не задается, то за описание видимо берется имя файла. К примеру:

  • название файла: paljto-s-shirokim-vorotnikom-na-pugovicakh-c02011.jpg
  • атрибут alt: paljto-s-shirokim-vorotnikom-na-pugovicakh-c02011

Идея понятна, таким alt быть не должен. Я считаю, если alt не задан, то он должен быть равным "названию товара + артикул" (у меня на сайте по-умолчанию артикул включен в название товара).

Virtuemart в функции displayMediaThumb() использует следующий алгоритм:

if(empty($this->file_meta)){
	if(!empty($this->file_description)){
		$file_alt = $this->file_description;
	} else if(!empty($this->file_name)) {
		$file_alt = $this->file_name;
	} else {
		$file_alt = '';
	}
} else {
	$file_alt = $this->file_meta;
}

В случае заданного "Alt текст изображения", значение хранится в переменной $this->file_meta. В ином случае в переменную попадает описание (file_description) или название файла (file_name). Я не стал разбираться что есть описание файла, поэтому к делу.

Сделаем пустой alt равным "названию товара + артикул". В примере перезапишем шаблон страницы товара. Точнее его подшаблон для отображения изображений по товару.

Переместим данный файл:

/components/com_virtuemart/views/productdetails/tmpl/default_images.php

в папку с используемым шаблоном:

/templates/kroko/html/com_virtuemart/productdetails/

Открываем файл на редактирование и заменяем:

// Product Main Image
if (!empty($this->product->images[0])) {

на:

// Product Main Image
if (!empty($this->product->images[0])) {
	if ( empty( $this->product->images[0]->file_meta ) ) {
		$this->product->images[0]->file_meta = $this->product->product_name;
	}

Теперь при вызове функции displayMediaThumb() атрибут alt будет равным названию товара (в моем случае включая артикул).

Tags: , ,
Записано в Joomla, PHP, Программирование    |    Постоянная ссылка

Определяем в коде путь до папки с шаблоном в Joomla 2.5.7

Ноябрь 10, 2012

Программно добраться до папки с шаблоном элементарно:

$app = JFactory::getApplication();
$tpl_path = JURI::base() . 'templates/' .$app->getTemplate() . '/';

Tags:
Записано в Joomla, PHP, Программирование    |    Постоянная ссылка

Как снять товар с продажи в новом Virtuemart 2.0.12f

Ноябрь 7, 2012

Никогда бы не подумал, что это может стать проблемой. Как и в ранней версии Virtuemart установил количество товара на складе на "0", а товар остался доступен к покупке. Так как поиски я начинаю всегда с кода, то как я ни копал, данное условие не срабатывало:

$stockhandle = VmConfig::get( 'stockhandle', 'none' );
if ( ( $stockhandle == 'disableit' or $stockhandle == 'disableadd' ) and ( $this->product->product_in_stock - $this->product->product_ordered ) < 1) {
	...
}

Стал копать в Интернете и увидел рекомендацию "enable Check Stock". Ок, но это еще нужно найти в настройках Virtuemart.

Решение выглядит так:

  • переходим: Virtuemart --> Инструменты --> Панель управления --> Настройки
  • перемещаемся на вкладку "Внешний вид"
  • отмечаем в блоке "Действие при поступлении товара на склад" - "Показывать 'Известить меня' вместо кнопки 'Добавить в корзину'"

Теперь указанное условие сработает.

Tags: , ,
Записано в Joomla    |    Постоянная ссылка

Делаем мета-тег description равным краткому описанию товара в Virtuemart 2.0.12f

Ноябрь 7, 2012

В Virtuemart под Joomla 1.0.15 по-умолчанию на странице подробностей товара "Краткое описание" становилось мета-тегом description. HTML-теги из описания вырезались, как и символ новой строки. В новой версии Virtuemart как видно, мета-тег description = описанию сайта, что неверно. Для того, чтобы повторяющиеся мета-описания не стали ошибками в Веб-мастерах Google и Яндекс необходимо изменить шаблон страницы товара Virtuemart.

Перезапишем следующий файл:

/components/com_virtuemart/views/productdetails/tmpl/default.php

Для этого скопируем данный файл в папку с используемым шаблоном сайта. В моем случае шаблон называется "kroko":

/templates/kroko/html/com_virtuemart/productdetails/default.php

В каждой создаваемой папке также должен быть пустой html-файл "index.html".

Открываем скопированный файл и добавляем сверху:

if ( empty( $this->product->product_s_desc ) == false ) {
	$document->setMetaData( 'description', $this->product->product_s_desc );
}

Все. Теперь достаточно задать для товара "Краткое описание" и проверить исходный код страницы. Должно быть так:

<meta name="description" content="..." />

Кстати "Краткое описание" в самом шаблоне Virtuemart (тот же файл default.php) я удалил, чтобы текст не дублировался.

Tags: , ,
Записано в Joomla, PHP, Программирование    |    Постоянная ссылка

Ошибка при установке демо-данных в Virtuemart 2.0.12f

Ноябрь 5, 2012

Ошибка выглядит следующим образом:

JInstaller::install: /var/www/kroko/administrator/components/com_virtuemart/install/install_sample_data.sql Ошибка SQL Ошибка базы данных с номером 1054 
Unknown column 'product_price_vdate' in 'field list' SQL=-- -- Dumping data for table `B4kn1_virtuemart_product_prices` -- INSERT INTO `B4kn1_virtuemart_product_prices` (`virtuemart_product_price_id`, `virtuemart_product_id`, `product_price`, `override`, `product_override_price`, `product_tax_id`, `product_discount_id`, `product_currency`, `product_price_vdate`, `product_price_edate`, `virtuemart_shoppergroup_id`, `price_quantity_start`, `price_quantity_end`) VALUES (1, 5, '24.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0), (2, 1, '4.49000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0), (3, 2, '39.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0), (4, 3, '24.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0), (5, 4, '17.99000', 1, '77.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0), (6, 6, '4.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0), (7, 7, '149.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0), (8, 8, '220.90000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0), (9, 9, '48.12000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0), (10, 10, '74.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0), (11, 11, '2.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0), (12, 12, '14.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0), (13, 13, '79.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0), (14, 14, '49.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0), (15, 15, '59.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0), (16, 16, '3.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0);
SQL =
--
-- Dumping data for table `#__virtuemart_product_prices`
--

INSERT INTO `#__virtuemart_product_prices` (`virtuemart_product_price_id`, `virtuemart_product_id`, `product_price`, `override`, `product_override_price`, `product_tax_id`, `product_discount_id`, `product_currency`, `product_price_vdate`, `product_price_edate`, `virtuemart_shoppergroup_id`, `price_quantity_start`, `price_quantity_end`) VALUES
(1, 5, '24.99000', 0, '0.00000', NULL, NULL, '144', 0, 0,  NULL, 0, 0),
(2, 1, '4.49000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0),
(3, 2, '39.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0),
(4, 3, '24.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0),
(5, 4, '17.99000', 1, '77.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0),
(6, 6, '4.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0),
(7, 7, '149.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0),
(8, 8, '220.90000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0),
(9, 9, '48.12000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0),
(10, 10, '74.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0),
(11, 11, '2.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0),
(12, 12, '14.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0),
(13, 13, '79.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0),
(14, 14, '49.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0),
(15, 15, '59.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0),
(16, 16, '3.99000', 0, '0.00000', NULL, NULL, '144', 0, 0, NULL, 0, 0);
vmError: Problems execution of SQL File /var/www/kroko/administrator/components/com_virtuemart/install/install_sample_data.sql

Будем считать, что явно что-то нехорошее. Однако, решение простое. Переходим в данную папку:

/administrator/components/com_virtuemart/install

и открываем файл: install_sample_data.sql

В файле заменяем:

  1. product_price_vdate --> to product_price_publish_up
  2. product_price_edate --> to product_price_publish_down

Сохраняем файл и:

  1. переходим Компоненты - Virtuemart - Инструменты - Панель управления - Настройки.
  2. Отмечаем "Обновление базы данных" и сохраняем изменения.
  3. переходим в "Инструменты и миграция".
  4. нажимаем "Восстановить таблицы и установить демо данные".
Видим сообщение об успешном обновлении:
Dropped virtuemart table adminmenuentries, calc_categories, calc_countries, calc_shoppergroups, calc_states, calcs, categories, categories_ru_ru, category_categories, category_medias, configs, countries, coupons, currencies, customs, invoices, manufacturer_medias, manufacturercategories, manufacturercategories_ru_ru, manufacturers, manufacturers_ru_ru, medias, migration_oldtonew_ids, modules, order_calc_rules, order_histories, order_items, order_userinfos, orders, orderstates, paymentmethod_shoppergroups, paymentmethods, paymentmethods_ru_ru, permgroups, product_categories, product_customfields, product_manufacturers, product_medias, product_prices, product_relations, product_shoppergroups, products, products_ru_ru, rating_reviews, rating_votes, ratings, shipmentmethod_shoppergroups, shipmentmethods, shipmentmethods_ru_ru, shoppergroups, states, userfield_values, userfields, userinfos, vendor_medias, vendors, vendors_ru_ru, vmuser_shoppergroups, vmusers, waitingusers, worldzones
System succesfull restored and sampledata installed, user id of the mainvendor is 990
Info: created table B4kn1_virtuemart_products_ru_ru
Info: created table B4kn1_virtuemart_vendors_ru_ru
Info: created table B4kn1_virtuemart_categories_ru_ru
Info: created table B4kn1_virtuemart_manufacturers_ru_ru
Info: created table B4kn1_virtuemart_manufacturercategories_ru_ru
Info: created table B4kn1_virtuemart_paymentmethods_ru_ru
Info: created table B4kn1_virtuemart_shipmentmethods_ru_ru
Info: Данные сохранились
Info: Данные сохранены
Info: Демо-данные установлены!
Info: Taking config from file

Ссылки по теме

Tags: ,
Записано в Joomla    |    Постоянная ссылка

Отключение magic quotes gpc через .htaccess

Октябрь 28, 2012

Для установки Joomla 3.0.1 необходимо отключить "magic quotes gpc".

Добавляем в файл .htaccess следующую директиву:

php_flag magic_quotes_gpc off

Tags: ,
Записано в Joomla, PHP    |    Постоянная ссылка

Активация mod_rewrite в Ubuntu LAMP

Август 18, 2012

Открываем файл:

/etc/apache2/sites-available/default

Заменяем "AllowOverride None" на "AllowOverride All" для нужной директории.

Выполняем команды в терминале:

sudo a2enmod rewrite
sudo /etc/init.d/apache2 restart

Теперь можно использовать файл .htaccess и директиву "RewriteEngine On".

Tags: , , ,
Записано в Joomla, Ubuntu    |    Постоянная ссылка