Так сложилось, что мне понадобилось использовать сервер MySQL 4.1, тогда как в моей версии Denwer уже стоит версия 5.5, которая и загружается при запуске Denwer. Что делаем? Ставим на машину версию 4.1, запускаем её из командной строки (net start MySQL) - и после этого запускаем Denwer. Он видит, что MySQL уже запущен - и вуаля, не запускает более новую версию. Дело закрыто.
суббота, 10 мая 2014 г.
пятница, 9 мая 2014 г.
Zend Optimizer. Fatal error: This encoded file is corrupted.
Предыстория. Делаю я себе тихо-мирно локальную копию сайта. Несколько файлов оказываются ужаты Zend Optimizer'ом. ОК, ставлю на свою машину этот Optimizer. А он, животное безобразное, ругается Fatal error: This encoded file is corrupted.
Что делать? А оказывается, надо было эти ужатые файлы качать не как обычно, а в бинарном режиме. В Filezilla, например, он настраивается так: Основное меню - Передача - Режим передачи - Двоичный. Обратите внимание, что в таком режиме эти файлы будут весить чуть-чуть побольше, чем когда вы их качали в режиме по умолчанию.
Соответственно, и заливать на сервер такие файлы нужно ровно так же, в бинарном режиме. И будет вам счастье. Ура, товарищи.
Что делать? А оказывается, надо было эти ужатые файлы качать не как обычно, а в бинарном режиме. В Filezilla, например, он настраивается так: Основное меню - Передача - Режим передачи - Двоичный. Обратите внимание, что в таком режиме эти файлы будут весить чуть-чуть побольше, чем когда вы их качали в режиме по умолчанию.
Соответственно, и заливать на сервер такие файлы нужно ровно так же, в бинарном режиме. И будет вам счастье. Ура, товарищи.
среда, 22 января 2014 г.
jQuery Chosen и Bootstrap Modal
Внезапно вылез глюк: красивые выпадающие списки плагина Chosen, в отличие от обычного select'а, при работе в модальном окне вместо того, чтобы выпадать за его пределы, выпадают внутри этого окна и создают некрасивый и неудобный длинный скроллбар. Лечится следующим образом:
$(selector).on('chosen:showing_dropdown',function(){ //selector - идентификатор вашего изначального select'a, к которому вы применили .chosen()
$('.modal-body').css('overflow','visible');
$('.modal-body .tab-content').css('overflow','visible'); //у меня в модальном окне были ещё и вкладки, так что пришлось и о них подумать
});
$(selector).on('chosen:hiding_dropdown',function(){
$('.modal-body').css('overflow','auto');
});
Побочный эффект - если у вас в модальном окне изначально был скроллбар, он будет пропадать при появлении выпадающего списка, и всё содержимое будет чуть подёргиваться. Для меня это не очень важно, но если найдёте способы этого избежать, дайте знать :)
$(selector).on('chosen:showing_dropdown',function(){ //selector - идентификатор вашего изначального select'a, к которому вы применили .chosen()
$('.modal-body').css('overflow','visible');
$('.modal-body .tab-content').css('overflow','visible'); //у меня в модальном окне были ещё и вкладки, так что пришлось и о них подумать
});
$(selector).on('chosen:hiding_dropdown',function(){
$('.modal-body').css('overflow','auto');
});
Побочный эффект - если у вас в модальном окне изначально был скроллбар, он будет пропадать при появлении выпадающего списка, и всё содержимое будет чуть подёргиваться. Для меня это не очень важно, но если найдёте способы этого избежать, дайте знать :)
среда, 28 августа 2013 г.
Wordpress: считаем комментарии Вконтакте
Как-то раз заказчику попала вожжа под мантию и он возжелал вместо обычных комментариев Вордпресса поставить комментарии Вконтакте. И, разумеется, чтобы у постов были счётчики этих самых комментариев. ОК, поехали.
1) Допустим, мы уже убрали стандартные комментарии и поставили контактовские в том же файле comments.php. Дальше после вызова комментариев контакта ставим следующий код (я по привычке использую jQuery):
VK.Observer.subscribe("widgets.comments.new_comment", function f(num, last_comment)
{
$.ajax({
type: "post",
url: "<?=admin_url('admin-ajax.php')?>",
data: "action=vk_comments_num&nonce="+"<?=wp_create_nonce('ajax-nonce')?>"+"&num="+num+"&id=<?=get_the_ID();?>",
success: function(data){
}
});
});
VK.Observer.subscribe("widgets.comments.delete_comment", function f1(num, last_comment)
{
/* ровно то же самое, что в первом блоке */
});
2) Итак, мы повесили обработчик на добавление комментария и такой же обработчик на удаление. Число комментариев к посту (переменная num) и id этого самого поста отправляются ajax'ом на сервер, где должно произойти действие vk_comments_num. К этому действию нам надо привязать свою функцию. Для этого идём в functions.php и ставим туда следующий код:
add_action('wp_ajax_vk_comments_num', 'vk_comments_num');
add_action('wp_ajax_nopriv_vk_comments_num', 'vk_comments_num');
/* не забываем, что нашу функцию надо привязать к двум действиям - для авторизованного пользователя и гостя */
function vk_comments_num()
{
// проверка, с нашего ли сервера пришёл запрос
$nonce = $_POST['nonce'];
if ( ! wp_verify_nonce( $nonce, 'ajax-nonce' ) )
die ( 'Busted!');
if(isset($_POST['id']))
{
// сохраняем количество комментарием в мета данные
update_post_meta($_POST['id'], 'vk_comments', $_POST['num'] );
echo $_POST['num'];
exit();
}
}
3) Теперь остаётся только выводить наш счётчик там, где это необходимо. Делается это просто:
<?php
$comments_num = get_post_meta( get_the_ID(), 'vk_comments', true ); // получите
echo 'Комментарии ('.$comments_num.')'; // и распишитесь
?>
1) Допустим, мы уже убрали стандартные комментарии и поставили контактовские в том же файле comments.php. Дальше после вызова комментариев контакта ставим следующий код (я по привычке использую jQuery):
VK.Observer.subscribe("widgets.comments.new_comment", function f(num, last_comment)
{
$.ajax({
type: "post",
url: "<?=admin_url('admin-ajax.php')?>",
data: "action=vk_comments_num&nonce="+"<?=wp_create_nonce('ajax-nonce')?>"+"&num="+num+"&id=<?=get_the_ID();?>",
success: function(data){
}
});
});
VK.Observer.subscribe("widgets.comments.delete_comment", function f1(num, last_comment)
{
/* ровно то же самое, что в первом блоке */
});
2) Итак, мы повесили обработчик на добавление комментария и такой же обработчик на удаление. Число комментариев к посту (переменная num) и id этого самого поста отправляются ajax'ом на сервер, где должно произойти действие vk_comments_num. К этому действию нам надо привязать свою функцию. Для этого идём в functions.php и ставим туда следующий код:
add_action('wp_ajax_vk_comments_num', 'vk_comments_num');
add_action('wp_ajax_nopriv_vk_comments_num', 'vk_comments_num');
/* не забываем, что нашу функцию надо привязать к двум действиям - для авторизованного пользователя и гостя */
function vk_comments_num()
{
// проверка, с нашего ли сервера пришёл запрос
$nonce = $_POST['nonce'];
if ( ! wp_verify_nonce( $nonce, 'ajax-nonce' ) )
die ( 'Busted!');
if(isset($_POST['id']))
{
// сохраняем количество комментарием в мета данные
update_post_meta($_POST['id'], 'vk_comments', $_POST['num'] );
echo $_POST['num'];
exit();
}
}
3) Теперь остаётся только выводить наш счётчик там, где это необходимо. Делается это просто:
<?php
$comments_num = get_post_meta( get_the_ID(), 'vk_comments', true ); // получите
echo 'Комментарии ('.$comments_num.')'; // и распишитесь
?>
среда, 7 августа 2013 г.
Тысяча первый костыль для починки чпу пользовательского типа wordpress (custom post type)
Достался мне сегодня в работу сайт аж с четырьмя пользовательскими типами постов. Плюс кучей плагинов и огромной свалкой кода в functions.php. И вот у них все страницы с кастомными типами постов (структура url - /тип поста/алиас записи/) выдают 404. И кагбэ надо это чинить. Плагины отключаю - ничего не меняется. Промудохавшись пару часов, вывела следующее:
a) flush_rewrite_rules(); упорно выдаёт бесконечную переадресацию
б) помогает вот такой костыль в functions.php
function my_parse_request($vars){
if(in_array($vars->query_vars['category_name'], array(_тут список кастомных типов записи_))) unset($vars->query_vars['category_name']);
return $vars;
}
add_action ('parse_request','my_parse_request');
То есть Wordpress почему-то считает, что /тип поста/ - это имя категории. Костылём мы его в этом разубеждаем, но вот докопаться, почему он так считает, у меня уже не хватило сил. Будем думать.
среда, 10 июля 2013 г.
SVG: как нарисовать сектор круга
Сегодня мы будем рисовать круг, разбитый на шесть равных секторов, используя замечательную библиотеку jQuery SVG. Плюс сделаем подсветку сектора при наведении.
Итак, качаем библиотеку отсюда: http://keith-wood.name/svg.html и подключаем её. Если нам нужна реакция на клик или наведение, дополнительно подключаем расширение SVG DOM (обратите внимание - для нормальной работы с нашим любимым ИЕ понадобится изменённая библиотека jQuery, она есть на этом же сайте). Собственно, мануал с большими картинками там присутствует, примеров сколько душе угодно - играйтесь на здоровье. А я перейду сразу к секторам, поскольку гугл выдаёт очень мало информации на этот счёт, а хочется быстро и красиво.
Итак, после подключения библиотеки создаём блок, в котором всё будет происходить:
<div id='circle' width='200' height='200'></div>
Ну и поехали. См. комментарии.
<script type="text/javascript">
$(function() {
$('#circle').svg({onLoad: drawCircle}); // #circle - идентификатор вашего блока, drawCirce - функция, которая будет отрисовывать всю красоту
});
/* Эта функция вычисляет координату точки на окружности, из которой будет начинаться следующий сектор. Чуть ниже мы загоним её в цикл. В неё передаются координаты точки начала отсчёта. */
function nextCoord(x0, y0){
// это координаты центра круга
x = 100;
y = 100;
alpha = 60*Math.PI/180; // считаем угол в радианах. 60 - это размер угла в градусах. Соответственно, у нас будет 6 секторов по 60 градусов.
rx = x0 - x;
ry = y0 - y;
c = Math.cos(alpha);
s = Math.sin(alpha);
x1 = x + rx * c - ry * s;
y1 = y + rx * s + ry * c;
return [x1,y1]; // и возвращаем массив с новыми координатами
}
// Наша основная функция
function drawCircle(svg) {
svg.circle(100, 100, 100, {fill: '#ffffff'}); /* Рисуем круг. Параметры: координата x, координата y, радиус, объект со всяческими опциями (см. мануал). Этот шаг можно пропустить, поскольку наши сектора и так составят тот же самый круг. */
var coord = [0,100]; // точка, с которой мы начинаем отсчитывать сектора. В данном случае - крайняя левая точка окружности.
for(var i=0;i<6;i++){ // секторов у нас будет 6, поэтому прогоняем цикл 6 раз
var sector = svg.createPath(); // создаём новую кривую
sector.move(100,100,false); // "ставим карандаш" в центр будущего круга. false здесь означает, что координаты абсолютные, опять же, курите мануал
sector.line(coord[0],coord[1],false); // ведём линию из центра к нашей отправной точке на окружности
var newcoord = nextCoord(coord[0],coord[1]); // с помощью предыдущей функции считаем координаты конечной точки дуги сектора
sector.arc(100, 100, 60, false, true, newcoord[0], newcoord[1], false)
/* рисуем дугу. Параметры:
1,2) 100 и 100 - 2 радиуса эллипса, частью которого будет наша дуга (в данном случае это и есть круг с радиусом 100),
3) угол - у нас он 60 градусов
4) Выбрать бОльшую дугу (если эллипс вытянутый) - true или false
5) Отмеряем дугу по часовой стрелке - true, против часовой - false
6,7) Наши новые координаты, точка, в которую дуга должна прийти
8) false - координаты итоговой точки абсолютные
*/
sector.close(); // "закрываем" наш сектор, конец дуги автоматически соединяется с начальной точкой в центре круга
svg.path(sector, {fill:'#ffffff',stroke: 'black', strokeWidth: 1, class: 'sector'}); //отрисовываем наш сектор. не забываем присвоить ему класс или id, если хотим повесить на него событие
coord = newcoord; // принимаем точку на круге, в которую мы пришли, за исходную, и идём на следующий цикл
}
// Добавляем изменение заливки при наведении
$('.sector').mouseover(function(){
$(this).attr('fill','#d5ff9d');
});
$('.sector').mouseout(function(){
$(this).attr('fill','#fff');
});
}
</script>
Как-то так.
ЗЫ: Я окончательно убедилась, что мне нужна подсветка кода. Надо бы что-нибудь такое подключить к блогу.
Итак, качаем библиотеку отсюда: http://keith-wood.name/svg.html и подключаем её. Если нам нужна реакция на клик или наведение, дополнительно подключаем расширение SVG DOM (обратите внимание - для нормальной работы с нашим любимым ИЕ понадобится изменённая библиотека jQuery, она есть на этом же сайте). Собственно, мануал с большими картинками там присутствует, примеров сколько душе угодно - играйтесь на здоровье. А я перейду сразу к секторам, поскольку гугл выдаёт очень мало информации на этот счёт, а хочется быстро и красиво.
Итак, после подключения библиотеки создаём блок, в котором всё будет происходить:
<div id='circle' width='200' height='200'></div>
Ну и поехали. См. комментарии.
<script type="text/javascript">
$(function() {
$('#circle').svg({onLoad: drawCircle}); // #circle - идентификатор вашего блока, drawCirce - функция, которая будет отрисовывать всю красоту
});
/* Эта функция вычисляет координату точки на окружности, из которой будет начинаться следующий сектор. Чуть ниже мы загоним её в цикл. В неё передаются координаты точки начала отсчёта. */
function nextCoord(x0, y0){
// это координаты центра круга
x = 100;
y = 100;
alpha = 60*Math.PI/180; // считаем угол в радианах. 60 - это размер угла в градусах. Соответственно, у нас будет 6 секторов по 60 градусов.
rx = x0 - x;
ry = y0 - y;
c = Math.cos(alpha);
s = Math.sin(alpha);
x1 = x + rx * c - ry * s;
y1 = y + rx * s + ry * c;
return [x1,y1]; // и возвращаем массив с новыми координатами
}
// Наша основная функция
function drawCircle(svg) {
svg.circle(100, 100, 100, {fill: '#ffffff'}); /* Рисуем круг. Параметры: координата x, координата y, радиус, объект со всяческими опциями (см. мануал). Этот шаг можно пропустить, поскольку наши сектора и так составят тот же самый круг. */
var coord = [0,100]; // точка, с которой мы начинаем отсчитывать сектора. В данном случае - крайняя левая точка окружности.
for(var i=0;i<6;i++){ // секторов у нас будет 6, поэтому прогоняем цикл 6 раз
var sector = svg.createPath(); // создаём новую кривую
sector.move(100,100,false); // "ставим карандаш" в центр будущего круга. false здесь означает, что координаты абсолютные, опять же, курите мануал
sector.line(coord[0],coord[1],false); // ведём линию из центра к нашей отправной точке на окружности
var newcoord = nextCoord(coord[0],coord[1]); // с помощью предыдущей функции считаем координаты конечной точки дуги сектора
sector.arc(100, 100, 60, false, true, newcoord[0], newcoord[1], false)
/* рисуем дугу. Параметры:
1,2) 100 и 100 - 2 радиуса эллипса, частью которого будет наша дуга (в данном случае это и есть круг с радиусом 100),
3) угол - у нас он 60 градусов
4) Выбрать бОльшую дугу (если эллипс вытянутый) - true или false
5) Отмеряем дугу по часовой стрелке - true, против часовой - false
6,7) Наши новые координаты, точка, в которую дуга должна прийти
8) false - координаты итоговой точки абсолютные
*/
sector.close(); // "закрываем" наш сектор, конец дуги автоматически соединяется с начальной точкой в центре круга
svg.path(sector, {fill:'#ffffff',stroke: 'black', strokeWidth: 1, class: 'sector'}); //отрисовываем наш сектор. не забываем присвоить ему класс или id, если хотим повесить на него событие
coord = newcoord; // принимаем точку на круге, в которую мы пришли, за исходную, и идём на следующий цикл
}
// Добавляем изменение заливки при наведении
$('.sector').mouseover(function(){
$(this).attr('fill','#d5ff9d');
});
$('.sector').mouseout(function(){
$(this).attr('fill','#fff');
});
}
</script>
Как-то так.
ЗЫ: Я окончательно убедилась, что мне нужна подсветка кода. Надо бы что-нибудь такое подключить к блогу.
четверг, 27 июня 2013 г.
PHP: Zip архив с кириллическими именами
Допустим, нам нужно извлечь из zip архива файлы с именами на русском. А он извлекает крокозябры. Вот что сработало в итоге для меня:
$zip = new ZipArchive;
$res = $zip->open(*путь к папке, в которую загружен архив*);
if ($res === TRUE) {
$docs_path = *путь к папке, в которую надо распаковать архив*;
if(!is_file($docs_path)) mkdir($docs_path); // создаём, если не существует
$count = $zip->numFiles;
for($i = 0; $i < $count; $i++) {
$stat = $zip->statIndex($i);
$newname = mb_convert_encoding ($stat['name'], 'UTF-8','CP866' );
$newname = $this->makeAlias($newname, 1); // makeAlias - функция для перевода в транслит. Highly recommended, не используйте кириллические имена
$zip->renameIndex($i,$newname);
$res = $zip->extractTo($docs_path, array($zip->getNameIndex($i)));
}
$zip->close();
}
Итак, мы прошлись циклом по архиву, переименовали каждый файлик и распаковали его в нужное место. Хочу отметить, что функция iconv для меня по каким-то таинственным причинам не сработала, поэтому, как вы видите, используется mb_convert_encoding.
$zip = new ZipArchive;
$res = $zip->open(*путь к папке, в которую загружен архив*);
if ($res === TRUE) {
$docs_path = *путь к папке, в которую надо распаковать архив*;
if(!is_file($docs_path)) mkdir($docs_path); // создаём, если не существует
$count = $zip->numFiles;
for($i = 0; $i < $count; $i++) {
$stat = $zip->statIndex($i);
$newname = mb_convert_encoding ($stat['name'], 'UTF-8','CP866' );
$newname = $this->makeAlias($newname, 1); // makeAlias - функция для перевода в транслит. Highly recommended, не используйте кириллические имена
$zip->renameIndex($i,$newname);
$res = $zip->extractTo($docs_path, array($zip->getNameIndex($i)));
}
$zip->close();
}
Итак, мы прошлись циклом по архиву, переименовали каждый файлик и распаковали его в нужное место. Хочу отметить, что функция iconv для меня по каким-то таинственным причинам не сработала, поэтому, как вы видите, используется mb_convert_encoding.
Подписаться на:
Сообщения (Atom)