phuongnamcorpsintern / workspace Goto Github PK
View Code? Open in Web Editor NEWPhuong Nam Corps Internship Workspace
Phuong Nam Corps Internship Workspace
Bài viết này mình sẽ nói về cách sử dụng captcha trong sifoni một cách đơn giản nhất mà mình đã được được cha đẻ của sifoni truyền lại ......kkkkk
Tạo file Captcha.php
nằm trong thư mục controller
với nội dung sau
<?php
namespace App\Controller;
use Sifoni\Controller\Base;
class Captcha extends Base
{
private function genRandomString($length = 6) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyz';
$random = '';
for ($p = 0; $p < $length; $p++) {
$random .= $characters[mt_rand(0, strlen($characters))];
}
return $random;
}
private function getNewCaptcha($length = 6, $fontSize = 6)
{
header("Content-type: image/png");
$random = $this->genRandomString($length);
$this->app['session']->set('captcha_hash', md5($random));
$width = imagefontwidth($fontSize)* strlen($random) + 6;
$height = imagefontheight($fontSize) + 6;
$im = imagecreatetruecolor($width, $height);
$bgc = imagecolorallocate($im, 0, 0, 0);
$tc = imagecolorallocate($im, 255, 255, 255);
imagefilledrectangle($im, 0, 0, $width, $height, $bgc);
imagestring($im, $fontSize, 3, 3, $random, $tc);
imagepng($im);
exit();
}
public function renderAction() {
$this->getNewCaptcha();
}
}
Hàm genRandomString
nhằm tạo ra một chuỗi kí tự bất kì,hàm getNewCaptcha
giúp bạn tạo ra chuỗi captcha,lưu ý chúng ta sẽ sử dụng session
để lưu lại giá trị của captcha nhằm thuận lợi cho việc check captcha sau này(thay vì phải viết thêm hàm check captcha chúng ta sẽ kiểm tra trực tiếp với session )
Hàm renderAction
gọi 1 captcha mới,như vậy đã xong phần tạo captcha giờ tiến tới việc sử dụng
Bạn thêm dòng này vào file routing.php
'/captcha/new' => 'Captcha:render:captcha_render',
Giả sử bạn sử dụng captcha trong chức năng người dùng gửi phản hồi hay gửi bài về web,bạn có thể tạo ra 1 đoạn code nằm trong form như sau :
<div class="form-group">
<label for="text" class="col-sm-2 control-label">Captcha</label>
<div class="col-sm-10">
<img src="{{ url('captcha_render') }}" />
<input type="text" value="" name="captcha"></input>
</div>
</div>
Với <img src="{{ url('captcha_render') }}" />
gọi ra captcha và <input type="text" value="" name="captcha"></input>
để người dùng nhập giá trị vào
Tại `controller``
$frmData = $this->getPostData();
$captcha = md5($frmData['captcha']);
if ($captcha == $this->app['session']->get('captcha_hash','null')) {
}
Bạn tiến hành get giá trị từ form về bình thường sau đó kiểm giá trị đó với session chứa captcha đã set từ trước...
Chúc bạn thành công !
Ở cuối bài viết hay có Tag hoặc các từ khóa. Làm thế nào để tạo và xử lý các tag cho nhẹ nhàng, thanh thoát và mỹ miều nhất là vấn đề được đề cập ở issue này.
Chúng ta cần phải tạo ra 3 table:
Articles (bài viết)
Title (varchar)
Content (text)
Tags (varchar)
Tags
ID (int)
Name (varchar)
Articles-Tags
article_id (int)
tag_id (int)
Trong view để nhập bài viết ta tạo input:
<input type="text" name="tags">
Insert
Yêu cầu người sử dụng nhập các tags cách nhau bằng dấu phẩy.
Ví dụ cho trực quan đơn giản dễ hiểu:
//Insert bài viết
mysql_query(“INSERT INTO articles (title, content, tags) values ($_POST[title], $_POST[content], $_POST[tags])”);
//Lấy id của bài viết vừa mới thêm vào
$idArticle = mysql_insert_id();
//Cắt chuổi tags đưa vào mảng
$arrTags = explode(“,”, $_POST['tags']);
//Duyệt từng phần tử của Tags
foreach ($arrTags as $tag)
{
//Hàm trim để cắt bỏ khoảng trắng
$tag = trim($tag);
//Lấy id của tag có tên là $tag, nếu ko có thì thêm mới
$result = mysql_query(“select id from tags where name= ‘$tag’ limit 0,1 ”);
if (mysql_num_rows($result) > 0)
{
$idTag = mysql_result($result, 0, 0);
}
else
{
mysql_query(“insert into tags(name) values ($tag)”);
$idTag = mysql_insert_id();
}
//Insert dữ liệu vào table Articles_Tags
mysql_query(“insert into articles_tags value ($idArticle, $idTag)”);
}
Update
Ví dụ:
//Giả sử id của bài viết đang sửa là: $idCurrent;
//Delete dữ liệu bên table Articles_Tags
mysql_query(“DELETE FROM articles_tags where article_id = $idCurrent”);
//Update bài viết
mysql_query(“UPDATE articles SET title =…, content = … WHERE ID = $idCurrent”);
//Cắt chuổi tags đưa vào mảng
$arrTags = explode(“,”, $_POST['tags']);
//Duyệt từng phần tử của Tags
foreach ($arrTags as $tag)
{
$tag = trim($tag);
//Lấy id của tag có tên là $tag, nếu không có thì thêm mới
$result = mysql_query(“select id from tags where name= ‘$tag’ limit 0,1 ”);
if (mysql_num_rows($result) > 0)
{
$idTag = mysql_result($result, 0, 0);
}
else
{
mysql_query(“insert into tags(name) values ($tag)”);
$idTag = mysql_insert_id();
}
//Insert dữ liệu vào table Articles_Tags
mysql_query(“insert into articles_tags value ($idCurrent, $idTag)”);
}
Delete
Rất đơn giản, ta chỉ cần xóa hết thông tin liên quan đến bài viết trong cả 3 table.
Trên đây là hướng dẫn tạo tag mỹ miều yêu kiều và cơ bản. Chúc các bạn tag tốt! 💞 💫✨😆
Lưu ý: Tất cả cú pháp viết xong đều nhấn phím Tab để thực thi lệnh.
Sử dụng dấu chấm thang: ! hoặc html5
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
</body>
</html>
Chỉ cần viết tên thẻ + phím Tab
<p></p>
<a href=""></a>
<img src="" alt="">
...
div#header
<div id="header"></div>
div.banner
<div class="banner"></div>
Sử dụng dấu * nhân với số thẻ
p*5
<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
a.link*5
<a href="" class="link"></a>
<a href="" class="link"></a>
<a href="" class="link"></a>
<a href="" class="link"></a>
<a href="" class="link"></a>
Sử dụng dấu >
ul>li
<ul>
<li></li>
</ul>
ul>li*5
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
Sử dụng cặp dấu [] với cú pháp sau: tên thẻ[thuộc tính=giá trị]:
a[href=# target=_blank]
<a href="#" target="_blank"></a>
input[type=text name=email placeholder='your email']
<input type="text" name="email" placeholder="your email">
Sử dụng cặp dấu {}
p{Đây là nội dung thẻ p}
<p>Đây là nội dung thẻ p</p>
lorem
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sed nesciunt dolor eos soluta at illum adipisci commodi quia distinctio sint, veniam officiis quaerat ullam delectus autem blanditiis ab esse cupiditate.
Có thể sử dụng số lượng từ trong nội dung phát sinh tùy theo nhu cầu người sử dụng
lorem10
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Velit, deserunt.
lorem20
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sit optio ea, numquam pariatur recusandae modi rem ipsa magnam quaerat amet!
Sử dụng dấu $
ul>li*3>a.item${Menu$}
<ul>
<li><a href="" class="item1">Menu1</a></li>
<li><a href="" class="item2">Menu2</a></li>
<li><a href="" class="item3">Menu3</a></li>
</ul>
h$[title=item$]{Header $}*3
<h1 title="item1">Header 1</h1>
<h2 title="item2">Header 2</h2>
<h3 title="item3">Header 3</h3>
Như các bạn thấy, dấu $ được tự động đánh số theo giá trị tăng dần từ 1 đến 3
Để đánh số bắt đầu từ số bất kì, sử dụng: $@i => với i là số bất kì bạn muốn bắt đầu
ul>li*3>a.item$@4{Menu$@}
<ul>
<li><a href="" class="item4">Menu4</a></li>
<li><a href="" class="item5">Menu5</a></li>
<li><a href="" class="item6">Menu6</a></li>
</ul>
Để số thứ tự nội dung hoặc thuộc tính giảm dần, sử dụng $@-
ul>li*3>a.item$@-{Menu$@-}
<ul>
<li><a href="" class="item3">Menu3</a></li>
<li><a href="" class="item2">Menu2</a></li>
<li><a href="" class="item1">Menu1</a></li>
</ul>
Sử dụng dấu + để biểu diễn các Tag ngang hàng nhau
p+a+img
<p></p>
<a href=""></a>
<img src="" alt="">
.content>.content-left+.content-right
<div class="content">
<div class="content-left"></div>
<div class="content-right"></div>
</div>
Sử dụng cặp dấu ngoặc tròn () để gom nhóm những tag cùng bậc
.content>(.content-left>a[href=#]{Content-left})+(.content-right>p>lorem10)
<div class="content">
<div class="content-left"><a href="#">Content-left</a></div>
<div class="content-right">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ipsa, aliquid!</p>
</div>
</div>
.news.(.new-images>a[href=#]>img[src=#])+(.new-name>(h3{Title name})+(p>lorem10))
<div class="news ">
<div class="new-images"><a href="#"><img src="#" alt=""></a></div>
</div>
<div class="new-name">
<h3>Title name</h3>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Qui, quia?</p>
</div>
Sử dụng dấu ^ để lùi thẻ ra một bậc trước đó. Tùy từng trường hợp, có thể sử dụng dấu ^ thay cho + sẽ giúp cho đoạn code đơn giản và nhanh hơn.
.header>a>img^p>lorem10
<div class="header">
<a href=""><img src="" alt=""></a>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sapiente, tenetur.</p>
</div>
.class
<div class="class"></div>
em>.class
<em><span class="class"></span></em>
ul>.class
<ul>
<li class="class"></li>
</ul>
table>.row>.col
<table>
<tr class="row">
<td class="col"></td>
</tr>
</table>
Trên đây là những cú pháp cơ bản nhất của Emmet nhằm hỗ trợ việc code HTML. Còn rất nhiều cách viết hay nữa của Emmet. Để biết thêm, các bạn có thể tham khảo tại trang: http://docs.emmet.io/cheat-sheet/
http://2015vuicuoi.net/post/41
điều này rất mất quan đô thị :)))) và không đảm bảo sự chuyên nghiệphttp://vnexpress.net/tin-tuc/the-gioi/tu-lieu/chiec-xe-bieu-tuong-moi-cho-suc-manh-my-tren-chien-truong-3274441.html
B1: Cài đặt thư viện của slugify thông qua composer
** Mở file composer.json
thêm đoạn code này :
"cocur/slugify": "~1.0"
vào require
,sau khi thêm sẽ có dạng :
"require": {
"php": ">=5.4.0",
"sifoni/sifoni": "~2.0",
"knplabs/console-service-provider": "~1.0",
"facebook/php-sdk-v4": "~5.0",
"cocur/slugify": "~1.0"
},
B2: Chạy update composer thông qua lệnh : composer update -vvv
B3: Mở file hook.php
trong folder config
và thêm đoạn code này :
$app->register(new Cocur\Slugify\Bridge\Silex\SlugifyServiceProvider());
Như vậy bạn đã hoàn tất việc cài đặt,bây giờ tiến hành sử dụng :
VD : Mình tạo url cho post khi thêm bài viết thông qua title thì tiến hành như sau :
Trong controller
tiến hành lấy giá trị từ form về như bình thường : $input = $this->getPostData();
Bây giờ mình tạo ra 1 biến url để lưu slug :
$url= $this->app['slugify']->slugify($input['title']);
Như vậy biến $url đang lưu url dưới dạng chúng ta cần
VD : Nhập tiêu đề là "Ai BD số 1 công ty" -> biến $url ="ai-bd-so-1-cong-ty"
Tiến hành add vào csdl như bình thường (Lưu ý các bạn phải tạo ra 1 cột trong table để lưu trữ biến $url)
Link tham khảo : https://github.com/cocur/slugify
Khì tác vụ cần chạy nhiều dòng insert, update (vài trăm dòng :D) như Import từ file Excel. Thì ta đặt toàn bộ code xữ lý của Model giữa 2 câu lệnh sau.
DB::beginTransaction();
// code model
DB::commit();
use Sifoni\Model\DB;
Mới thử insert 1000 dòng mà mất có 2 3 giây :D
Khi chúng ta viết 1 đoạn code ajax có chứa đường dẫn url tự động phát sinh, ví dụ:
<script type="text/javascript">
$('.add-to-cart').on('click', function ()
{
var idp = $(this).data('id');
$.ajax({
url: '{{ url('cartadd') }}',
type : 'post',
dataType : 'text',
data: {
id: idp,
},
success : function (result){
$('#cart-info').html(result);
}
});
});
mặc định giao thức của đường dẫn url: '{{ url('cartadd') }}
là http. Khi up lên c9.io sẽ tự động bị sửa đổi thành https, và như thế đường dẫn thay đổi, không thể thực thi được.
Ta thêm các dòng code sau vào file hook.php
, và chỉ cần thêm vào file trên c9.io, không cần thêm vào khi thực thi ở localhost:
<?php
// Only C9.io
use Symfony\Component\HttpFoundation\Request;
$app = \Sifoni\Engine::getInstance()->getApp();
$app->register(new Cocur\Slugify\Bridge\Silex\SlugifyServiceProvider());
// Hook anything you want :)
// Only C9.io
Request::setTrustedProxies(array('10.0.0.0/8'));
$app['request_context']->setScheme('https');
Như vậy các đường dẫn của chúng ta đã có thể thực thi bình thường. Goodluck!
Có 2 vấn đề cần giúp đỡ. hjx mọi người giúp với nhen! ( Fw sifoni)
form style="float:left" method="post" action="danh_muc"
input type="hidden" value="{{c.id}}" name="id"
button type="submit" class="gree"
2 vấn đề:
thanks !
Các bạn nên refactor những đoạn code có số lần lặp lại > 2 lần
Ví dụ điển hình là viết các controller admin, các bạn thường viết đoạn sau đầu mỗi method (quá dư thừa, cần refactor lại)
$nameAdmin = $this->app['session']->get('adminName', '1');
if ($nameAdmin == 1) {
return $this->redirect('login-admin');
}
Giải pháp : dùng OOP chẳng hạn
AuthedController.php
có nội dung như sau<?php
abstract class AuthedController extends Base {
protected $authed = null;
public function __construct() {
parent::__construct();
if ($authed = $this->app['session']->get('admin_authed', false)) {
$this->authed = $authed;
} else {
$response = $this->redirect('admin_auth_signin');
$response->send();
return $this->app->terminate($this->request, $response);
}
}
}
với admin_auth_signin
là route Signin vào admin , vào $this->authed
sẽ là biến lưu instance User
đăng nhập vào hệ thống mà trong Controller Signin $this->app['session']->set('admin_authed', $user);
Công việc là chỉ cần extends AuthedController
những Controller muốn bảo vệ Admin là xong. ví dụ
<?php
namespace App\Controller\Admin;
class CategoryController extends AuthedController {
}
http://vietuc.smo.vn/vi/
,http://vietuc.smo.vn/en/
. \vi
\en
là biến $lang dùng để phân biệt ngôn ngữ của người dùng mà render đúng với ngôn ngữ đó.tìm kiếm
của search kết nối với chúng tôi
ở footer, ... .Thông thường chúng ta sẽ dùng if
trong view để làm cho việc nài. Nhưng nếu site mình có 100 thì sao 😄 .Để giải quyết vấn đề nài chúng ta chỉ cần......
Xem phần bên dưới ⬇️
Thêm vào đoạn code sau :
$app->before(function (Request $request, Application $app) {
$request->setDefaultLocale($app['config.app.languages'][0]);
$app['request_context']->setParameter('_locale', $request->getLocale());
}, 8); // 1 - 15
Thêm đoạn code sau :
return array(
'languages' => array('vi', 'en', 'ja', 'zh')
Trong đó 'vi', 'en', 'ja', 'cn'
là ngôn ngữ dùng trong site ,mặc định khi vào http://vietuc.smo.vn/
thì $lang = 'vi'
.
Đến đây thì trong routing
mình không cần quan tâm biến $lang nữa nhé . Mình sẽ đặt routing như site 1 ngôn ngữ.
Tạo CommonController.php
abstract class CommonController extends Base{
public $data = [];
public function __construct() {
parent::__construct();
$this->data = $this->Block();
$this->data['lang'] = $this->lang = $this->request->getLocale(); // lấy $lang
$this->app['translator']->setLocale($this->lang); // dùng {{ |trans }} trong view
}
Khi sử dụng các Controller khác thì extends CommonController
để lấy data['lang']
. Có data['lang']
rồi thì tự tin index trên database nhé.
Tìm kiếm
, Liên lạc với chúng tôi
, VĂN PHÒNG
, ... sang tiếng anh. Sau đó cho đoạn code nài vào :return [
'messages' => [
'search ' => 'Search', // key dùng trong view
'contact_us' =>'Contact Us', // value hiển thị
'office' => 'Office',
],
'validators' => []
];
key
chữ thường , khoảng trống thay bằng _
/config/app.php
{{ 'office'| trans }}
Hãy test thử ngay để thấy sự tuyệt vời và tiện lợi của đa ngôn ngữ trong sifoni 😺
Chúng ta có một mảng như sau :
Array
(
[1] => Array
(
[id] => 1
[name] => Tin Tức
[parent_id] => 0
)
[2] => Array
(
[id] => 2
[name] => Công Nghệ
[parent_id] => 0
)
[3] => Array
(
[id] => 3
[name] => Tin Trong Nước
[parent_id] => 1
)
[4] => Array
(
[id] => 4
[name] => Tin Nước Ngoài
[parent_id] => 1
)
[5] => Array
(
[id] => 5
[name] => Điện Thoại
[parent_id] => 2
)
[6] => Array
(
[id] => 6
[name] => Laptop
[parent_id] => 2
Mảng này rất hay gặp khi chúng ta truy vấn dữ liệu từ bảng category
ra để load dữ liệu lên menu
.Với menu cấp 1 chính là những thành phần có parent_id
= 0 và menu con của nó là những thành phần có parent_id
= id
danh mục chính.
Giờ ta làm gọn lại mảng này để có dạng như sau :
Array
(
1 => Array
(
[id] => 1
[parent_id] => 0
[name] => Tin Tức
[children] => Array
(
3=> Array
(
[id] => 3
[name] => Tin Trong Nước
[parent_id] => 1
4 => Array
(
[id] => 4
[name] => Tin Trong Quốc Tế
[parent_id] => 1
)
)
2 => Array
(
[id] => 2
[name] => Công Nghệ
[parent_id] => 0
[children] => Array
(
5 => Array
(
[id] => 5
[name] => Điện Thoại
[parent_id] => 2
)
)
(
6 => Array
(
[id] => 6
[name] => Laptop
[parent_id] => 2
)
)
)
Sử dụng build Tree (thực tế là kỹ thuật lập trình đệ quy).Các bạn có thể tìm hiểu thêm về kỹ thuật lập trình đệ quy để hiểu hơn về phương pháp này.
Ở đây mình lấy 1 ví dụ mà hầu như mọi người hay sử dụng đó là load menu cho website
B1. Xây dựng hàm buildTree
Mở file hook.php
trong thư mục config
thêm đoạn code có nội dung như sau :
function buildTree(array $elements, $parentId = 0, $parent_id_field = 'parent_id') {
$branch = array();
foreach ($elements as $element) {
if ($element[$parent_id_field] == $parentId) {
$children = buildTree($elements, $element['id']);
$element['children'] = empty($children) ? [] : $children;
$branch[] = $element;
}
}
return $branch;
}
Hàm này có 4 thông số truyền vào trong đó $elements chính là toàn bộ dữ liệu lấy ra ở bảng category
dưới dạng mảng
B2. Sử dụng buildTree
để gom dữ liệu trong model
Trong model category chúng ta tạo hàm 1 như sau :
public function getTreeMenu() {
$cats = Category::where('status','=','1')->get()->toArray();
$tree = buildTree($cats);
return $tree;
}
B3. Controller lấy dữ liệu rồi đưa ra view
Trong Controller bạn lấy dữ liệu menu bằng $data['cats'] = Category::getTreeMenu();
Ở đây
mình sử dụng 1 file BlockController riêng để xử lý dữ liệu riêng cho những phần layout cố đinh.
<?php
namespace App\Controller;
use Sifoni\Controller\Base;
use App\Model\Post;
use App\Model\Category;
class BlockController extends Base
{
public function renderMainMenuAction() {
$data['cats'] = Category::getTreeMenu();
return $this->render('default/block/main_menu.html.twig', $data);
}
Như vậy dữ liệu xuất ra tại main_menu.html.twig
.Dưới đây là mã code mình dùng
<div class="container">
<ul>
{% for c in cats %}
<li><a href="/threads/{{c.target}}">{{c.name}}</a>
<ul>
{% for cc in c['children'] %}
<li><a href="/threads/{{cc.target}}">{{cc.name}}</a></li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
</div>
Mục đích mình sử dụng file main_menu.html.twig
để render dữ liệu cho menu là để làm nó tách biệt cũng như giảm tải dòng code.Còn nếu các bạn không muốn phức tạp thì có thể lấy dữ liệu menu bằng $data['cats'] = Category::getTreeMenu();
trong controller và đỗ dữ liệu ra view bình thường.Nếu bạn sử dụng cách này thì có thể dừng lại tại đây là không cần làm tiếp bước dưới.
B4. Tạo routing render menu
Ở file routing.php
bạn thêm dòng sau
'/_block/main_menu' => 'BlockController:renderMainMenu:block_main_menu',
B5. Đưa vùng render riêng cho menu vào layout
Ở file phân vùng block cho layout bạn gọi tới routing render cho menu.VD :
{% endblock %}
{% block navmenu %}
{{ render(url('block_main_menu')) }}
{% endblock %}
Như vậy bạn đã có được một cách để đỗ dự liệu cho menu mới.
Các bạn có thể sử dụng phương pháp này để áp dụng với trường hợp load các bài post theo category
Chúc các bạn thành công !
npm install gulp -g
npm install gulp --save-dev
var gulp = require('gulp');
gulp.task('default', function(){
// Default task code
});
gulp.task('tên-task', function() {
// xử lý task
});
gulp tên-task
npm install browser-sync --save-dev
như hình sau:var browserSync = require('browser-sync');
gulp.task('serve', [], function () {
browserSync({
notify: false,
server: {
baseDir: '.'
}
});
gulp.watch(['*.html'], reload);
gulp.watch(['assets/js/*.js'], reload);
gulp.watch(['assets/css/*.css'], reload);
gulp.watch(['assets/images/*.css'], reload);
});
gulp serve
npm install gulp-minify-css --save-dev
npm install gulp-minify --save-dev
npm install gulp-imagemin --save-dev
npm install imagemin-pngquant --save-dev
gulp.task('compress', function() {
//cấu hình minify js
gulp.src('assets/js/*.js') //đường dẫn đến thư mục chứa các file js
.pipe(minify({
exclude: ['tasks'],
ignoreFiles: ['-min.js'] //những file không muốn nén
}))
.pipe(gulp.dest('dist/js')); //thư mục dùng để chứa các file js sau khi nén
//cấu hình minify css
gulp.src('assets/css/*.css') //đường dẫn đến thư mục chứa các file css
.pipe(minifyCss({compatibility: 'ie8'}))
.pipe(gulp.dest('dist/css')); //thư mục dùng để chứa các file css sau khi nén
//cấu hình minify image
gulp.src('assets/images/*') //đường dẫn đến thư mục chứa các file images
.pipe(imagemin({
progressive: true,
svgoPlugins: [{removeViewBox: false}],
use: [pngquant()]
}))
.pipe(gulp.dest('dist/images')); //thư mục dùng để chứa các file images sau khi nén
});
gulp compress
var gulp = require('gulp');
var browserSync = require('browser-sync');
var reload = browserSync.reload;
var minify = require('gulp-minify');
var minifyCss = require('gulp-minify-css');
var imagemin = require('gulp-imagemin');
var pngquant = require('imagemin-pngquant');
gulp.task('serve', [], function () {
browserSync({
notify: false,
server: {
baseDir: '.'
}
});
gulp.watch(['*.html'], reload);
gulp.watch(['assets/js/*.js'], reload);
gulp.watch(['assets/css/*.css'], reload);
gulp.watch(['assets/images/*.css'], reload);
});
gulp.task('compress', function() {
//cấu hình minify js
gulp.src('assets/js/*.js') //đường dẫn đến thư mục chứa các file js
.pipe(minify({
exclude: ['tasks'],
ignoreFiles: ['-min.js'] //những file không muốn nén
}))
.pipe(gulp.dest('dist/js')); //thư mục dùng để chứa các file js sau khi nén
//cấu hình minify css
gulp.src('assets/css/*.css') //đường dẫn đến thư mục chứa các file css
.pipe(minifyCss({compatibility: 'ie8'}))
.pipe(gulp.dest('dist/css')); //thư mục dùng để chứa các file css sau khi nén
//cấu hình minify image
gulp.src('assets/images/*') //đường dẫn đến thư mục chứa các file images
.pipe(imagemin({
progressive: true,
svgoPlugins: [{removeViewBox: false}],
use: [pngquant()]
}))
.pipe(gulp.dest('dist/images')); //thư mục dùng để chứa các file images sau khi nén
});
{
"name" : "chat-realtime",
"version" : "0.0.1",
"private" : "true",
"dependencies" : {
"socket.io" : "1.4.5",
"express": "4.13.4"
}
}
npm install
để tiến hành cài đặt.var express = require('express'),
app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server),
port = process.env.PORT || 3000;
server.listen(port);
io.sockets.on('connection', function(socket) {
// code xử lý khi client kết nối đến server
// gửi dữ liện cho client
io.to(socket.id).emit('event_name', data);
// gửi dữ liệu cho toàn bộ client
io.sockets.emit('event_name', data);
// client join 1 room
socket.join(room);
// client leave 1 room
socket.leave(room);
});
io.sockets.on('disconnect', function(socket) {
// code xử lý khi client ngắt kết nối đến server
});
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect();
socket.on('event_name', function(data) {
// code xữ lý khi nhận được emit từ Server
});
// emit đến Server
socket.emit('event_name', data);
</script>
node app.js
Để tối ưu hóa hình ảnh (một số hình ảnh được up lên tám chục MB mà lúc lấy ra sài thỉ chỉ có 200px (chỉ còn khoảng 80 KB), thế là hoang phí cuộc đời.
Để tối ưu ta có thể dùng TimThumb một cách khéo léo mà không để lại tí link xấu nào như http://abc.xyz/timthumb.php?src=Hinh_Bay_Ba_Cam_Con_Nit.jpg&w=800&h=600
OKAY ! Lets start !!!
thumb_cache
ở DocumentRoot (nếu là Sifoni
thì DocumentRoot = web
, còn lại thì public_html
)_timthumb_.php
với nội dung như file này.htaccess
ra, copy đoạn sau bỏ vào sau dòng RewriteEngine On
, nhớ có 2 dòng trắng đầu và cuối bên dưới nhé
RewriteRule ^thumbw\/(\d+)\/(.*)$ _timthumb_.php?src=$2&w=$1 [QSA,L]
RewriteRule ^thumbh\/(\d+)\/(.*)$ _timthumb_.php?src=$2&h=$1 [QSA,L]
RewriteRule ^thumb\/(\d+)\/(\d+)\/(.*)$ _timthumb_.php?src=$3&w=$1&h=$2 [QSA,L]
/uploads/Hinh_Bay_Ba.jpg
thành /thumb/RONG/CAO/uploads/Hinh_Bay_Ba.jpg
/uploads/Hinh_Bay_Ba.jpg
thành /thumbw/RONG/uploads/Hinh_Bay_Ba.jpg
/uploads/Hinh_Bay_Ba.jpg
thành /thumbh/CAO/uploads/Hinh_Bay_Ba.jpg
Lưu ý : RONG
và CAO
là số nguyên zin, không bỏ thêm bất kỳ ký tự gì như px
, cm
Một trang admin quản lý hệ thống và nội dung phức tạp yêu cầu có nhiều nhóm người dùng với các quyền hạn khác nhau về việc truy cập cũng như thực hiện các chức năng.
app/config/hook.php
...
// define Permission CONST
define('SUAD', 'super admin');
define('ADMIN', 'admin');
define('EDITOR', 'editor');
define('AUTHOR', 'author');
define('USER', 'user');
app/model/admin/Permission.php
<?php
namespace App\Model\admin;
use Sifoni\Model\Base;
use App\Model\admin\User;
class Permission extends Base{
static function checkPermission($permit, $cur_username, $cur_user_permit){
$user=User::where('username', $cur_username)
->where('permission', $cur_user_permit)->first();
if($user){
switch ($permit) {
case SUAD:
if($cur_user_permit==SUAD)
return true;
break;
case ADMIN:
if(in_array($cur_user_permit, [SUAD, ADMIN]))
return true;
break;
case EDITOR:
if(in_array($cur_user_permit, [SUAD, ADMIN, EDITOR]))
return true;
break;
case AUTHOR:
if(in_array($cur_user_permit, [SUAD, ADMIN, EDITOR, AUTHOR]))
return true;
break;
default:
break;
}
}
return false;
}
}
app/controller/admin/PermissionController.php
để kế thừa và sử dụng bởi các controller
khác<?php
namespace App\Controller\admin;
use App\Model\admin\Permission;
use App\Controller\admin\BlockController;
class PermissionController extends BlockController{
static $permit=AUTHOR;
/*
* static::$permit : Giấy phép người dùng thấp nhất cho chức năng muốn phân quyền.
* $this->app['session']->get('username') : Session chứa username đã đăng nhập.
* $this->app['session']->get('user_permission') : Session chứa permission của username đó.
*/
public function __construct() {
parent::__construct();
if(!Permission::checkPermission(static::$permit, $this->app['session']->get('username'), $this->app['session']->get('user_permission'))){
$response = $this->redirect('error_permission');
$response->send();
return $this->app->terminate($this->request, $response);
}
}
}
error_permission
(hay tên nào khác cũng được) và trang báo lỗi. Chú ý: function trả về của route này không được nằm trong controller
có kế thừa PermissionController
.controller
:Ví dụ UserController
kế thừa PermissionController
ở trên:
...
class UserController extends PermissionController{
static $permit=SUAD;
...
}
Gán giá trị cho $permit
là nhóm người dùng thấp nhất mà bạn muốn cho phép sử dụng tất cả các function nằm trong class UserController
function
:Vẫn cho UserController
kế thừa PermissionController
nhưng không khai báo biến $permit
nữa
Thay vào đó, trong function muốn phân quyền, ta thêm vào như sau:
class UserController extends PermissionController{
function createAction(){
static::$permit=SUAD;
parent::__construct();
// your code
}
...
}
Vậy là bạn đã phân quyền cho function createAction
chỉ có nhóm người dùng SUAD
mới được phép sử dụng.
Lưu ý: Nhóm người dùng có quyền cao hơn được quyền sử dụng tất cả các
function
được phân quyền cho nhóm người dùng thấp hơn.
Tada
======Copy url=====
+Các tag meta cần thiết:
<link rel="image_src" href="[IMAGE_LINK]" />
<meta property="og:title" content="[TITLE_SHARE]" />
<meta property="og:type" content="[article/website]" />
<meta property="og:image" content="[IMAGE_LINK]" />
<meta property="og:description" content="[DESCRIPTION]" />
<meta property="og:url" content="[PAGE_LINK]" />
<meta property="article:author" content="https://www.facebook.com/[FAN_PAGE]" />
<meta name="author" itemprop="author" content="[DOMAIN_SITE]" />
<meta property="fb:app_id" content="xxxxxxxxxxxxxxxxxx" />
Ví dụ:
TÊN.DOMAIN | BỞI TÊN.FAN.PAGE
{tên fanpage này có link về trang fan page}
BỞI TÊN.FAN.PAGE
{tên fanpage này có link về trang fan page}
{Giống như các bài share của trang 24h trên fb}
+Một số property khác:
- og:site_name
- og:locale : Ngôn ngữ, mặc định là en_US
- og:video : url của video, cần bổ sung og:image
- og:video:type
https://developers.facebook.com/docs/sharing/opengraph/object-properties
+Link tạo App facebook:
https://developers.facebook.com/apps
+Kích hoạt app
Trong phần Setting, nhập đầy đủ App Domains, Contact Email
Trong phần Status & Review, kích hoạt App
+Link debug url cần share:
https://developers.facebook.com/tools/debug/
Sau khi debug, url sẽ được facebook sẽ lưu vào cache.
Sau đó, có thể copy url lên facebook để share.
+Code js auto refresh cache của fb:
<script>
$.post('https://graph.facebook.com', {id: document.URL, scrape: true}, function (response) {});
</script>
Đặt code này vào trang cần refresh cache.
Như vậy sau khi mở bài viết lên, đem link đi share sẽ hiện đầy đủ thông tin trong thẻ meta.
Link repository: https://github.com/huynhtrucquyen0812/elfinder-for-sifoni
php.ini
đã bỏ comment dòng extension=php_fileinfo.dll
require
trong composer.json
đoạn code:"studio-42/elfinder": "2.x-dev",
"barryvdh/elfinder-flysystem-driver": "~0.1”
rồi chạy lệnh composer update -vvv
routing
trong phần /admin
'/finder/browse' => 'Admin\FinderController:browse:admin_finder_browse',
'/finder/connector' => 'Admin\FinderController:connector:admin_finder_connector’,
FinderController.php
vào app/controller/Admin/
finder.html.twig
vào app/view/admin/utils/
elfinder
vào web/assets/
upload
và chmod 777 trong web/
<textarea name="content" id="content" rows="10" cols="60"></textarea>
<script>
CKEDITOR.replace('content', {
filebrowserBrowseUrl : "{{ url('admin_finder_browse') }}"
});
</script>
config.js
trong ckeditor/
CKEDITOR.editorConfig = function( config ) {
// Config Sth
config.filebrowserBrowseUrl = '/admin/finder/browse';
};
<img id="img" src="/assets/admin/image/no-image.png"/> <!-- để hiển thị image được chọn -->
<input id="imgURL" type="text" name="image"/> <!-- để lưu URL của image -->
<button class="btn-browse-img" type="button">Choose</button> <!-- để browse image -->
<script>
$(document).ready(function() {
window.admin_finder_browse = "{{ url('admin_finder_browse') }}";
});
$(document).ready(function() {
$('.btn-browse-img').on('click', function() {
var txt = $(this);
var fileupload = function(file) {
$('#img').attr('src', file.url);
$('#imgURL').attr('value', file.url)
};
window.upload_handle = fileupload;
var w = window.open(window.admin_finder_browse, "", "width=600, height=400");
w.onbeforeunload = function(){
window.upload_handle = false;
};
});
});
</script>
--> Về AuthedController
trong FinderController
: #3
DONE~!!! (╯°□°)╯︵ ┻━┻
Bạn đang thiết kế 1 website hay 1 software có các chuyên mục đa cấp (có chuyên mục cha, con, cháu, chắt, chút, chít, v.v..). Làm sao để thiết kế CSDL để đạt sự tối ưu trong lưu trữ, truy vấn, chỉnh sửa đây ? Bài viết sẽ đưa ra 1 giải pháp hoàn toàn mới, và bạn sẽ thấy được tầm quan trọng của Cấu Trúc Dữ Liệu.
parent_id
để lưu giá trị id của node cha ( parent_id = 0 khi node đó thuộc root - node 'ông tổ' ). Khi đó để lấy dữ liệu ra, phải dùng đệ quy để giải quyết => performance thấp.Giải pháp mới này sử dụng cấu trúc cây dữ liệu như sau :
Cây dữ liệu sẽ có những thuộc tính như sau :
value
sẽ 2 giá trị là left
và right
( right
> left
). Ví dụ node A ( có left là 1, right là 8).left con
> left cha
AND right con
< right cha
. Ví dụ C là con của A ( 2 > 1 AND 5 < 8 ) và F là con của B ( 10 > 9 AND 11 < 12 ).left
= 0 và đường đi tăng dần sẽ theo "từ trên xuống dưới, từ trái qua phải". Xem hình minh họa bên dưới để hiểu rõ hơn đường đi.left
và right
của tất cả các node tạo thành 1 dãy số tự nhiên liên tục từ 0 đến (N*2 + 1) với N là tổng số node của cây ( không tính node root).Khi sử dụng ckfinder để upload hình lên website chúng ta cần chú ý tới việc bảo vệ hệ thống tài nguyên của web.
VD khi bạn truy cập địa chỉ sau : http://thanhngutienganh.com/scripts/ckfinder/ckfinder.html bạn có thể dễ dàng nhận ra rằng mình chẳng cần phải đăng nhập hay trải qua bất kì khó khăn nào để có thể xóa và up hình lên website đó -> điều này thật sự nguy hiểm
Trong ckfinder có chứa file config.php
trong đó có hàm CheckAuthentication
cho phép bạn thực hiện việc kiểm tra vấn đề truy cập.Tuy nhiên việc sử dụng session trong file config.php
không phải là dễ dàng vì thế hôm nay mình sẽ nói về cách dễ dàng hơn được NAM htacess
mang tới,đó là dùng htacess để chặn truy cập.
File .htacess
của bạn có thể viết lại như sau
<IfModule mod_rewrite.c>
RewriteEngine on
Options -Indexes
RewriteRule scripts/ckfinder/ckfinder.html - [F,NC]
RewriteRule ^$ web/ [L]
RewriteRule (.*) web/$1 [L]
</IfModule>
Cùng tận hưởng kết quả nào !!!!
Chúc các bạn thành công ~!
Vào trang Console Google API để tạo API Key.
Tạo một project mới.
Enable API cần dùng. Google Maps JavaScript API
Trong tab Credentials
tạo mới một API Key.
Thư viện Google Javascript API
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=[YOUR_KEY]&signed_in=true&callback=initMap" async defer></script>
Tạo mới map.
<div id="map_canvas"></div>
function initMap(){
var map = new google.maps.Map(document.getElementById('map_canvas'), {
center: {
lat: 10.825,
lng: 106.685
},
zoom: 15
});
}
Hàm xử lý.
function geocoderAddress(geocoder, resultsMap, address) {
geocoder.geocode({
address: address
}, function (results, status) {
if (status === google.maps.GeocoderStatus.OK) {
resultsMap.setCenter(results[0].geometry.location);
if (marker) {
marker.setMap(null);
}
marker = addMarker(resultsMap, results[0].geometry.location);
}
});
}
function addMarker(map, position) {
var mk = new google.maps.Marker({
map: map,
position: position
});
return mk;
}
Tạo Marker.
var geocoder = new google.maps.Geocoder();
var address = "07 Nam Quoc Cang, Phuong Pham Ngu Lao, Quan 1, TP Ho Chi Minh";
geocoderAddress(geocoder, map, address);
Để giảm tải phần lớn khối lượng CPU & RAM cho server khi chạy production
thì ta có thể cache những file mà Twig Template Engine compile từ .twig
ra .php
Vào file app/config/hook.php
thêm vào dòng sau :
// Twig Cache on production
if ($app['env'] == \Sifoni\Engine::ENV_PROD) {
$app['twig.options'] = [
'cache' => \Sifoni\Engine::getInstance()->getDirPath('cache')
];
}
và theo đó ta chỉ cần chỉnh trong bootstrap.php
sang môi trường PROD sau khi chỉnh sửa và test xong web (nhớ tắt hoặc comment dòng này khi debug hoặc chỉnh sửa web để tránh cache)
$engine = \Sifoni\Engine::getInstance()->init()->bootstrap(array(
'env' => \Sifoni\Engine::ENV_PROD, // Tắt mở bằng cách comment hoặc bỏ comment dòng này
'path.root' => ROOT_PATH,
'autoloader' => $autoloader,
));
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.