前言

主题是EduSoho的插件之一,是通过HTML及JavaScript开发的可插拔的应用;主要是展现给用户良好的交互界面;此文档即对如何开发主题进行了详细的说明,供开发者参考使用。
EduSoho的主题样式是基于bootstrap定义的,所以要制作主题样式之前需要一定的bootstrap基础。 在bootstrap的基础上您可以制做任意风格、任意布局的主题,只要你能发挥出想像。

主题的目录结构说明

目录地址说明
web/themes主题的根目录
web/themes/autumnautumn为主题名称
web/themes/autumn/TopxiaWebBundle/views主页面目录
web/themes/autumn/csscss样式目录
web/themes/autumn/jsjavascript目录
web/themes/autumn/img图片资源目录

创建主题

1.创建主题的目录

此文档以建一个名为“天之蓝”的主题作为例子,创建一个名为blue的主题标准目录结构.
在blue目录下创建文件theme.json文件,内容如下:
  { 
    "name": "天之蓝 [主题名称]",
    "author": "drools [作者]",
    "version": "1.0 [版本号]",
    "supprot_version": "1.0+ [兼容EduSoho版本号]",
    "date": "2013-11-09 [发布日期]",
    "thumb": "img/theme.jpg [主题图标]"
  } 
EduSoho会自动扫描web/themes/XXX/theme.json文件,并将theme.json里面的数据显示到"管理后台"->"系统"->"全局设置"->"主题设置" 在“主题设置”中已经显示了刚刚定义的主题,点击“使用”后使用该主题。

2.创建布局

文件地址:web/themes/blue/TopxiaWebBundle/views/layout.html.twig
 {% import "TopxiaWebBundle::macro.html.twig" as web_macro %}
<!DOCTYPE html>
<!--[if lt IE 7]>      <html class="lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]>         <html class="lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]>         <html class="lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class=""> <!--<![endif]-->
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{% block title %} {{ setting('site.name', 'EDUSOHO') }} - {% if setting('site.slogan') %}{{ setting('site.slogan') }} -{% endif %} Powered by EDUSOHO{% endblock %}</title>
  <meta name="keywords" content="{% block keywords %}{% endblock %}" />
  <meta name="description" content="{% block description %}{% endblock %}" />
  <meta content="{{ csrf_token('site') }}" name="csrf-token" />
  {{ setting('login_bind.verify_code', '')|raw }}
  {% if setting('site.favicon') %}
  <link href="{{ asset(setting('site.favicon')) }}" rel="shortcut icon" />
  {% endif %}
  {% block stylesheets %}
    <link href="{{ asset('assets/libs/gallery2/bootstrap/3.1.1/css/bootstrap.css') }}" rel="stylesheet" />
    <link rel="stylesheet" media="screen" href="{{ asset('assets/css/common.css') }}" />
    <link rel="stylesheet" media="screen" href="{{ asset('assets/css/bootstrap-extends.css') }}" />
    
<!-- 在这加入自定义的CSS样式表 -->
<!--[if lt IE 8]> <link href="{{ asset('assets/css/oldie.css') }}" rel="stylesheet"> <![endif]--> {% endblock %} <!--[if lt IE 9]> <script src="{{ asset('assets/libs/html5shiv.js') }}"></script> <![endif]--> <!--[if IE 8]> <script src="{{ asset('assets/libs/respond.min.js') }}"></script> <![endif]--> {% block head_scripts %}{% endblock %} </head> <body {% if bodyClass|default('') %}class="{{ bodyClass }}"{% endif %}> {% block body %} <!-- 头部页面布局代码 --> {% block topbanner %}{% endblock %} {% block content %}{% endblock %} {% block bottom %}{% endblock %} <div id="login-modal" class="modal" data-url="{{ path('login_ajax') }}"></div> <div id="modal" class="modal"></div> {% endblock %} {% include 'TopxiaWebBundle::script_boot.html.twig' with {script_main: asset('bundles/topxiaweb/js/app.js')} %} </body> </html>
在这个布局文件<body>里面是空的,只有给模板是继承预留了block 定义头部布局

3.创建首页

文件地址:web/themes/blue/TopxiaWebBundle/views/Default/index.html.twig
{% extends 'TopxiaWebBundle:Default:layout.html.twig' %}

{% block topbanner %}{% endblock %}

{% block content %}{% endblock %}

{% block bottom %}{% endblock %}
一个空白的主题框架已经搭好了,现在访问首页会是一个空白界面。

制作主题

此文档将分头部、内容模板、底部来分别阐述如何制作一个完整的主题。

1.制作头部

头部界面布局的代码可以放在web/themes/blue/TopxiaWebBundle/views/layout.html.twig的{% block body %}标签里面。
头部界面示意: 这个头部界面可分为几块:网站Logo栏、搜索栏、登录栏、菜单栏,整个头部界面的布部如下:
<div class="head">
    <div class="container">
      <div class="row">
        <div id="head-top">
          <!--  网站Logo栏 -->
          <div id="header_logo" class="col-sm-3">
            {% if setting('site.logo') %}
                <a class="navbar-brand-logo" href="{{ path('homepage') }}"><img src="{{ asset(setting('site.logo')) }}"></a>
              {% else %}
                <a class="navbar-brand" href="{{ path('homepage') }}">{{ setting('site.name', 'EDUSOHO') }}</a>
              {% endif %}
          </div>
          <!--  搜索栏 -->
          <div id="search_block_top" class="col-sm-5 clearfix">
             <form id="searchbox" class="navbar-form navbar-left clearfix" role="search" action="/search">
                <div class="form-group">
                  <input type="search" id="search_query_top" placeholder="" name="q" maxLength="40">
                  <button type="submit" class="btn button-search iconfont  icon-sousuo1"></button>
                </div>
                
            </form>
          </div>
          <!--  登录栏 -->
          <div class="navbar-collapse collapse">
           <ul class="nav navbar-nav navbar-right">
                {% if app.user %}
                <li class="dropdown">

                    <a href="#" class="dropdown-toggle" data-toggle="dropdown">
                   <img class="img-circle" src="{{ file_path(app.user.smallAvatar, 'avatar.png') }}">

               <p>我</p></a>

                      <ul class="dropdown-menu">
                            <li><a href="{{ path('user_show', {id:app.user.id}) }}"><i class="glyphicon glyphicon-home"></i> 我的主页</a></li>
                            <li><a href="{{ path('settings') }}"><i class="glyphicon glyphicon-cog"></i> 帐号设置</a></li>
                            {% if is_granted('ROLE_ADMIN') %}
                            <li><a href="{{ path('admin') }}"><i class="glyphicon glyphicon-lock"></i> 管理后台</a></li>
                            {% endif %}
                            <li><a href="{{ path('logout') }}"><i class="glyphicon glyphicon-off"></i> 退出</a></li>
                      </ul>
                </li>

                <li><a href="{{ path('my') }}"> <span class="iconfont icon-iconbook"></span><p>课程</p></a></li>

               <li>
                <a href="{{ path('message') }}" class="badge-container message-badge-container">
                  <span class="iconfont  icon-email"></span>
                  <p>消息</p>
                  {% if app.user.newMessageNum > 0 %}<i class="badge">{{ app.user.newMessageNum }}</i>{% endif %}
                </a>
                </li>

                <li>
                      <a href="{{ path('notification') }}" class="badge-container notification-badge-container">
                      <span class="iconfont icon-wushenglaba"></span>
                     <p>通知</p>
                      {% if app.user.newNotificationNum > 0 %}<i class="badge">{{ app.user.newNotificationNum }}</i>{% endif %}</a>
                </li>

               {% else %}
                <li><a href="{{ path('login') }}"> <span class="iconfont icon-login"></span><p>登录</p></a></li>
                <li><a href="{{ path('register') }}"> <span class="iconfont icon-zhuce"></span><p>注册</p></a></li>
              {% endif %}
            </ul>
          </div>

        </div>
      </div>
      <!--  菜单栏 -->
      <div id="block_top_menu" class="row">
        <div class="stickUpTop" style="position: relative; top: 0px;">
          <div>
            <ul class="sf-menu clearfix menu-content">
              <li><a href="/" title="首页">首页</a></li>
              <li><a href="/course/explore" title="课程">课程</a></li>
              <li><a href="/" title="直播">直播</a></li>
              <li><a href="/teacher" title="教题团队">教题团队</a></li>
            </ul>
          </div>
        </div>
      </div>
    </div>
  </div>
代码详解:
  • 1.网站Logo栏:分析网站Logo栏的代码会发现,当管理员没有设置网站的Logo 时,会显示网站默认的Logo
  • 2.搜索栏:在搜索栏里面是一个form表单,表单中包含一个文件框和一个按钮
  • 3.登录栏:在登录栏里面有两种状态,在登录状态下{% if app.user %}显示我的个人中心、课程、消息、通知;在没有登录的情况下只显示“登录”,“注册”两个按钮。
  • 4.菜单栏:在菜单栏中放置几个连接作为菜单。

2.制作内容模板

在内容区域可以填充多个内容模板,每个内容模板尽量遵循简单、独立、易用的原则,以方便后续的升级维护等工作。在开发内容模板之前,您得先了解API文档,只有了解了每块数据如何获取和使用之后,才能开发出更好的应用。此文档列举了一些常用内容模板的开发作为例子。
2.1最新课程列表模板
创建web/themes/blue/TopxiaWebBundle/Default/category-course.html.twig文件作为模板文件。
{% import "TopxiaWebBundle::macro.html.twig" as web_macro %}
<!-- course 为模板定义一个样式es-box,主要申明布局的大小、边距等样式 -->
<div class="es-box es-course"> <h2>最新课程</h2>
<!-- 获取最新课程列表 LatestCourses -->
{% set count = 3 %} {% set categoryId = null %} {% set courses = data('LatestCourses',{'count':count, 'categoryId':categoryId}) %}
<!-- 当有课程数据时,显示课程 -->
{% if courses %} {% set mode = mode|default('default') %} <ul class="row">
<!-- 遍历Course -->
{% for course in courses %} <!-- 单个课程的布局 --> <li class="col-sm-6 col-md-4 es-course-list"> <div> <div class="course-top"> <img src="{{ file_path(course.largePicture, 'course-large.png') }}" class="img-responsive thumb"> </div> <div class="course-bottom"> <h4>{{ course.title }}</h4> <p class="metas clearfix"> {% if course.price > 0 %} <span class="price-num">¥{{ course.price }}</span> {% else %} <span class="price-num">免费</span> {% endif %} {% if course.showStudentNumType == 'opened' %} <span class="student-num">{{ course.studentNum }}人报名</span> {% endif %} </p> <span class="nickname"> {% for teacher in course.teachers %} {{ web_macro.user_link(teacher, 'text-muted') }} {% endfor %} </span> </div>
<!-- 当鼠标hover时显示mask里的信息,hover显示控制要在js中进行处理 -->
<div class="mask"> <a href="{{ path('course_show', {id:course.id}) }}"> {% set teacher = course.teachers|first %} <div class="teacher-info clearfix"> <img class="img-circle pull-left" src="{{ file_path(teacher.smallAvatar, 'avatar.png') }}"> <span>{{ teacher.nickname }}</span> <span>{{ teacher.title|plain_text(10) }}</span> </div> <p>{{ teacher.profile.about|plain_text(50) }}</p> <span class="iconfont icon-jiantou"></span> <h4 class="title">{{ course.title|plain_text(8) }}</h4> </a> </div> </div> </li> {% endfor %} </ul>
<!-- 当没有课程数据时,显示“还没有发布的课程” -->
{% else %} <div class="empty">还没有发布的课程</div> {% endif %} <a class="more" href="{{ path('course_explore',{category:config.categoryId|default(null)}) }}">更多></a> </div>
2.2推荐教师模板
创建web/themes/blue/TopxiaWebBundle/Default/recommend-teacher.html.twig文件作为模板文件。
{% import "TopxiaWebBundle::macro.html.twig" as web_macro %}
<!--  teacher -->
<div class="es-box teachers">
      <h2>{{ 教师风采 }}</h2>
      
<!-- 获取推荐教师数据 LatestCourses -->
{% set teachers = data('RecommendTeachers',{'count':10}) %} {% if teachers %} <ul class="row">
<!-- 遍历teachers -->
{% for teacher in teachers %} {% if teacher %} <li class="col-sm-6 col-md-4"> <div class="teachers-item"> <a href="{{ path('user_show', {id:teacher.id}) }}"> <img class="img-circle" src="{{ file_path(teacher.mediumAvatar, 'avatar.png') }}"> </a> <span>{{ web_macro.user_link(teacher) }}</span> <i> {{ teacher.title }}</i> <p> {{ teacher.about|plain_text(30) }}</p> </div> </li> {% endif %} {% endfor %} </ul>
<!-- 当没有课程数据时,显示"尚未设置推荐老师,请在管理后台设置" -->
{% else %} <div class="empty">尚未设置推荐老师,请在管理后台设置。</div> {% endif %} <a class="more" href="{{ path('teacher') }}">更多></a> </div>

3.制作底部

底部页面一般有如下几块内容组成:网站Logo、版权信息、底部导航、课程存档和备案信息。 创建web/themes/blue/TopxiaWebBundle/Default/complex-bottom.html.twig文件作为模板文件。
<div id="footer-sec" class="hidden-xs ">
  <div class="container clearfix  htmlcontenttop">

    <div class="copyright">
          
<!-- 网站Logo -->
{% if setting('site.logo') %} <a class="site-footer-logo" href="{{ path('homepage') }}"><img src="{{ asset(setting('site.logo')) }}"></a> {% else %} <a class="site-footer-logo" href="{{ path('homepage') }}">{{ setting('site.name', 'EDUSOHO') }}</a> {% endif %}
<!-- 版权信息 -->
<p>{% include "TopxiaWebBundle::powered-by.html.twig" %} </p> </div> <div class="footer-container">
<!-- 底部导航 -->
{{ render(controller('TopxiaWebBundle:Default:footNavigation')) }}
<!-- 课程存档 -->
<a href="{{ path('course_archive') }}">课程存档</a> {% if setting('site.copyright') %} 课程内容版权均归<a href="/">{{ setting('site.copyright') }}</a>所有 {% endif %}  
<!-- 备案信息 -->
{% if setting('site.icp') %} <a href="http://www.miibeian.gov.cn/" target="_blank">{{ setting('site.icp') }}</a> {% endif %}</p> {{ setting('site.analytics')|raw }} </div> </div> </div>
这个底部使用到的参数说明:

4.使用模板

模板的使用方式采用twig标记语法如下
  {% include 'TopxiaWebBundle:Default:category-course.html.twig' %} 
在主页面引用模板示例:
{% extends 'TopxiaWebBundle:Default:layout.html.twig' %}

{% block topbanner %}{% endblock %}
{% block content %}
  <div class="row">
    <div class="col-sm-9">
      {% include 'TopxiaWebBundle:Default:category-course.html.twig'%}
      {% include 'TopxiaWebBundle:Default:recommend-teacher.html.twig'%}
    </div>
  </div>
{% endblock %}

{% block bottom %}{% include 'TopxiaWebBundle:Default:complex-bottom.html.twig' %}{% endblock %}