SSHFS — Альтернатива использованию SFTP/SSH клиентов

Больинство из нас работает с файлами сайта удалённо, через ftp или sftp/ssh. FTP жутко тормозной, в связи с чем я уже давно перешёл полностью на ssh (чего и вам советую).

Каким образом легко получить доступ к файлам, не используя sftp/ssh-клиент?

Есть такая чудо-штука, называемая fuse. На её основе можно делать файловые системы, которые находятся удалённо (например, smbfs). Так вот, существует sshfs, который позволяет примонтировать удалённый хост на локальный компьютер и работать с файлами так, будто они на вашем жёстком диске!

Debian-way howto:
  1. sudo apt-get install sshfs
  2. sudo mkdir /media/host-name
  3. sudo chown your-username /media/host-name
  4. sudo adduser your-username fuse
  5. sudo adduser your-username nogroup
  6. sudo gnome-session-save --logout
  7.  
  8. #after logout-login you can mount a host like this
  9. sshfs -o workaround=rename host-name.com:/working-dir/ /media/host-name
  10.  
  11. #and unmount a host like this
  12. fusermount -u /media/host-name

Как ещё упростить жизнь? :)

Я пользуюсь Убунтой, а в ней по умолчанию gedit. В настройках можно включить auto save, save backups, line numbers, auto indentation, right margin и плагин side pane, а также подсветку синтаксиса. Side pane позволяет по нажатию F9 открывать "проводник", в котором легко перейти в монтированную директорию и заняться работой.

Пишем расширение для Google Chrome - History spy

Отмазка Вступление

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

Исправим ситуацию

Решение:
  1. Написать расширение, которое будет мониторить табы, записывая всяческую информацию о посещениях
  2. Заставить расширение работать также и в incognito mode
  3. Не спалиться :)

Что ж, приступим..
  1. В любом месте создаём папку, именуем как угодно
  2. Создаём manifest.json в ней. Формат - JSON. Манифест - паспорт расширения.
    Листинг:
    1. {
    2. "name": "Mr. Trololo", //имя расширения
    3. "version": "0.1", //версия
    4. "description": "History spy.", //описание
    5. "icons": { //иконки - не обязательно
    6. "48": "48.png",
    7. "128": "128.png"
    8. },
    9.  
    10. "background_page": "main.html", //воркер
    11. "options_page": "main.html", //настройки расширения
    12. "permissions": [ //разрешения
    13. "tabs", //доступ к tabs api
    14. "unlimited_storage" //безлимит места под хранение данных
    15. ]
    16. }
    Почитать: спецификация по манифестам
  3. Теперь займёмся нашим воркером.. Он-то и будет делать всю "грязную" работу :)
    Что требуется от воркера:
    • Повесить event listener на событие onUpdated табов
    • Обрабатывать каждый вызов события, записывая информацию о посещениях в localStorage
    • При вызове настроек расширения (у нас воркер отвечает и за это, для простоты) предоставить интерфейс для просмотра истории посещений, а также её очистки.

    Для начала реализуем javascript ядро воркера, которое будет подключаться из main.html
    Листинг main.js:
    1. /*
    2.   @collection
    3.   Sorry for my bad Javascript ^_^
    4. */
    5. m = {
    6. key: 'history', //key for local storage
    7. ls: window.localStorage, //local alias
    8.  
    9. /* Clear stored data */
    10. clearData: function() {
    11. m.ls.removeItem( m.key );
    12. m.ls.setItem( m.key, "[]" ); //initialize our var' as JSON's array
    13. },
    14.  
    15. /* Get stored data helper */
    16. get: function() {
    17. if ( m.ls.getItem( m.key ) == null ) { //var' isn't initialized
    18. m.ls.setItem( m.key, "[]" );
    19. }
    20.  
    21. /* Local storage provides only string saving. Using JSON for [un]serializing arrays */
    22. return JSON.parse( m.ls.getItem( m.key ) );
    23. },
    24.  
    25. /* Set stored data helper */
    26. set: function( v ) {
    27. m.ls.setItem(
    28. m.key,
    29. JSON.stringify( (m.get()).concat( [v] ) ) //concat prev' and present data, JSON it
    30. );
    31. },
    32.  
    33. /* Callback for event listener */
    34. callbackTabs: function( id, i, tab ) {
    35. if ( tab.status == 'complete' ) { //event fires 2 times: "loading" and "complete" states
    36. m.set( { //store that
    37. date: m.date(),
    38. favIcon: tab.favIconUrl || '',
    39. url: tab.url || '',
    40. title: tab.title || '',
    41. incognito: tab.incognito
    42. } );
    43. }
    44. },
    45.  
    46. /* Ugly date format helper */
    47. date: function() {
    48. d = new Date();
    49. return '' + (
    50. ( d.getDate() > 9 ? d.getDate() : '0' + d.getDate() ) + ' ' +
    51. ['January','February','March','April','May','June','July','August',
    52. 'September','October','November','December'][d.getMonth()] +
    53. ' ' + d.getFullYear() + ', ' +
    54. ( d.getHours() > 9 ? d.getHours() : '0' + d.getHours() ) + ':' +
    55. ( d.getMinutes() > 9 ? d.getMinutes() : '0' + d.getMinutes() ) + ':' +
    56. ( d.getSeconds() > 9 ? d.getSeconds() : '0' + d.getSeconds() )
    57. );
    58. },
    59.  
    60. /* $(id) getter */
    61. $: function( id ) {
    62. return document.getElementById( id );
    63. },
    64.  
    65. /* Helper for decomposition of an URL */
    66. parseURL: function( url ) {
    67. var a = document.createElement('a');
    68. a.href = url;
    69.  
    70. //some extra parts are commented, we don't need 'em
    71. return {
    72. /* source: url,
    73. protocol: a.protocol.replace(':',''), */
    74. host: a.hostname,
    75. /* port: a.port,
    76. query: a.search,
    77. params: (function(){
    78. var ret = {},
    79. seg = a.search.replace(/^?/,'').split('&'),
    80. len = seg.length, i = 0, s;
    81. for (;i<len;i++) {
    82. if (!seg[i]) { continue; }
    83. s = seg[i].split('=');
    84. ret[s[0]] = s[1];
    85. }
    86. return ret;
    87. })(),
    88. file: (a.pathname.match(//([^/?#]+)$/i) || [,''])[1],
    89. hash: a.hash.replace('#',''), */
    90. path: a.pathname.replace(/^([^/])/,'/$1')/* ,
    91. relative: (a.href.match(/tp://[^/]+(.+)/) || [,''])[1],
    92. segments: a.pathname.replace(/^//,'').split('/') */
    93. };
    94. },
    95.  
    96. /* Displaying history helper. History iterator */
    97. displayData: function() {
    98. var html = ''; //dirty working with DOM
    99.  
    100. (m.get()).forEach( function( v ) {
    101. p = m.parseURL( v.url );
    102.  
    103. html += '<li><span>' + v.date + '</span> ' + ( v.incognito ? '<strong>incognito mode</strong> ' : '')
    104. + '<a href="' + v.url + '"><img src="'
    105. + ( v.favIcon ? v.favIcon : ('http://www.google.com/s2/favicons?domain=' + p.host) ) + '"/>'
    106. + ( v.title ? v.title : ((p.host + p.path ).substr( 0, 80 )) ) + '</a></li>';
    107. });
    108.  
    109. m.$( 'putHere' ).innerHTML = html;
    110.  
    111. return false;
    112. }
    113. };
    114.  
    115. /* Add a tabs listener */
    116. chrome.tabs.onUpdated.addListener( m.callbackTabs );
    Код отлично комментирован, как мне кажется
  4. Теперь сам воркер, обычная html страница с подключением скрипта + небольшой интерфейс.
    Код:
    1. <script type="text/javascript" src="main.js"></script>
    2. <style type="text/css">
    3. #putHere {
    4. list-style-type: none;
    5. }
    6. span {
    7. color: #ddd;
    8. -webkit-transition: 1s;
    9. }
    10. span:hover {
    11. color: #000;
    12. }
    13.  
    14. #putHere a, strong {
    15. margin-left: 20px;
    16. text-decoration: none;
    17. }
    18.  
    19. img {
    20. margin-right: 7px;
    21. position: relative;
    22. top: 3px;
    23. }
    24. </style>
    25. <ul>
    26. <li><a href="javascript:m.displayData()">Show</a></li>
    27. <li><a href="javascript:( m.clearData() || m.displayData() )">Clear</a></li>
    28. </ul>
    29. <ul id="putHere"></ul>
  5. Расширение, в принципе, готово :) Добавим в Хром..
    1. Ctrl+T, chrome://extensions/
    2. Включить режим разработчика
    3. "Загрузить распакованное расширение..", выбираем папку, которую создавали в самом начале
    4. Если всё прошло без ошибок, то ставим галочку "разрешить работать в автономном режиме" и радуемся. Если нет - действуйте по обстановке, Хром скажет в чём и где ошибка расширения.
Ну а теперь самое время проверить, работает ли действительно :) Походите по страницам, запустите режим инкогнито - тоже "посёрфите" немного.. Надоело? Тогда идём на страницу с расширениями, и открываем настройки нашего свеженаписанного :) Ссылка "Show" может заставить кого-то покраснеть однажды..

P.S. Страница расширения на Google Chrome Extensions

UPD: Перевод текста с помощью Google Translate на php

Думаю многим уже известно, что с помощью Google Translate можно делать незатейливые рерайты текста. Нужно просто перевести текст в другой язык (например, английский), затем перевести в изначальный. Сколько не пробовал - получается уникальный текст :). Правда читабельность иногда чрезвычайно ухудшается..

Предлагаю вашему вниманию скрипт хелпера для перевода текста с помощью Google Переводчика:

  1. <?php
  2. /**
  3. * @package Google Translate Helper
  4. * @author B1rdEX http://stuff-coding.blogspot.com/
  5. */
  6. // языковая пара из_какого_языка|в_какой переводить, список на translte.google.com
  7. $langpair = 'ru|en';
  8. // текст для перевода. Максимальная длина = 5120 (проверял на момент написания поста)
  9. $text = 'Важная законодательная инициатива президента: Россия может использовать свои Вооруженные силы за рубежом для отражения нападения, предотвращения агрессии против другого государства, защиты своих граждан и борьбы с пиратством. Президентский законопроект уже внесен в Госдуму.';
  10. function translate( $text, $langpair ) {
  11. $postdata = http_build_query (
  12. array (
  13. 'v' => '1.0',
  14. // обрезаем строку и переводим в Юникод
  15. 'q' => iconv ( 'windows-1251', 'utf-8', substr( strip_tags( $text ), 0, 5120 ) ),
  16. 'langpair' => $langpair,
  17. )
  18. );
  19. $options = array (
  20. 'http' => array (
  21. 'method' => 'POST',
  22. 'header' => 'Content-type: application/x-www-form-urlencoded',
  23. 'referrer' => 'http://google.com',
  24. 'content' => $postdata
  25. )
  26. );
  27. $context = stream_context_create ( $options );
  28. // Google возвращает результат в JSON
  29. $result = json_decode( file_get_contents ( 'http://ajax.googleapis.com/ajax/services/language/translate', false, $context ), 1 );
  30. return $result['responseStatus'] == 200
  31. ? iconv( 'utf-8', 'windows-1251', $result['responseData']['translatedText'] )
  32. : 'Error!' . "n" . 'Code: ' . $result['responseStatus'] . "n" . 'Details: ' . $result['responseDetails'];
  33. }
  34. echo translate( $text, $langpair );