クライアントから自社サイトを安く構築したいと相談を受けWordPressでやったのでメモしておく。
要件
- 無料テーマを改造して実装する。
環境
- Amazon Linux2
- Wordpress 5.2.2
- Visual Studio Code + SSHFS
環境構築
使いやすさでVisual Studio Codeを選択。SSHFSプラグインでLinuxサーバを直接いじる。これが快適でだいぶ生産性があがった。
Visual Studio Codeで拡張機能検索で「SSH FS」で検索してインストール。
Configurationを作成、入力するのは
- Host
- Port
- Root
- Username
- Private Key(AWSのpemファイルを指定)
である。
作成できたら右クリで接続。緑丸になれば接続成功。
後はVSCで直接いじれる。
実装
機能を大別すると
- 表向きのページ(いわゆるHP)
- 管理者ページ(記事投稿用)
がある。
Wordpressのインストールファイル
.
├── themes
│ └── bluestreet
├── wp-admin //管理者ページのルート
│ ├── css★
│ ├── images★
│ ├── includes
│ ├── js★
│ ├── maint
│ ├── network
│ └── user
├── wp-content //HPページのルート
│ ├── languages
│ ├── plugins★ プラグイン
│ ├── themes★
│ │ ├── bluestreet★ 親テーマのルート
│ │ └── wallstreet★ 子テーマのルート
│ ├── upgrade
│ └── uploads★ 記事投稿リソース保存先
└── wp-includes //インクルードリソース
├── blocks
├── certificates
├── css
├── customize
├── fonts
├── ID3
├── images
├── IXR
├── js
├── pomo
├── random_compat
├── Requests
├── rest-api
├── SimplePie
├── Text
├── theme-compat
└── widgets
★がついているところが主に手を入れたところ。
下記に要点を列挙する。
共通部品
グローバルヘッダー、グローバルフッター。
header.php、footer.phpをいじる。
ベースにしたテーマではSNSリンクがあったが使わないのでこれは外した。
コンタクト情報はDBのoptionから取る方式はそのままとした。
グローバルヘッダーはポップアップメニューとした。
<!-- PC版メニュー -->
<div class="navbar navbar-wrapper navbar-inverse navbar-static-top headerbar" role="navigation">
<!-- Brand and toggle get grouped for better mobile display -->
<div>
<ul class="navbar-header global-header-menu">
<li class="header_div_main">
<a href="<?php echo esc_url( get_option( 'ssl_url' ) ); ?>">
<div class="header_div_sub1">
<!--<span class="header_label1-1"><?php echo get_option('company_mainlogo'); ?></span>-->
<span class="header_label1-2"><?php echo get_option('company_sublogo'); ?></span>
</div>
<div class="header_div_sub2">
<span class="header_label2"><?php $l->g('company'); ?></span>
</div>
</a>
</li>
<li>
<ul class="header-menu-table">
<li class="header-menu-parent accodionArea">
<a href="<?php echo esc_url( get_option( 'ssl_url' ) ); ?>/company-introduction/">
<div class="accodionBtn">
<span class="header-menu-label1">会社案内</span>
</div>
</a>
<ul class="header-menu-children sub-menu-close sub-menu-hidden"> ★ここ以下がポップアップする。
<li>
<a href="<?php echo esc_url( get_option( 'ssl_url' ) ); ?>/company-introduction" class="sub-menu-label1">会社の理念</a>
</li>
<li>
@keyframes menu-fadeout {
0% {
opacity: 1;
margin-top: 0;
transform: translateY(0);
}
99% {
opacity: 0;
margin-top: 0;
transform: translateY(-50px);
}
100% {
opacity: 0;
margin-top: -200px;
}
}
.sub-menu-open {
animation: menu-fadein 0.5s forwards;
display: block !important;
}
.sub-menu-close {
animation: menu-fadeout 0.5s forwards;
}
.sub-menu-hidden {
display: none;
}
jQuery(function() {
jQuery('.accodionBtn').on('mouseenter', function() {
jQuery(this).parent().next(".header-menu-children").removeClass("sub-menu-close");
jQuery(this).parent().next(".header-menu-children").addClass("sub-menu-open");
});
jQuery('.accodionArea').on('mouseleave', function() {
jQuery(this).children(".header-menu-children").removeClass("sub-menu-open");
jQuery(this).children(".header-menu-children").addClass("sub-menu-close");
});
});
ページ内項目外出し化
基本的にDBのwp_optionに突っ込んでget_optionsで取得するようにする。
サブ機能の実装
ベースにしたテーマになかった「お知らせ」、「求人票」の投稿周りの機能をプラグインとして実装した。
functions.phpに実装してもできるとは思うが、プラグインだと有効かしたときにコンストラクタでDBテーブルを作成するなどの準備処理ができる。
規定のコメントヘッダーを入れることでWordpress管理画面のプラグイン一覧に載るようになる。
<?php
/*
Plugin Name: information function
Plugin URI:
Description: お知らせコンテンツ関係機能プラグイン
Author: fantec
Version: 1.0
Author URI:
*/
class informationFunction {
//テーブル名
var $table_name;
//コンストラクタ
public function __construct()
{
global $wpdb;
//接頭辞(wp_)を付けてテーブル名を設定
$this->table_name = $wpdb->prefix . 'informations';
//プラグイン有効化したとき実行
register_activation_hook (__FILE__, array($this, 'create_table'));
}
//初期化処理
function create_table() {
global $wpdb;
//DBのバージョン
$func_db_version = '1.0';
//現在のバージョン取得
$installed_ver = get_option( 'information_func_version' );
//DBバージョンが違ったら作成
if( $installed_ver != $func_db_version ) {
$sql = "CREATE TABLE " . $this->table_name . " (
info_id bigint(20) UNSIGNED DEFAULT '0' NOT NULL,
info_title text,
author_id int(10) DEFAULT NULL,
content text,
imgname text,
regist_date datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
update_date datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
UNIQUE KEY info_id (info_id)
)
CHARACTER SET 'utf8';";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
//オプションにDBバージョン保存
update_option('information_func_version', $func_db_version);
}
}
//全お知らせ取得
public function get_all() {
global $wpdb;
return $wpdb->get_results("SELECT * FROM wp_informations ORDER BY update_date DESC");
}
//お知らせ先頭n件取得
public function get_topn($n) {
global $wpdb;
return $wpdb->get_results("SELECT * FROM wp_informations ORDER BY update_date DESC LIMIT ".$n);
}
//一覧表示用お知らせ取得
//
public function get_page_list($page) {
global $wpdb;
$result = array();
//一覧ページあたり掲載数
$num_per_page = 5;
$tmp = $wpdb->get_results("SELECT * FROM wp_informations ORDER BY update_date DESC");
$result['data'] = array_slice($tmp, $num_per_page*($page-1), $num_per_page);
$result['maxpage'] = (count($tmp) % $num_per_page == 0) ? floor(count($tmp) / $num_per_page) : (floor(count($tmp) / $num_per_page))+1;
return $result;
}
//特定お知らせ取得
public function get_by_infoid($info_id) {
global $wpdb;
$result = $wpdb->get_results("SELECT * FROM wp_informations WHERE info_id = ".$info_id." ORDER BY update_date DESC");
return isset($result[0]) ? $result[0] : null;
}
//特定お知らせ新規登録
public function insert($info_title, $author_id, $content, $imgname) {
global $wpdb;
$datetime = new DateTime();
$datetime->setTimeZone( new DateTimeZone('Asia/Tokyo'));
$date_formated = $datetime->format('Y-m-d H:i:s');
$result = $wpdb->get_results("SELECT MAX(info_id) AS maxid FROM wp_informations");
$wpdb->insert('wp_informations',
array(
'info_id' => $result[0]->maxid+1,
'info_title' => $info_title,
'author_id' => $author_id,
'content' => $content,
'imgname' => $imgname,
'regist_date' => $date_formated,
'update_date' => $date_formated
));
return intval($result[0]->maxid+1);
}
//特定お知らせ更新(画像変更なし)
public function update_noimg($info_id, $info_title, $author_id, $content) {
global $wpdb;
$datetime = new DateTime();
$datetime->setTimeZone( new DateTimeZone('Asia/Tokyo'));
$update_date_formated = $datetime->format('Y-m-d H:i:s');
$wpdb->update('wp_informations',
array(
'info_title' => $info_title,
'author_id' => $author_id,
'content' => $content,
'update_date' => $update_date_formated
),
array( 'info_id' => $info_id ));
return intval($info_id);
}
//特定お知らせ更新
public function update($info_id, $info_title, $author_id, $content, $imgname) {
global $wpdb;
$datetime = new DateTime();
$datetime->setTimeZone( new DateTimeZone('Asia/Tokyo'));
$update_date_formated = $datetime->format('Y-m-d H:i:s');
$wpdb->update('wp_informations',
array(
'info_title' => $info_title,
'author_id' => $author_id,
'content' => $content,
'imgname' => $imgname,
'update_date' => $update_date_formated
),
array( 'info_id' => $info_id ));
return intval($info_id);
}
//画像ルートURI取得
public function get_img_root_uri() {
return content_url()."/uploads/informations/img/";
}
//画像ルートディレクトリ取得
public function get_img_root_dir() {
$upload_dir = wp_upload_dir();
return $upload_dir['basedir']."/informations/img/";
}
//未設定時画像パス取得
public function get_notsetimg_uri() {
return content_url()."/uploads/informations/img/noimage.jpg";
}
//文の省略加工
public function get_omission_str($str, $len) {
return mb_strlen($str) > $len ? mb_substr(esc_html($str), 0, $len)."..." : $str;
}
}
$incetance = new informationFunction;
利用側
<!-- AddThis Button END -->
<?php $wallstreet_pro_options=theme_data_setup();
$current_options = wp_parse_args( get_option( 'wallstreet_pro_options', array() ), $wallstreet_pro_options );
//お知らせ機能用
$infofunc = new informationFunction();
?>
<div class="information-section">
<div>
<div class="row">
<div class="section_heading_title">
<h1><?php echo esc_html(get_option('information_title')); ?></h1>
<div class="pagetitle-separator">
<div class="pagetitle-separator-border">
<div class="pagetitle-separator-box"></div>
</div>
</div>
</div>
</div>
<div class="row">
<?php
$infos = $infofunc->get_topn(3);
foreach($infos as $info) {
$info_id = isset($info->info_id) ? $info->info_id : null;
$title = isset($info->info_title) ? $info->info_title : null;
$content = isset($info->content) ? $info->content : null;
$imgname = isset($info->imgname) ? $info->imgname : null;
$update_date = isset($info->update_date) ? $info->update_date : null;
$datetime = new DateTime($update_date);
//$update_date_formated = $datetime->format('Y/m/d H:i:s');
$update_date_formated = $datetime->format('Y/m/d');
?>
<div class="information-outline">
<div class="headerdiv">
<span class="information-title"><?php echo esc_html($title); ?></span>
<div class="information-datetime">
<span>掲載日:<?php echo esc_html($update_date_formated); ?></span>
</div>
</div>
<div class="contentdiv">
<?php if ($imgname!=null) { ?>
<img src="<?php echo $infofunc->get_img_root_uri().esc_html($imgname); ?>" alt="" />
<?php } ?>
<span class="information-content"><?php echo $infofunc->get_omission_str(nl2br(esc_html($content)), 50); ?></span>
</div>
<form method="post" name="information_form_<?php echo $info_id ?>" action="<?php echo esc_url( get_option( 'ssl_url' ) ); ?>/information-detail/">
<input type="hidden" name="info_id" value="<?php echo $info_id ?>">
<div class="information-detail-button-div">
<a href="javascript:information_form_<?php echo $info_id ?>.submit()">
<div class="information-detail-button">
<span>詳細を見る</span>
</div>
</a>
</div>
</form>
</div>
<?php
}
?>
</div>
<div class="information-more-button-div">
<a href="<?php echo esc_url( get_option( 'ssl_url' ) ); ?>/information-list/">
<div class="information-more-button">
<span>お知らせをもっと見る</span>
</div>
</a>
</div>
</div>
</div>
<!-- /wallstreet Portfolio Section -->
問合せをメール送信
PHPMailerで実装。メール本文はテンプレートをキーワード置換で作成するようにした。
<?php
get_header();
/*
Template Name: 問い合わせ内容のメール送信ページ
*/
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'PHPMailer/src/Exception.php';
require 'PHPMailer/src/PHPMailer.php';
require 'PHPMailer/src/SMTP.php';
$inquiry_no;
$mail = new PHPMailer(true);
$is_spam = spam_check(); // スパム判定
$error = null;
try {
// サーバ設定
//$mail->SMTPDebug = 2; // デバッグモード。本番時はコメントアウトすること。
$mail->isSMTP();
$mail->Host = 'localhost';
$mail->SMTPAuth = true;
$mail->Username = 'xxx@bbb.cc'; // 送信アカウント
$mail->Password = 'xxxxxxx'; // 送信アカウントのパスワード
$mail->SMTPSecure = false; // TLSなどの暗号化非対応のサーバならfalseを設定。使えるなら'tls'か'ssl'を設定。
$mail->SMTPAutoTLS = false; // SMTPSecureをfalseにする場合はfalseにする。それ以外なら未設定で。
$mail->Port = 587;
// 送り先情報
$mail->setFrom('xxxx@bbb.ccc'); // 送信元アドレス
// $mail->addCC(); // CC
// $mail->addBCC(); // BCC
$mail->addAddress('aaa@xxx.cc'); // 送信先アドレス
$mail->addReplyTo('aaa@xxx.cc'); // 返信先アドレス
// 本文
$mail->isHTML(false);
$mail->CharSet = 'UTF-8';
$inquiry_type = $_POST['inquiry_type']; // 問合せ種別(joboffer:派遣求人票問合せ tradingcompany:派遣取引企業向け問合せ recruitment:自社採用情報問合せ)
// 置換対象を順に指定
$search = array('$$inquiry_no$$', '$$simei$$', '$$furi$$', '$$inquiry_ages$$', '$$inquiry_sex$$', '$$tel1$$', '$$tel2$$', '$$tel3$$',
'$$email$$', '$$offer_no$$', '$$inquiry_subject$$', '$$inquiry_content$$');
// 置き換える文字列を順に指定
$inquiry_no = make_inquiry_no("JO");
$replace = array($inquiry_no, $_POST['simei'], $_POST['furi'], $age, $sex, $_POST['tel1'], $_POST['tel2'], $_POST['tel3'],
$_POST['email'], $_POST['offer_no'], $subject, $_POST['inquiry_content']);
$mail->Body = str_replace($search, $replace, $template); // 本文
$mail->Subject = mb_encode_mimeheader($subject." 問い合わせ番号「".$inquiry_no."」"); // 件名
$mail->send();
} catch (Exception $e) {
echo $mail->ErrorInfo;
}
差出人: HP 「派遣のお取引企業」お問い合わせ自動送信フォーム
お問い合わせ番号:「$$inquiry_no$$」
【貴社名】:
$$company$$
【部署名】:
$$section$$
【ご担当者様のお名前】:
$$simei$$
【フリガナ】:
$$furi$$
【お電話番号】:
$$tel1$$ - $$tel2$$ - $$tel3$$
【FAX番号】:
$$fax1$$ - $$fax2$$ - $$fax3$$
【メールアドレス】:
$$email$$
【お問い合わせ件名】:
$$inquiry_subject$$
【お問い合わせ内容】:
$$inquiry_content$$
--
このメールは 株式会社xxxx (yyyyyy) の「派遣お取引企業様向けお問い合わせフォーム」から送信されました
管理者画面のメニュー操作
1. wp_enqueue_styleでリソースのインクルード。
2. remove_menu_pageで余計なmニューを削除。
3. add_menu_pageとadd_submenu_pageで独自なメニューを追加。
function bluestreet_custmizer_style()
{
wp_enqueue_style('bluestreet-customizer-css',get_stylesheet_directory_uri() .'/css/cust-style.css');
}
add_action('customize_controls_print_styles','bluestreet_custmizer_style');
/* 管理画面メニューのカスタマイズ */
define('MAX_FILE_SIZE', 1 * 1024 * 1024); //アップロード画像のサイズ制限(1MB)
define('JTAG_ON', 'on'); //求人タグの選択時データ
define('JTAG_OFF', 'off'); //求人タグの未選択時データ
if (is_user_logged_in()) {
function remove_admin_menu() {
remove_menu_page( 'index.php' ); // ダッシュボード
remove_menu_page( 'edit.php' ); // 投稿
remove_menu_page( 'upload.php' ); // メディア
remove_menu_page( 'edit.php?post_type=page' ); // 固定ページ
remove_menu_page( 'edit-comments.php' ); // コメントs
remove_menu_page( 'themes.php' ); // 外観
remove_menu_page( 'plugins.php' ); // プラグイン
remove_menu_page( 'users.php' ); // ユーザー
remove_menu_page( 'tools.php' ); // ツール
remove_menu_page( 'options-general.php' ); // 設定
}
$user = wp_get_current_user();
if($user->roles[0]!="administrator") {
/* 管理者以外なら不要な管理画面メニューを非表示にする */
add_action( 'admin_menu', 'remove_admin_menu', 999 );
}
//各カスタムページを出力するコールバック定義外部ファイル読み込み
require( get_template_directory().'/functions/my-admin/admin-info-list.php'); //お知らせ一覧
require( get_template_directory().'/functions/my-admin/admin-info-post.php'); //お知らせ編集
require( get_template_directory().'/functions/my-admin/admin-joffer-list.php'); //求人一覧
require( get_template_directory().'/functions/my-admin/admin-joffer-post.php'); //求人編集
require( get_template_directory().'/functions/my-admin/admin-joffertags-edit.php'); //求人タグ編集
require( get_template_directory().'/functions/my-admin/admin-joffertypes-edit.php'); //求人職種編集
require( get_template_directory().'/functions/my-admin/admin-jofferareas-edit.php'); //求人勤務地編集
require( get_template_directory().'/functions/my-admin/admin-recruitment-list.php'); //採用情報一覧
require( get_template_directory().'/functions/my-admin/admin-recruitment-post.php'); //求人編集
/* 独自メニューを追加 */
function add_admin_menu() {
//お知らせ機能
add_menu_page( 'お知らせ一覧', 'お知らせ', 'publish_posts', 'informations', 'admin_info_list', '', 0 );
add_submenu_page( 'informations', 'お知らせ編集', '新規登録', 'publish_posts', 'admin_info_post', 'admin_info_post');
//求人機能
add_menu_page( '求人一覧', '求人', 'publish_posts', 'joboffers', 'admin_joffer_list', '', 1 );
add_submenu_page( 'joboffers', '求人編集', '新規登録', 'publish_posts', 'admin_joffer_post', 'admin_joffer_post');
add_submenu_page( 'joboffers', 'タグ編集', 'タグ編集', 'publish_posts', 'admin_joffertags_edit', 'admin_joffertags_edit');
add_submenu_page( 'joboffers', '職種編集', '職種編集', 'publish_posts', 'admin_joffertypes_edit', 'admin_joffertypes_edit');
add_submenu_page( 'joboffers', '勤務地編集', '勤務地編集', 'publish_posts', 'admin_jofferareas_edit', 'admin_jofferareas_edit');
//採用情報機能
add_menu_page( '採用情報一覧', '採用情報', 'publish_posts', 'recruitment', 'admin_recruitment_list', '', 1 );
add_submenu_page( 'recruitment', '採用情報編集', '新規登録', 'publish_posts', 'admin_recruitment_post', 'admin_recruitment_post');
$user = wp_get_current_user();
if($user->roles[0]!="administrator") {
/* 管理者以外なら余計な部品は非表示にする */
wp_enqueue_style( 'hidden-parts-style', get_theme_root_uri()."/wallstreet/css/my-admin-hidden-parts.css" );
}
}
add_action('admin_menu', 'add_admin_menu');
}
画面遷移
普通に。
<div class="information-more-button-div">
<a href="<?php echo esc_url( get_option( 'ssl_url' ) ); ?>/information-list/">
<div class="information-more-button">
<span>お知らせをもっと見る</span>
</div>
</a>
</div>
POSTパラメータつき。
<form method="post" name="information_form_<?php echo $info_id ?>" action="<?php echo esc_url( get_option( 'ssl_url' ) ); ?>/information-detail/">
<input type="hidden" name="info_id" value="<?php echo $info_id ?>">
<div class="information-detail-button-div">
<a href="javascript:information_form_<?php echo $info_id ?>.submit()">
<div class="information-detail-button">
<span>詳細を見る</span>
</div>
</a>
</div>
</form>
一覧ページング
同一ページへの同期型POSTリクエスト方式
<div class="joboffer-paging-area">
<div class="row">
<form method="post" name="joboffer_list_form_prev" action="<?php echo esc_url( get_option( 'ssl_url' ) ); ?>/joboffer-list/">
<input type="hidden" name="page" value="<?php echo $page-1 ?>">
<input type="hidden" name="filter" value="<?php echo $filter ?>">
<a class="<?php echo $page>1 ? '':'vs-hidden'; ?>" href="javascript:joboffer_list_form_prev.submit()">
<div class="page-prev-button">
<span>前へ</span>
</div>
</a>
</form>
<?php for($i=1; $i<=$offers['maxpage']; $i++) { ?>
<form method="post" name="joboffer_list_form_<?php echo $i ?>" action="<?php echo esc_url( get_option( 'ssl_url' ) ); ?>/joboffer-list/">
<input type="hidden" name="page" value="<?php echo $i ?>">
<input type="hidden" name="filter" value="<?php echo $filter ?>">
<?php if($i!=$page) { ?>
<a href="javascript:joboffer_list_form_<?php echo $i ?>.submit()">
<div class="page-no-button">
<span><?php echo $i ?></span>
</div>
</a>
<?php } else { ?>
<div class="page-no-button page-this">
<span><?php echo $i ?></span>
</div>
<?php } ?>
</form>
<?php } ?>
<form method="post" name="joboffer_list_form_next" action="<?php echo esc_url( get_option( 'ssl_url' ) ); ?>/joboffer-list/">
<input type="hidden" name="page" value="<?php echo $page+1 ?>">
<input type="hidden" name="filter" value="<?php echo $filter ?>">
<a class="<?php echo $page<$offers['maxpage'] ? '':'vs-hidden'; ?>" href="javascript:joboffer_list_form_next.submit()">
<div class="page-next-button">
<span>次へ</span>
</div>
</a>
</form>
</div>
</div>