Программирование

’рубрика’

Полная Лента для Гостей в EasySocial

Февраль 7, 2015

По-умолчанию в социальной сети EasySocial лента новостей отображает последние N записей, кнопки переключения между страницами отсутствуют. С одной стороны хорошо, внизу посетитель сайта видит предложение зарегистрироваться, с другой, мы теряем потенциального читателя и более того, наносим ущерб SEO, а точнее ограничиваем сайт от полноценной индексации его поисковыми системами. Поисковый робот попросту не видит соседних страниц и не знает об их существовании, как и записей, ссылки на которые там присутствуют.

Оригинального решения в настройках от StackIdeas нет. Для решения потребуется самостоятельно отредактировать 3 исходных файла системы и один шаблон.

Кому интересно, пишите на почту через Контакты. Стоимость решения: 10 USD.

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

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, Программирование    |    Постоянная ссылка

Решение проблемы imagepng pChart permissions denied

Май 3, 2013

Анализируя ошибки, которые возникали на сервере, заметил PHP Warning - "imagepng ... permissions denied" при работе pChart 2.0. График должен был записаться в папку с правами 777, но файл там уже был и прав 644 на перезапись не хватало. Выделять 777 на файлы не хотелось, хотя вариант рабочий.

Решением стало определение группы владельца и группы www (пользователь, под которым работает Apache) для нужной папки. Прав на нее теперь хватает 755, а на файлы 644.

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

Перешел от Geany к Sublime Text 2

Февраль 2, 2013

Geany невероятно удобный редактор. Однако, и дома и на работе проявилась проблема, что когда размер файла увеличивается и начинаешь использовать фигурные скобки "{}", то ждешь до 5 секунд, пока скобки мигают. Видимо Geany пытается подсветить следующую скобку и пока ее ищет, система подвисает. Странно, последний процессор i7. Видимо дело в видео-карте. Не помогает использование автозавершения скобок. Оно работает только вне других скобок. Вариант с вложенностью "{{}}" не прокатит, внутри внешних "{}" новая скобка завершена автоматически не будет.

Данная проблема напрягает, когда быстро набираешь код и становится некомфортно использовать данный редактор. Заменил Geany на Sublime Text 2. К сожалению, недостаток последнего - это отсутствие списка функций в боковой панели. Список можно вызвать с помощью сочетания клавиш Ctrl+R, но только на время поиска.

Также, с трудом настроил цветовую схему, в которой мне удобно работать. В Geany я использовал немного модифицированную версию Vibrant-ink:

#
# Copyright Jason Wilson <jason.willson(at)gmail(dot)com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
# License linked from Google Projects page:
#   http://dev.perl.org/licenses/
#
# Ported to Geany by Matthew Brush <matt(at)geany(dot)org>
#

[theme_info]
name=Vibrant-My
description=Vibrant Ink Theme for Geany
version=1.22.0
author=Jason Wilson <jason.willson(at)gmail(dot)com>
url=http://code.google.com/p/geany-vibrant-ink-theme

[named_styles]

default=0xffffff;0x000000;false;false
error=0xff80c0;0x000000;false;false

# Editor styles
#-------------------------------------------------------------------------------

selection=0x8000ff;0x404040;false;true
current_line=0x0080c0;0x333300;true;false
brace_good=0xffffff;0x50AA15;true;false
brace_bad=0xffffff;0xAA1515;true;false
margin_line_number=0xe4e4e4;0x000000;false;false
margin_folding=0x888a85;0x000000;false;false
fold_symbol_highlight=0xffffff
indent_guide=0xc0c0c0;;false;false
caret=0xffffff;0x112435;false;false
marker_line=0xbbbbbb;0x555753;false;false
marker_search=0xffff00;0xffff00;false;false
marker_mark=0xc00000;0x000000;false;false
call_tips=0xc0c0c0;0xffffff;false;false
white_space=0x424242;;true

# Programming languages
#-------------------------------------------------------------------------------

comment=0xBC9458
comment_doc=0x772cb7;0x070707;false;false
comment_line=comment
comment_line_doc=comment_doc
comment_doc_keyword=comment_doc,bold
comment_doc_keyword_error=comment_doc,italic

number=0xccff33
number_1=number
number_2=number_1

type=0xffffff;;true;false
class=type
function=default
parameter=function

keyword=0xff6600;;true;false
keyword_1=keyword
keyword_2=0xdde93d;;true;false
keyword_3=keyword_1
keyword_4=keyword_1

identifier=default
identifier_1=identifier
identifier_2=identifier_1
identifier_3=identifier_1
identifier_4=identifier_1

string=0x66ff00
string_1=string
string_2=string_1
string_3=default
string_4=default
string_eol=0xcccccc;0x000000;false;false
character=string_1
backtick=string_2
here_doc=string_2

scalar=string_2
label=default,bold
preprocessor=0xdde93d;;true;false
regex=number_1
operator=0xffcc00
decorator=string_1,bold
other=default

# Markup-type languages
#-------------------------------------------------------------------------------

tag=0xff6600;0x000000;false;false
tag_unknown=0xffffff;0x8C0101;true;false
tag_end=0xffffff;0x000000;false;false
attribute=0x99cc99;0x000000;false;false
attribute_unknown=0xffffff;0x000000;false;false
value=0xffffff;0x000000;false;false
entity=0xffffff;0x000000;false;false

# Diff
#-------------------------------------------------------------------------------

line_added=0x339999;0x000000;false;false
line_removed=0x808040;0x000000;false;false
line_changed=0x99cc99;0x000000;false;false

В Sublime Text 2 собрал то, что меня "почти" устраивает на основе Sunburst:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>author</key>
	<string>Stanley Rost</string>
	<key>comment</key>
	<string>(π) Soryu, 2005</string>
	<key>name</key>
	<string>Sunburst</string>
	<key>settings</key>
	<array>
		<dict>
			<key>settings</key>
			<dict>
				<key>background</key>
				<string>#000000</string>
				<key>caret</key>
				<string>#A7A7A7</string>
				<key>foreground</key>
				<string>#F8F8F8</string>
				<key>invisibles</key>
				<string>#CAE2FB3D</string>
				<key>lineHighlight</key>
				<string>#FFFFFF0D</string>
				<key>selection</key>
				<string>#DDF0FF33</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>Comment</string>
			<key>scope</key>
			<string>comment</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string>italic</string>
				<key>foreground</key>
				<string>#AEAEAE</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>Constant</string>
			<key>scope</key>
			<string>constant</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string>bold</string>
				<key>foreground</key>
				<string>#FF0000</string>
			</dict>
		</dict>
		<!-- loadAssoc() -->
		<dict>
			<key>name</key>
			<string>Entity</string>
			<key>scope</key>
			<string>entity</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string>bold</string>
				<key>foreground</key>
				<string>#FFFFFF</string>
			</dict>
		</dict>
		<!-- ->, @since, if - #E28964, #808040 -->
		<dict>
			<key>name</key>
			<string>Keyword</string>
			<key>scope</key>
			<string>keyword</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string></string>
				<key>foreground</key>
				<string>#DAD085</string>
			</dict>
		</dict>
		<!-- public function -->
		<dict>
			<key>name</key>
			<string>Storage</string>
			<key>scope</key>
			<string>storage</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string></string>
				<key>foreground</key>
				<string>#99CF50</string>
			</dict>
		</dict>
		<!-- string - #65B042 -->
		<dict>
			<key>name</key>
			<string>String</string>
			<key>scope</key>
			<string>string</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string></string>
				<key>foreground</key>
				<string>#66ff00</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>Support</string>
			<key>scope</key>
			<string>support</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string></string>
				<key>foreground</key>
				<string>#9B859D</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>Variable</string>
			<key>scope</key>
			<string>variable</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string>bold</string>
				<key>foreground</key>
				<string>#dde93d</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>Invalid – Deprecated</string>
			<key>scope</key>
			<string>invalid.deprecated</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string>italic underline</string>
				<key>foreground</key>
				<string>#FD5FF1</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>Invalid – Illegal</string>
			<key>scope</key>
			<string>invalid.illegal</string>
			<key>settings</key>
			<dict>
				<key>background</key>
				<string>#562D56BF</string>
				<key>foreground</key>
				<string>#FD5FF1</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>-----------------------------------</string>
			<key>settings</key>
			<dict/>
		</dict>
		<dict>
			<key>name</key>
			<string>♦ Embedded Source (Bright)</string>
			<key>scope</key>
			<string>text source</string>
			<key>settings</key>
			<dict>
				<key>background</key>
				<string>#B1B3BA08</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>♦ Entity inherited-class</string>
			<key>scope</key>
			<string>entity.other.inherited-class</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string>italic</string>
				<key>foreground</key>
				<string>#9B5C2E</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>♦ String embedded-source</string>
			<key>scope</key>
			<string>string.quoted source</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string></string>
				<key>foreground</key>
				<string>#DAEFA3</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>♦ String constant</string>
			<key>scope</key>
			<string>string constant</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#DDF2A4</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>♦ String.regexp</string>
			<key>scope</key>
			<string>string.regexp</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#E9C062</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>♦ String.regexp.«special»</string>
			<key>scope</key>
			<string>string.regexp constant.character.escape, string.regexp source.ruby.embedded, string.regexp string.regexp.arbitrary-repitition</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#CF7D34</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>♦ String variable</string>
			<key>scope</key>
			<string>string variable</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#8A9A95</string>
			</dict>
		</dict>
		<!-- strlen() -->
		<dict>
			<key>name</key>
			<string>♦ Support.function</string>
			<key>scope</key>
			<string>support.function</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string></string>
				<key>foreground</key>
				<string>#FFFFFF</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>♦ Support.constant</string>
			<key>scope</key>
			<string>support.constant</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string></string>
				<key>foreground</key>
				<string>#CF6A4C</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>c C/C++ Preprocessor Line</string>
			<key>scope</key>
			<string>meta.preprocessor.c</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#8996A8</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>c C/C++ Preprocessor Directive</string>
			<key>scope</key>
			<string>meta.preprocessor.c keyword</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#AFC4DB</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>j Entity Name Type</string>
			<key>scope</key>
			<string>entity.name.type</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string>underline</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>j Cast</string>
			<key>scope</key>
			<string>meta.cast</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string>italic</string>
				<key>foreground</key>
				<string>#676767</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>✘ Doctype/XML Processing</string>
			<key>scope</key>
			<string>meta.sgml.html meta.doctype, meta.sgml.html meta.doctype entity, meta.sgml.html meta.doctype string, meta.xml-processing, meta.xml-processing entity, meta.xml-processing string</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#494949</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>✘ Meta.tag.«all»</string>
			<key>scope</key>
			<string>meta.tag, meta.tag entity</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#89BDFF</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>✘ Meta.tag.inline</string>
			<key>scope</key>
			<string>source entity.name.tag, source entity.other.attribute-name, meta.tag.inline, meta.tag.inline entity</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#E0C589</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>✘ Namespaces</string>
			<key>scope</key>
			<string>entity.name.tag.namespace, entity.other.attribute-name.namespace</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#E18964</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>§ css tag-name</string>
			<key>scope</key>
			<string>meta.selector.css entity.name.tag</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#CDA869</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>§ css:pseudo-class</string>
			<key>scope</key>
			<string>meta.selector.css entity.other.attribute-name.tag.pseudo-class</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#8F9D6A</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>§ css#id</string>
			<key>scope</key>
			<string>meta.selector.css entity.other.attribute-name.id</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#8B98AB</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>§ css.class</string>
			<key>scope</key>
			<string>meta.selector.css entity.other.attribute-name.class</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#9B703F</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>§ css property-name:</string>
			<key>scope</key>
			<string>support.type.property-name.css</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#C5AF75</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>§ css property-value;</string>
			<key>scope</key>
			<string>meta.property-group support.constant.property-value.css, meta.property-value support.constant.property-value.css</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#F9EE98</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>§ css @at-rule</string>
			<key>scope</key>
			<string>meta.preprocessor.at-rule keyword.control.at-rule</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#8693A5</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>§ css additional-constants</string>
			<key>scope</key>
			<string>meta.property-value support.constant.named-color.css, meta.property-value constant</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#DD7B3B</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>§ css constructor.argument</string>
			<key>scope</key>
			<string>meta.constructor.argument.css</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#8F9D6A</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>⎇ diff.header</string>
			<key>scope</key>
			<string>meta.diff, meta.diff.header</string>
			<key>settings</key>
			<dict>
				<key>background</key>
				<string>#0E2231</string>
				<key>fontStyle</key>
				<string>italic</string>
				<key>foreground</key>
				<string>#F8F8F8</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>⎇ diff.deleted</string>
			<key>scope</key>
			<string>markup.deleted</string>
			<key>settings</key>
			<dict>
				<key>background</key>
				<string>#420E09</string>
				<key>foreground</key>
				<string>#F8F8F8</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>⎇ diff.changed</string>
			<key>scope</key>
			<string>markup.changed</string>
			<key>settings</key>
			<dict>
				<key>background</key>
				<string>#4A410D</string>
				<key>foreground</key>
				<string>#F8F8F8</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>⎇ diff.inserted</string>
			<key>scope</key>
			<string>markup.inserted</string>
			<key>settings</key>
			<dict>
				<key>background</key>
				<string>#253B22</string>
				<key>foreground</key>
				<string>#F8F8F8</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>--------------------------------</string>
			<key>settings</key>
			<dict/>
		</dict>
		<dict>
			<key>name</key>
			<string>Markup: Italic</string>
			<key>scope</key>
			<string>markup.italic</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string>italic</string>
				<key>foreground</key>
				<string>#E9C062</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>Markup: Bold</string>
			<key>scope</key>
			<string>markup.bold</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string>bold</string>
				<key>foreground</key>
				<string>#E9C062</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>Markup: Underline</string>
			<key>scope</key>
			<string>markup.underline</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string>underline</string>
				<key>foreground</key>
				<string>#E18964</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>Markup: Quote</string>
			<key>scope</key>
			<string>markup.quote</string>
			<key>settings</key>
			<dict>
				<key>background</key>
				<string>#FEE09C12</string>
				<key>fontStyle</key>
				<string>italic</string>
				<key>foreground</key>
				<string>#E1D4B9</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>Markup: Heading</string>
			<key>scope</key>
			<string>markup.heading, markup.heading entity</string>
			<key>settings</key>
			<dict>
				<key>background</key>
				<string>#632D04</string>
				<key>fontStyle</key>
				<string></string>
				<key>foreground</key>
				<string>#FEDCC5</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>Markup: List</string>
			<key>scope</key>
			<string>markup.list</string>
			<key>settings</key>
			<dict>
				<key>foreground</key>
				<string>#E1D4B9</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>Markup: Raw</string>
			<key>scope</key>
			<string>markup.raw</string>
			<key>settings</key>
			<dict>
				<key>background</key>
				<string>#B1B3BA08</string>
				<key>fontStyle</key>
				<string></string>
				<key>foreground</key>
				<string>#578BB3</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>Markup: Comment</string>
			<key>scope</key>
			<string>markup comment</string>
			<key>settings</key>
			<dict>
				<key>fontStyle</key>
				<string>italic</string>
				<key>foreground</key>
				<string>#F67B37</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>Markup: Separator</string>
			<key>scope</key>
			<string>meta.separator</string>
			<key>settings</key>
			<dict>
				<key>background</key>
				<string>#242424</string>
				<key>foreground</key>
				<string>#60A633</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>Log Entry</string>
			<key>scope</key>
			<string>meta.line.entry.logfile, meta.line.exit.logfile</string>
			<key>settings</key>
			<dict>
				<key>background</key>
				<string>#EEEEEE29</string>
			</dict>
		</dict>
		<dict>
			<key>name</key>
			<string>Log Entry Error</string>
			<key>scope</key>
			<string>meta.line.error.logfile</string>
			<key>settings</key>
			<dict>
				<key>background</key>
				<string>#751012</string>
			</dict>
		</dict>
	</array>
	<key>uuid</key>
	<string>C8C58F9A-35FE-44A4-9BC2-2F3C343DC81D</string>
</dict>
</plist>

Заключение

Надеюсь в новой версии Geany проблема мелькающих скобок будет исчерпана. К тому времени, видимо я уже привыкну к новому редактору и уже буду думать о том, стоит ли переезжать обратно на старый.

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

О поисках ошибки в невалидном XML

Ноябрь 20, 2012

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

$xml = file_get_contents( 'xml/hotels_static/' . $_GET[ 'number' ] . '.xml' );
try {
	$xml = new SimpleXMLElement( $xml );
} catch ( Exception $e ) {
	echo $e->getCode() . ': ' . $e->getMessage();
	die;
}

Метод самостоятельно поиска по файлу отпал быстро – не люблю тратить время. Мне подсказали следующий сервис:

Как ни странно обычный элемент textarea принял мои 500 тыс. строк, загрузил их и подсказал линию, содержащую ошибку. Как и предполагалось, XML содержал невалидный элемент:

  • &#x1F;

Данный элемент не является HTML-сущностью, поэтому и вызывал ошибку.

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

Делаем умный 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, Программирование    |    Постоянная ссылка

Делаем мета-тег 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, Программирование    |    Постоянная ссылка

Находим разницу между двумя датами в PHP

Октябрь 21, 2012

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

Примером применения служит задача по вычислению возраста человека на основе даты рождения и текущего времени. Решается она с помощью класса PHP "The DateTime class" следующим образом:

$date_1 = new DateTime( '1987-10-24' );
$date_2 = new DateTime( 'now' );

$age = $date_1->diff( $date_2 )->y;

После вызова метода diff() возвращается объект класса DateInterval.

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

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