понедельник, 18 августа 2014 г.

Wall.post и open api access error

Римлянцы, совграждане, товарищи дорогие! Если вы с вашего сайтика делаете запрос вконтакт, пытаясь запостить картинку пользователю на стену, а он вам отвечает "open api access error", проверьте, добавлен ли ваш домен в список доменов приложения вконтакте.

Сам же запрос элементарен:
img = 'photo-11111_1111'; //id картинки, уже загруженной вконтакт, можно найти в её url
VK.Api.call('wall.post', { attachments:img}, function(r) {
//callback
});

Всем бобра!

воскресенье, 25 мая 2014 г.

Пишем макрос для OpenOffice Writer на Python. Вставляем изображения.

Да-а, ребята, это было непросто. Документация, конечно, есть, но в ней чёрт ногу сломит, а большая часть примеров вовсе даже на Basic. А мы вот попробуем something completely different - на Питоне.

ТЗ: Имеется папка, в ней тыща миллион до неба картинок jpg. Картинки требуется вставлять попарно в документ OpenOffice Writer между абзацами текста. То есть поставил курсор - воткнул следующую пару картинок - поехал дальше. Посмотрим, что мы можем сделать.

Прежде всего: питоновские скрипты не редактируются в самом OpenOffice. Редактируем их в чём угодно и загружаем в специальную папку - при стандартной установке на Windows это C:\Program Files\OpenOffice 4\share\Scripts\python . Там должны уже лежать встроенные скриптики типа HelloWorld.py.

ОК, что же нужно написать в нашем файле, чтобы сделать красиво? Разберём построчно.

Подгружаем нужные библиотеки
from com.sun.star.awt import Size
from PIL import Image #Если вы придумаете, как по-другому определять размер картинки, можно эту строчку пропустить. Если нет, то нужно скачать библиотеку PIL (гугл в помощь) и загрузить в папку питона в OpenOffice: C:\Program Files\OpenOffice 4\program\python-core-2.7.6\lib\site-packages

Тут у нас четыре функции. При вызове макроса исполняется последняя, первые три - вспомогательные.

def AddGraphic(pbreak):
st = getImagePath() # Вторая вспомогательная функция. Вторая и третья нужны для того, чтобы последовательно переходить от картинки к картинке в заданной папке.
oDoc = XSCRIPTCONTEXT.getDocument() # Получаем открытый документ
oText = oDoc.Text #Получаем текст документа
VC = oDoc.CurrentController.ViewCursor #Получаем объект курсора

GraphObj = oDoc.createInstance("com.sun.star.text.GraphicObject") # Создаём картинку
GraphObj.GraphicURL = 'file:///' + st # Источник картинки - наш очередной файл. Не забываем про приставку file:///

img = Image.open(st) #В следующих строчках мы при помощи библиотеки PIL получаем размер изображения, чтобы правильно смасштабировать его высоту при заданной ширине. Вообще-то предполагается, что у GraphicObject есть свойство ActualSize, в документации оно вроде как прописано, но на деле у меня ругалось, что такого свойства нет - пришлось выкручиваться.
if img.size[1] > 0:
prop = float(img.size[0])/float(img.size[1])
imgsize = Size(8500, (8500 / prop) ); # 8500 - это чуть меньше половины ширины страницы (8.5 см) - я нагло и беспардонно посмотрела ширину, потому что искать, как высчитать её программно, уже не было времени и сил. Если у вас получится, отпишитесь в комменты))
GraphObj.setSize(imgsize)
                 # Если это первая картинка, выравниваем по левому краю, если вторая - по правому
if(pbreak == 1):                  
      GraphObj.HoriOrient = 1
else:
       GraphObj.HoriOrient = 0
# Вставляем изображение
oText.insertTextContent(VC.Start, GraphObj, False)
               # Если это вторая картинка, создаём новый абзац
if(pbreak == 1):
oText.insertControlCharacter(VC.Start, 0, False)
#Есть мнение, что после всего этого нужно обновить документ. Не заметила особых изменений, но пусть будет.
oDoc.refresh()

def getImagePath():   # У меня всё облегчается тем, что изображения пронумерованы, поэтому мы просто прибавляем цифирку к номеру. Впрочем, просто перебрать все картинки в папке особого труда тоже не составит.
    dir = 'D:/mymages/image-'
    type = '.jpg'
    num = getNumber()
    st = dir + num + type
    return st

def getNumber(): # Чтобы сохранить номер последней загруженной картинки, используем текстовый файл-счетчик
    file = "D:/counter.txt"
    num = 0
    f = open(file, 'r+')
    num = f.read()
    f.seek(0)
    newnum = str(int(num) + 1)
    f.write(newnum)
    f.close()
    return num

def InsertImage( ): #Два раза вызываем функцию вставки картинок, второй раз после этого переходим на новый абзац
    AddGraphic(0)
    AddGraphic(1)
    return None

# В качестве макроса нам нужна только основная функция, так и пишем.
g_exportedScripts = InsertImage

Скачать файл можно тут. На самом деле, даже нужно, это же Python, вряд ли вы захотите потом исправлять все отступы. Удачи!

суббота, 10 мая 2014 г.

Две версии MySQL и Denwer

Так сложилось, что мне понадобилось использовать сервер MySQL 4.1, тогда как в моей версии Denwer уже стоит версия 5.5, которая и загружается при запуске Denwer. Что делаем? Ставим на машину версию 4.1, запускаем её из командной строки (net start MySQL) - и после этого запускаем Denwer. Он видит, что MySQL уже запущен - и вуаля, не запускает более новую версию. Дело закрыто.

пятница, 9 мая 2014 г.

Zend Optimizer. Fatal error: This encoded file is corrupted.

Предыстория. Делаю я себе тихо-мирно локальную копию сайта. Несколько файлов оказываются ужаты Zend Optimizer'ом. ОК, ставлю на свою машину этот Optimizer. А он, животное безобразное, ругается Fatal error: This encoded file is corrupted.

Что делать? А оказывается, надо было эти ужатые файлы качать не как обычно, а в бинарном режиме. В 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');
 });

Побочный эффект - если у вас в модальном окне изначально был скроллбар, он будет пропадать при появлении выпадающего списка, и всё содержимое будет чуть подёргиваться. Для меня это не очень важно, но если найдёте способы этого избежать, дайте знать :)

среда, 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.')'; // и распишитесь
?>

среда, 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 почему-то считает, что /тип поста/ - это имя категории. Костылём мы его в этом разубеждаем, но вот докопаться, почему он так считает, у меня уже не хватило сил. Будем думать.