Доброго времени суток, уважаемый читатель. Планировал в качестве следующей статьи написать более подробно про то, как собираются типовые проекты на WP. Бюджеты у нас в Хабаровске довольно скромные, кушать хочется всем, в том числе и нам, поэтому есть множество моментов, которые необходимо учесть при разработке, чтобы сохранить должный уровень качества для наших клиентов, заработав на кусок хлеба с маслом себе. И предусмотреть такой нюанс, как обучение новых сотрудников.
Но комментарий пользователя m00t задел довольно чувствительную для меня струнку. Конфликт интересов бизнеса и интернет-агентств. Множество “мертвых” сайтов, живущих ровно до первого продления доменного имени. Такая ситуация заставляет меня грустить.
Поэтому в качестве вводной части выступают мои размышления о том, почему не всегда необходимо делать сайт на %framework_name%. Возможно мысли очевидные, возможно нет, но так тому и быть, без них пост будет не совсем полным. И да — то что подходит для нашего дальневосточного рынка, может не подходить для вашего. Приступим.
Для тех, кому интересно только то, как мы собираем проекты, без мыслей о том, зачем вообще нужно делать сайт за 10 000 рублей и философского сумбура — GOTO 10.
Вводная часть.
Некоторые утверждения немного спорны и эмоциональны, даже с моей точки зрения, но я стремлюсь к диалогу и был бы очень рад услышать мнения, которые не совпадают с моим или дополняют его. В споре рождается истина.
Доверие.
Здесь довольно уместна аллегория. Допустим мне необходимо заменить в ванной сантехнику. Если я не буду доверять мастеру, который занимается этим, постоянно критиковать, пытаться давать ему “ценные указания”, “лезть под руку” — практически всегда это ни к чему хорошему не приведет (конечно, если вы сами не обладаете глубокими познаниями в этом деле, что, будем честны, не так часто встречается). Конфликт лишь создаст негатив, что крайне излишне. Да, бывают не очень хорошие исполнители, но выбор сантехника — не тема этой заметки.
Итак, первая мысль. На моей практике наиболее успешные проекты это те, где клиент понимает что ему необходимо, без указаний как это сделать. Черт возьми, подозреваю, что мастер лучше знает, как сделать разводку сантехники, опыта немного больше у него в этом. И главная задача студии — как можно лучше понять цели клиента, выбрав для их достижения максимально оптимальный инструмент. Что и подводит нас ко второй мысли.
“Продавать или помогать?”
Менеджер, при должном опыте, всегда может “продать” клиенту ajax-калькулятор на сайт парикмахерской c одним сотрудником, который и стрижет и директор. Может продать дизайн с иллюстрациями, по десять тысяч каждая, для сайта эвакуаторов с одной машиной. Сказав магические слова “юзабилити, конверсия, продакшн”, навязать клиенту трехстраничник на Zend, с возможностью расширения до пульта управления космолетом, причем потенциальная аудитория — два человека в месяц. Всегда есть выбор, каждый решает сам по какому пути пойти.
Мысль тривиальна как 10 копеек — у клиента есть деньги, у меня есть деньги. Ресейл и все такое. Даже есть заготовленная фраза — “вам пока нет необходимости в проекте за %too_big_number% тысяч рублей, сделайте типовой, через год вернетесь, когда у вас будут ресурсы, сделаем крутой сайт”.
Проекты делаются же не для показа исходников другим программистам, а для клиентов, с денег которых, в студиях, платится зарплата. Но тут важен баланс. Это не значит, что необходимо писать проект в одном файле, относиться «сделал и забыл». Если сделать хорошо — клиент вернется, а их в поле зрения студии конечное число, в любом случае. Да и о поддержке необходимо думать дальнейшей, ведь сэкономленный рубль — заработанный рубль. И тратить время программиста на смену телефона или добавление новости — не думаю, что это хорошо.
Простейший кейс, к примеру. Есть человек, владеющий фирмой, которая помогает зимой застрявшим автомобилистам. Бюджет ограничен. Что мы видим и что мы можем предложить? Аудитория не требовательна, конкуренция сравнительно низкая в нашем регионе, да и дело ли до моушена и дизайна, когда ты опаздываешь на работу, застряв на дороге. Запрос в поисковике -> номер телефона -> звонок. Делаем самый простейший сайт, подаем рекламу + сео, готово. Он получает клиентов, мы получаем его довольного, который расскажет о нас своим знакомым, и, вероятней всего, закажет в будущем разработку более высокого уровня, имея средства. И этот сайт не бесплатно делали.
По поводу того, как попытаться формализовать выбор того, что будет делаться. Смотрим в среднем на сколько процентов отличается конверсия бюджетных и «крутых» проектов, пробуем приблизительно высчитать разницу в чистой прибыли. Затем считаем срок, в какой окупится индивидуальная разработка, если для клиента подходит — работаем. Нет — человек выбирает шаблон, мы приступаем к сборке сайта. О чем и есть нижеследующее.
Сборка сайта на WP.
Данная методология (громкое слово, но стилистически оно уместно) предназначена больше для «типовых» проектов, но ничего не мешает нарисовать хороший дизайн, позаботиться о написании текстов, подобрать графику, сделать красивый «моушен» (любимое слово дизайнера, когда я начинаю верстать любопытные вещи), что выведет сайт на другой уровень. Это же просто админ-панель, она нужна больше для клиента, нежели чем для посетителя.
Требования для обучающего материала, которые были формализованы и исполнены:
- Простой процесс сборки. Новый человек должен быстро вливаться в работу. Причем его знания могут быть крайне далеки от программирования, главное наличие логики, усидчивости, находчивости. Забавная метрика, под названием «Bus factor», пусть будет наиболее высокой.
- Удобная и понятная для клиента админ-панель. Вся текстовая и графическая информация должна легко изменяться без знания аббревиатур HTML/CSS/PHP. Будет снижать количество просьб по смене телефона, контактов, добавления новости etc. до минимальных отметок.
- Оптимальная скорость разработки. Один-два дня на вдумчивую сборку проекта, при условии готовой верстки. Можно и быстрее, но лучше тратить немного больше времени, но мелочи шлифовать.
Отступление небольшое. Кейс надуманный, но суть даст понять. Пытаюсь писать как можно лаконичней, поэтому, если будет желание ввести это где-то еще, необходимо иметь опытного человека, который сможет прояснить моменты, которые мне кажутся очевидными, но, может такое быть, что только для меня. И буду благодарен, если есть дополнения, т.к. хоть и зарекомендовал себя данный способ, всегда есть что-то лучше.
Дебют.
Поступает информация. Необходимо сделать сайт, который будет представлять информацию об услугах и товарах компании, без корзины/остатков и прочих штук интернет-магазина. Эта компания — наши соседи через здание. Маленький сервисный центр по ремонту телефонов, который еще и запчасти иногда продает.
Путем переговоров на свет рождается исписанный лист, который в данном случае и является неким «Техническим заданием» (что по нашей практике и есть достаточно). Затем в таск-менеджере появляется следующая задача:
Тип разработки: типовая.
Позиция по прайсу: каталог.
Структура меню:
— Главная
— О компании
— Ремонт
— Каталог запчастей
— Новости
— Контакты
Домен: simple.ru
Набор функционала тривиальный, когда нет дополнительных требований — этого обычно и достаточно для понимания того, что будет на сайте.
За данность возьмем следующее: у разработчика установлен LAMP-сервер, phpmyadmin, git, имеется репозиторий, к примеру на bitbucket, со штуками из предыдущего поста. Все команды даны относительно Ubuntu.
Ставим LAMP, если вдруг не стоит:
sudo apt-get install tasksel
sudo tasksel #Пробел на позиции LAMP server, Enter etc.
sudo a2enmod rewrite
sudo apt-get install phpmyadmin
sudo apt-get install git
Создаем VirtualHost, прописываем в hosts, включаем сайт:
mkdir /var/www/simple.ru
sudo nano /etc/apache2/sites-available/simple-ru.conf #Пример конфига - http://pastebin.com/1aWZhurE
sudo a2ensite simple-ru.conf
sudo service apache2 reload
sudo nano /etc/hosts # Вписываем строчку 127.0.0.1 simple.ru
Переходим в директорию проекта, тянем с git'а файлы, создаем бд и импортим дамп, который лежит в репозитории:
cd /var/www/simple.ru
git clone git@bitbucket.org:%username%/kosher_wordpress.git
mysql -uroot -p
mysql> CREATE DATABASE `simple-ru` CHARACTER SET utf8 COLLATE utf8_general_ci;
mysql -u username -p simple-ru < dump.sql
Обязательно не забыть сделать sql запросы, чтобы wp понял то, на каком он домене находится.
Прописываем в wp-config.php параметры соединения с бд:
define('DB_NAME', 'simple-ru');
define('DB_USER', 'root');
define('DB_PASSWORD', 'pass');
define('DB_HOST', 'localhost');
Переходим в браузере по адресу simple.ru и видим развернутый WordPress с нашими любимыми плагинами и прочими нужными штуками. И сейчас, когда я пишу эту статью, стало ясно, что все вышесказанное можно запихнуть в один sh скрипт/оформить в гуях, т.к. первые 20 раз весело в консоли писать — потом нет. Но все же.
Миттельшпиль.
В качестве шаблона, который «выбрал» клиент, я буду использовать этот — FORNAX. Он очень наглядно покажет проблемы, которые возникают, пути их решения.
Упор делается, по большей части, на максимальную простоту для новичка, поэтому это не догма. Баланс, если брать опыт сборки большого количества проектов подобного типа и обучения ребят, за последний год, соблюден. Улучшения необходимые приходят с опытом, но как база — подходит. Поехали.
1. Переименовываем папку snippets в simple-ru и кидаем внутрь верстку, папка assets.
В файле themes/simple-ru/style.css пишем следующее:
/*
Theme Name: Тема для сервиса
*/
Содержимое themes/simple-ru/assets/index.html копируем в themes/simple-ru/index.php и активируем тему в админ-панели WP (Внешний вид -> Темы -> Активировать). Если мы в данный момент посмотрим на сайт — стили/скрипты/изображения не подключились. Решим эту проблему функционалом редактора/IDE (я использую PhpStorm, CTRL+R) Найти и заменить. Вот три сниппета, которые справедливы в контексте этого шаблона, выполняются последовательно:
Найти: href="css/
Заменить: href="<?php bloginfo('template_url');?>/assets/css/
Найти: src="js/
Заменить: src="<?php bloginfo('template_url');?>/assets/js/
Найти: src="img/
Заменить: src="<?php bloginfo('template_url');?>/assets/img/
В моем случае еще необходимо прописать пути к фавиконкам, что я и делаю.
2. Начинаем резать верстку по файлам
Для начала, исходя из структуры сайта, представляем сколько разных по типу страниц у нас будет. А именно:
- Главная страница
- Статические страницы (О компании, ремонт)
- Страница контактов
- Страница списка товаров
- Страница списка новостей, страница отдельной новости (1 шаблон для них)
И, конечно, верхнюю часть и нижнюю часть страницы вынесем тоже в отдельные шаблоны. Создаем недостающие файлы:
- front-page.php (Главная страница)
- page.php (Статические страницы)
- tpl-contacts.php (Страница контактов)
- tpl-products.php (Страница продукции)
В админ-панели не забываем выставить Настройки->Чтение->На главной отображать->Указываем статическую страницу новостей и главную.
И разбрасываем верстку по файлам. Здесь необходимо применить чуть смекалки, но обычно ничего сложного нет. В файлы themes/simple-ru/header.php и themes/simple-ru/footer.php копируем верхний блок с меню, нижний с подвалом. Остальные страницы из themes/simple-ru/assets/*.html вырезаем соответственно в свои themes/simple-ru/*.php.
Добавляем get_header() наверх и get_footer() вниз каждого шаблона, для подключения шапки и футера. Сниппет «Найти и Заменить» исполняем аналогично, обычно нужна только часть, связанная с изображениями. В файлах tpl-contacts.php,tpl-products.php перед всем кодом должно быть следующее:
<?php
/*
* Template Name: Шаблон главной
*/
?>
Для чего? Потом в страницах будем выбирать их как шаблоны. Подробности.
Чтобы было наглядней, вот листинги текущего состояния, CTRL+C CTRL+V:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ShapeBootstrap Clean Template</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<!-- Bootstrap -->
<link href="<?php bloginfo('template_url');?>/assets/css/bootstrap.css" rel="stylesheet">
<link href="<?php bloginfo('template_url');?>/assets/css/bootstrap-responsive.css" rel="stylesheet">
<link href="<?php bloginfo('template_url');?>/assets/css/style.css" rel="stylesheet">
<!--Font-->
<link href='http://fonts.googleapis.com/css?family=Source+Sans+Pro:200,300,400,600' rel='stylesheet' type='text/css'>
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<!-- Fav and touch icons -->
<link rel="shortcut icon" href="<?php bloginfo('template_url');?>/assets/favicon.ico">
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="<?php bloginfo('template_url');?>/assets/ico/apple-touch-icon-144-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="<?php bloginfo('template_url');?>/assets/ico/apple-touch-icon-114-precomposed.png">
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="<?php bloginfo('template_url');?>/assets/ico/apple-touch-icon-72-precomposed.png">
<link rel="apple-touch-icon-precomposed" href="ico/apple-touch-icon-57-precomposed.png">
<!-- SCRIPT
============================================================-->
<script src="http://code.jquery.com/jquery.js"></script>
<script src="<?php bloginfo('template_url');?>/assets/js/bootstrap.min.js"></script>
</head>
<body>
<!--HEADER ROW-->
<div id="header-row">
<div class="container">
<div class="row">
<!--LOGO-->
<div class="span3"><a class="brand" href="#"><img src="<?php bloginfo('template_url');?>/assets/img/logo.png"/></a></div>
<!-- /LOGO -->
<!-- MAIN NAVIGATION -->
<div class="span9">
<div class="navbar pull-right">
<div class="navbar-inner">
<a data-target=".navbar-responsive-collapse" data-toggle="collapse" class="btn btn-navbar"><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></a>
<div class="nav-collapse collapse navbar-responsive-collapse">
<ul class="nav">
<li class="active"><a href="index.html">Home</a></li>
<li class="dropdown">
<a href="about.html" class="dropdown-toggle" data-toggle="dropdown">About <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="about.html">Company</a></li>
<li><a href="about.html">History</a></li>
<li><a href="about.html">Team</a></li>
</ul>
</li>
<li><a href="service.html">Services</a></li>
<li><a href="blog.html">Blog</a></li>
<li><a href="contact.html">Contact</a></li>
</ul>
</div>
</div>
</div>
</div>
<!-- MAIN NAVIGATION -->
</div>
</div>
</div>
<!-- /HEADER ROW -->
<div class="container">
<!--PAGE TITLE-->
<div class="row">
<div class="span12">
<div class="page-header">
<h1>
Blog
</h1>
</div>
</div>
</div>
<!-- /. PAGE TITLE-->
</div>
<!-- /.Row View -->
<!--Footer
==========================-->
<footer>
<div class="container">
<div class="row">
<div class="span6">Copyright © 2013 Shapebootstrap | All Rights Reserved <br>
<small>Aliquam tincidunt mauris eu risus.</small>
</div>
<div class="span6">
<div class="social pull-right">
<a href="#"><img src="<?php echo bloginfo('template_url');?>/assets/img/social/googleplus.png" alt=""></a>
<a href="#"><img src="<?php echo bloginfo('template_url');?>/assets/img/social/dribbble.png" alt=""></a>
<a href="#"><img src="<?php echo bloginfo('template_url');?>/assets/img/social/twitter.png" alt=""></a>
<a href="#"><img src="<?php echo bloginfo('template_url');?>/assets/img/social/dribbble.png" alt=""></a>
<a href="#"><img src="<?php echo bloginfo('template_url');?>/assets/img/social/rss.png" alt=""></a>
</div>
</div>
</div>
</div>
</footer>
<!--/.Footer-->
</body>
</html>
<?php get_header();?>
<!--Carousel
==================================================-->
<div id="myCarousel" class="carousel slide">
<div class="carousel-inner">
<div class="active item">
<div class="container">
<div class="row">
<div class="span6">
<div class="carousel-caption">
<h1>Example headline</h1>
<p class="lead">Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
<a class="btn btn-large btn-primary" href="#">Sign up today</a>
</div>
</div>
<div class="span6"> <img src="<?php bloginfo('template_url');?>/assets/img/slide/slide1.jpg"></div>
</div>
</div>
</div>
<div class="item">
<div class="container">
<div class="row">
<div class="span6">
<div class="carousel-caption">
<h1>Example headline</h1>
<p class="lead">Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
<a class="btn btn-large btn-primary" href="#">Sign up today</a>
</div>
</div>
<div class="span6"> <img src="<?php bloginfo('template_url');?>/assets/img/slide/slide2.jpg"></div>
</div>
</div>
</div>
</div>
<!-- Carousel nav -->
<a class="carousel-control left " href="#myCarousel" data-slide="prev"><i class="icon-chevron-left"></i></a>
<a class="carousel-control right" href="#myCarousel" data-slide="next"><i class="icon-chevron-right"></i></a>
<!-- /.Carousel nav -->
</div>
<!-- /Carousel -->
<!-- Feature
==============================================-->
<div class="row feature-box">
<div class="span12 cnt-title">
<h1>At vero eos et accusamus et iusto odio dignissimos</h1>
<span>Contrary to popular belief, Lorem Ipsum is not simply random text.</span>
</div>
<div class="span4">
<img src="<?php bloginfo('template_url');?>/assets/img/icon3.png">
<h2>Feature A</h2>
<p>
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
</p>
<a href="#">Read More →</a>
</div>
<div class="span4">
<img src="<?php bloginfo('template_url');?>/assets/img/icon2.png">
<h2>Feature B</h2>
<p>
Consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua.
</p>
<a href="#">Read More →</a>
</div>
<div class="span4">
<img src="<?php bloginfo('template_url');?>/assets/img/icon1.png">
<h2>Feature C</h2>
<p>
Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante.
</p>
<a href="#">Read More →</a>
</div>
</div>
<!-- /.Feature -->
<div class="hr-divider"></div>
<!-- Row View -->
<div class="row">
<div class="span6"><img src="<?php bloginfo('template_url');?>/assets/img/responsive.png"></div>
<div class="span6">
<img class="hidden-phone" src="<?php bloginfo('template_url');?>/assets/img/icon4.png" alt="">
<h1>Fully Responsive</h1>
<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.</p>
<a href="#">Read More →</a>
</div>
</div>
<?php get_footer();?>
<?php get_header(); ?>
<div class="row">
<div class="span9">
<!--Blog Post-->
<div class="blog-post">
<h2>There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour</h2>
<div class="postmetadata">
<ul>
<li>
<i class="icon-user"></i> <a href="#">Author Name</a>
</li>
<li>
<i class="icon-calendar"></i>September 20th, 2013
</li>
<li>
<i class="icon-comment"></i> <a href="#">100 Comments</a>
</li>
<li>
<i class="icon-tags"></i> <a href="#">Themes</a>, <a href="#">Template</a>
</li>
</ul>
</div>
<img src="<?php bloginfo('template_url');?>/assets/img/img1-870x400.jpeg">
<p>The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.</p>
<a class="btn btn-primary" href="blog-details.html">Read More</a>
</div>
<!--===============-->
<div class="blog-post">
<h2>There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour</h2>
<div class="postmetadata">
<ul>
<li>
<i class="icon-user"></i> <a href="#">Author Name</a>
</li>
<li>
<i class="icon-calendar"></i>September 20th, 2013
</li>
<li>
<i class="icon-comment"></i> <a href="#">100 Comments</a>
</li>
<li>
<i class="icon-tags"></i> <a href="#">Themes</a>, <a href="#">Template</a>
</li>
</ul>
</div>
<img src="<?php bloginfo('template_url');?>/assets/img/img1-870x400.jpeg">
<p>The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.</p>
<a class="btn btn-primary" href="blog-details.html">Read More</a>
</div>
<!--/.Blog Post-->
<!--Pagination-->
<div class="pagination">
<ul>
<li>
<a href="#">Prev</a>
</li>
<li>
<a href="#">1</a>
</li>
<li>
<a href="#">2</a>
</li>
<li>
<a href="#">3</a>
</li>
<li>
<a href="#">4</a>
</li>
<li>
<a href="#">5</a>
</li>
<li>
<a href="#">Next</a>
</li>
</ul>
</div>
<!--/.Pagination-->
</div>
<div class="span3">
<div class="side-bar">
<h3>Categories</h3>
<ul class="nav nav-list">
<li><a href="#">Web Design</a></li>
<li><a href="#">Typography</a></li>
<li><a href="#">Inspiration</a></li>
<li><a href="#">Business</a></li>
</ul>
</div>
<div class="side-bar">
<h3>Tags</h3>
<a href="#">cras</a>,
<a href="#">sit</a>,
<a href="#">amet</a>,
<a href="#">nibh</a>,
<a href="#">libero</a>,
<a href="#">gravida</a>,
<a href="#">nulla</a>
</div>
<div class="side-bar">
<h3>Recent Post</h3>
<ul class="recent-post">
<li><a href=""><strong>The standard chunk of Lorem Ipsum used since </strong></a>
<small><i class="icon-user"></i> <a href="#">Author Name</a>, <i class="icon-calendar"></i>Jul 20th, 2013</small>
</li>
<li><a href=""><strong>The standard chunk of Lorem Ipsum used since </strong></a>
<small><i class="icon-user"></i> <a href="#">Author Name</a>, <i class="icon-calendar"></i>Jul 20th, 2013</small>
</li>
<li><a href=""><strong>The standard chunk of Lorem Ipsum used since </strong></a>
<small><i class="icon-user"></i> <a href="#">Author Name</a>, <i class="icon-calendar"></i>Jul 20th, 2013</small>
</li>
</ul>
</div>
</div>
<!--==================-->
</div>
<?php get_footer(); ?>
<?php get_header();?>
<div class="row">
<div class="span12">
<h2>Welcome to Fornax</h2>
<img class="pull-left" alt="" src="<?php bloginfo('template_url');?>/assets/img/img2.png" />
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.
</p>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.
</p>
<p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.
</p>
</div>
</div>
<div class="hr-divider"></div>
<?php get_footer();?>
<?php get_header(); ?>
<!--GOOGLE MAP-->
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>
<script>
var map;
function initialize() {
var mapOptions = {
zoom: 8,
center: new google.maps.LatLng(-34.397, 150.644),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
<div class="row">
<div class="span12">
<div id="map-canvas"></div>
</div>
<span class="span6">
<form>
<fieldset>
<h3>Get in Touch</h3>
<input class="input-xxlarge" type="text" placeholder="Name" />
<input class="input-xxlarge" type="text" type="email" placeholder="Email" required/>
<textarea class="input-xxlarge" rows="10" placeholder="Your Message"></textarea>
<br>
<button type="submit" class="btn">Submit</button>
</fieldset>
</form>
</span>
<span class="span6">
<h3>Address</h3>
<address>
<strong>Fornax, Inc.</strong><br>
795 Folsom Ave, Suite 600<br>
San Francisco, CA 94107<br>
<abbr title="Phone">P:</abbr> (123) 456-7890
</address>
<address>
<strong>Full Name</strong><br>
<a href="mailto:#">first.last@example.com</a>
</address>
<p>
Cras sit amet nibh libero, in gravida nulla. Nulla vel metus scelerisque ante sollicitudin commodo. Cras purus odio, vestibulum in vulputate at, tempus viverra turpis.
</p>
</span>
</div>
<?php get_footer(); ?>
<?php
/*
* Template Name: Шаблон страницы продукции
*/
?>
<?php get_header(); ?>
<div class="span12">
<ul class="thumbnails">
<li class="span4">
<div class="thumbnail">
<img alt="" src="<?php bloginfo('template_url');?>/assets/img/team1.jpg">
<div class="caption">
<strong>DERRICK JEFFERSON</strong> /
UI DESIGNER
<p>
Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.
</p>
<p>
</p>
</div>
</div>
</li>
<li class="span4">
<div class="thumbnail">
<img alt="300x200" src="<?php bloginfo('template_url');?>/assets/img/team2.jpg">
<div class="caption">
<strong>ENJAMIN GARRETT</strong> /
MARKETING
<p>
Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.
</p>
<p>
</p>
</div>
</div>
</li>
<li class="span4">
<div class="thumbnail">
<img alt="300x200" src="<?php bloginfo('template_url');?>/assets/img/team3.jpg">
<div class="caption">
<strong>DASIA KATELYN</strong> /
IOS DEVELOPER
<p>
Cras justo odio, dapibus ac facilisis in, egestas eget quam. Donec id elit non mi porta gravida at eget metus. Nullam id dolor id nibh ultricies vehicula ut id elit.
</p>
<p>
</p>
</div>
</div>
</li>
</ul>
</div>
<?php get_footer(); ?>
3. Наполняем смыслом «обвязку»
Теперь вся работа сводится к замену верстки на WP функции. Работа муторная, но что делать, мы же профессионалы.
Начнем с обвязки (header, footer). Необходимо следующее:
- Шрифт Source Sans Pro не имеет русских символов, поправить.
- Тайтлы, сео теги, контекстный админ-бар сверху. Пропишем wp_head() и wp_footer().
- Меню должно управляться с админ-панели.
- Оставить заголовок страницы только на внутренних.
- Редактируемый логотип
- Социальные сети внизу
- Копирайты
В шаблоне header.php прописываем тайтлы и заменяем шрифт (подбираем аналог, я взял для примера Open Sans).
<title>ShapeBootstrap Clean Template</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
<title></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<?php wp_head();?>
<link href='http://fonts.googleapis.com/css?family=Source+Sans+Pro:200,300,400,600' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Open+Sans:200,300,400,600&subset=latin,cyrillic' rel='stylesheet' type='text/css'>
<!-- В файле assets/css/style.css заменяем вхождения Soure Sans Pro на Open Sans, CTRL+R -->
И перед body в файле footer.php вставляем <?php wp_footer();?>
.
С пунктом три немного интересней. Для вывода WP использует функцию wp_nav_menu($args). Наша цель состоит в том, чтобы сделать генерируемый этой функцией HTML аналогичным шаблону.
Как мы видим, исходя из верстки, класс дочерних меню — dropdown-menu. Да и для родителя не все хорошо:
class="dropdown-toggle" data-toggle="dropdown">About <b class="caret"></b>
...
<div class="nav-collapse collapse navbar-responsive-collapse">
<ul class="nav">
<li class="active"><a href="index.html">Home</a></li>
<li class="dropdown">
<a href="about.html" class="dropdown-toggle" data-toggle="dropdown">About <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="about.html">Company</a></li>
<li><a href="about.html">History</a></li>
<li><a href="about.html">Team</a></li>
</ul>
</li>
<li><a href="service.html">Services</a></li>
<li><a href="blog.html">Blog</a></li>
<li><a href="contact.html">Contact</a></li>
</ul>
</div>
...
Но ничего страшного, достаточно наследовать стандартный класс Walker_Nav_Menu, немного модифицировать, поместить его в functions.php и назначить его нашей функции, которая выводит меню. Ну и плюс, чтобы активный пункт меню подсвечивался.
<?php
/* functions.php */
class Bootstrap_Walker_Nav_Menu extends Walker_Nav_Menu {
/**
* Display Element
*/
function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) {
$id_field = $this->db_fields['id'];
if ( isset( $args[0] ) && is_object( $args[0] ) )
{
$args[0]->has_children = ! empty( $children_elements[$element->$id_field] );
}
return parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
}
/**
* Start Element
*/
function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
if ( is_object($args) && !empty($args->has_children) )
{
$link_after = $args->link_after;
$args->link_after = ' <b class="caret"></b>';
}
parent::start_el($output, $item, $depth, $args, $id);
if ( is_object($args) && !empty($args->has_children) )
$args->link_after = $link_after;
}
/**
* Start Level
*/
function start_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat("t", $depth);
$output .= "n$indent<ul class="dropdown-menu list-unstyled">n";
}
}
add_filter('nav_menu_link_attributes', function($atts, $item, $args) {
if ( $args->has_children )
{
$atts['data-toggle'] = 'dropdown';
$atts['class'] = 'dropdown-toggle';
}
return $atts;
}, 10, 3);
add_filter('nav_menu_css_class' , 'special_nav_class' , 10 , 2);
function special_nav_class($classes, $item){
if( in_array('current-menu-item', $classes) ){
$classes[] = 'active ';
}
return $classes;
}
?>
<?php
/* header.php */
$args = array(
'container' => 'div',
'menu_class' => 'nav',
'container_class' => 'nav-collapse collapse navbar-responsive-collapse',
'walker' => new Bootstrap_Walker_Nav_Menu
);
wp_nav_menu( $args );
?>
В итоге у нас получилось меню, которое соответствует шаблону:
Walker писать это уже выходит за рамки направления, которое я взял, когда писал эту заметку, но обычно можно решить этот вопрос альтернативно, потратив некоторое время на модификацию верстки.
Для того, чтобы убрать заголовок с главной и показывать только на внутренних, существуют функции условий. Нам понадобится is_home(). Просто оборачиваем блок в условие, заодно выводим заголовок в него:
<?php /* header.php */ ?>
<?php if(!is_home()):?>
<!--PAGE TITLE-->
<div class="row">
<div class="span12">
<div class="page-header">
<h1>
<?php wp_title('');?>
</h1>
</div>
</div>
</div>
<!-- /. PAGE TITLE-->
<?php endif;?>
Идем дальше. Все остальные пункты нашей обвязки попадают под понятие «глобальные настройки сайта». Для этого я использую данный инструмент (благодарю автора).
<?php
...
$options = array(
/*
* Шапка, подвал
*/
array(
'id' => 'standard',
'type' => 'opentab',
'name' => __('Шапка, подвал', 'slickadmin'),
),
array(
'name' => __('Логотип', 'slickadmin'),
'id' => 'logo',
'type' => 'upload',
),
array(
'name' => __('Копирайт, строка #1', 'slickadmin'),
'id' => 'copy1',
'type' => 'text',
),
array(
'name' => __('Копирайт, строка #2', 'slickadmin'),
'id' => 'copy2',
'type' => 'text',
),
array(
'type' => 'closetab',
)
);
...
?>
В итоге получается довольно симпатичная форма настроек для темы:
Заменяем в верстке плейсхолдеры на <?php echo sa_option($id);?>
, добавляем полей для социальных сетей и готово.
3. Переходим на внутренние страницы.
Для начала создадим все нужные нам страницы в админ-панели и назначим для страницы «Контакты» и страницы «Продукция» кастомные шаблоны. И в меню добавим заодно.
Теперь у нас можно переходить по меню, будут видны наши красивые странички. Но контент-менеджер, который будет наполнять это все дело, открутит голову мне, если я оставлю в таком виде. Продолжаем работать.
Шаблон статических страниц, page.php
Используем main loop, все стандартно. Листинг конечного шаблона:
<?php get_header();?>
<div class="row">
<div class="span12">
<?php if (have_posts()) : ?>
<?php while (have_posts()) : the_post(); ?>
<?php the_content(); ?>
<?php endwhile; ?>
<?php else: ?>
<p><?php _e('Sorry, no posts matched your criteria.'); ?></p>
<?php endif; ?>
</div>
</div>
<div class="hr-divider"></div>
<?php get_footer();?>
Шаблон каталога, tpl-products.php
Создадим, используя плагин Magic Fields, новый тип постов. Дополнительных полей у этого типа нет, выбираем галочками дефолтные title, thumbnail и excerpt. С помощью WP_Query заменим плейсхолдеры в шаблоне tpl-products.php на цикл из бд.
Если поля есть — просто получать их функцией get();
Листинг конечного шаблона:
<?php
/*
* Template Name: Шаблон страницы продукции
*/
?>
<?php get_header(); ?>
<div class="span12">
<?php
$args = array(
'post_type' => 'products',
'posts_per_page' => '-1'
);
$the_query = new WP_Query($args);
?>
<?php if ($the_query->have_posts()) : ?>
<ul class="thumbnails">
<?php while ($the_query->have_posts()) : $the_query->the_post(); ?>
<?php
$url = wp_get_attachment_url(get_post_thumbnail_id($the_query->post->ID));
$image = vt_resize(null, $url, 600, 300, true);
if (!$image['url']) $image['url'] = 'http://placehold.it/600x300&text=NO IMAGE';
?>
<li class="span4">
<div class="thumbnail">
<img alt="<?php echo the_title(); ?>" src="<?php echo $image['url']; ?>">
<div class="caption">
<strong><?php echo the_title(); ?></strong>
<p><?php the_excerpt();?></p>
</div>
</div>
</li>
<?php endwhile; ?>
<?php wp_reset_postdata(); ?>
</ul>
<?php else: ?>
<p><?php _e('Sorry, no posts matched your criteria.'); ?></p>
<?php endif; ?>
</div>
<?php get_footer(); ?>
Шаблон новостей, index.php
Дробить на сайдбары с виджетами не будем, ибо обычно нет в этом необходимости. Поэтому поступаем аналогично. Main loop и плейсхолдеры. Ну и несколько функций необходимо знать, для вывода тегов, паджинации, списка категорий. Код лаконичней объяснит:
<?php get_header(); ?>
<div class="row">
<div class="span9">
<?php if (have_posts()) : ?>
<?php while (have_posts()) : the_post(); ?>
<?php
$url = wp_get_attachment_url(get_post_thumbnail_id($post->ID));
$image = vt_resize(null, $url, 870, 400, true);
if (!$image['url']) $image['url'] = 'http://placehold.it/870x400&text=NO IMAGE';
?>
<div class="blog-post">
<h2><?php the_title();?></h2>
<div class="postmetadata">
<ul>
<li>
<i class="icon-calendar"></i><?php the_date();?>
</li>
<li>
<i class="icon-tags"></i> <?php echo get_the_tag_list();?>
</li>
</ul>
</div>
<img src="<?php echo $image['url'];?>">
<p><?php the_content('');?></p>
<?php if(!is_singular()):?>
<a class="btn btn-primary" href="<?php the_permalink();?>">Читать далее</a>
<?php else:?>
<a class="btn btn-primary" href="/novosti">Все новости</a>
<?php endif;?>
</div>
<?php endwhile; ?>
<div class="pagination">
<?php global $wp_query; wp_corenavi($wp_query);?>
</div>
<?php else: ?>
<p><?php _e('Sorry, no posts matched your criteria.'); ?></p>
<?php endif; ?>
</div>
<div class="span3">
<div class="side-bar">
<h3>Рубрики</h3>
<?php echo get_the_category_list(); ?>
</div>
<div class="side-bar">
<h3>Метки</h3>
<?php echo get_the_tag_list();?>
</div>
</div>
</div>
<?php get_footer(); ?>
Шаблон главной, шаблон страницы контактов (front-page.php, tpl-contacts.php).
Есть множество путей, самый простой — WP_Query бросить на слайдер, создав кастомный тип в админке, остальные элементы через глобальные настройки сайта. Единственный нюанс на странице контактов. Какой сайт без формы обратной связи? Я использую плагин Contact Form 7, интеграция верстки довольно простая. Все аналогично предыдущим пунктам, приводим генерируемый код в вид шаблона.
Делай раз-два-три.
- Скопировать верстку в редактор формы
- Заменить
<input class="input-xxlarge" type="text" placeholder="Name" />
на[text* name class:input-xxlarge akismet:author placeholder "Имя"]
- Настроить шаблон письма
Теперь у нас сайт, через который пользователи могут писать. Эту форму можно встроить в всплывающее окно, встроить на другие страницы etc. Сниппет для шаблонов:
<?php echo do_shortcode('[contact-form-7 id="4" title="Форма обратной связи"]');?>
Эндшпиль.
Деплоим сайт на сервер, наполняем контентом, высылаем клиенту доступы в админ-панель. Если необходимо — 20 минут консультаций по поводу администрирования и забываем вопросы про добавление страниц навсегда. Можно рассчитывать бюджет на seo/контекст.
А впереди еще целых 6 часов до конца рабочего дня.
Примечание. Да, данный сайт не является произведением искусства, но, есть один большой плюс. Этот сайт есть, и, на мой взгляд, он выглядит не совсем грустно. Дороже наша гипотетическая компания не смогла бы заказать по финансовым соображениям. И в следующий раз, когда у меня, моих знакомых, друзей, родственников, просто людей, сломается телефон, они смогут найти в интернете хоть что-то местное, а не сайты с кодом москвы в номере.
Результат.
Автор: Valeriy_tw3eX