ขออนุญาตแยกห้องให้เป็นระเบียบครับ
ลิงก์ลัด
ธีมนี้เกิดจากปัญหาลิขสิทธิ์ของธีมใน Drupal Themes ว่าส่วนใหญ่จะมีลิขสิทธิ์ในทำนองว่า เราต้องบอกว่าธีมนี้มาจากไหน และต้องโยงเครดิตไปหาผู้พัฒนา ซึ่งบางทีเราแก้ธีมเสียจนแทบไม่มีเค้าเดิม อีกประการหนึ่งคือ ธีมที่เป็นที่นิยม มักมีการดัดแปลงมาจาก cms อันอื่น (เช่น Wordpress) จึงทำให้ชื่อสไตล์ไม่คล้องกับธีมของ drupal เอง ทำให้งง
จึงเป็นเหตุให้พัฒนาธีมแบบง่าย ๆ สำหรับเป็นฐานที่จะพัฒนาต่อยอดออกไป โดยที่ไม่ต้องกังวลกับเรื่องลิขสิทธิ์เดิมอีกต่อไป เพราะเอาต้นแบบมาจาก bulemarine แต่ตัดส่วนของ <table> ทิ้งไป ใช้แท็ก <div> เข้ามาแทน
เนื่องจากทำใหม่ทั้งหมด จึงทำให้รูปนำหน้าแท็ก (เลียนแบบจากธีม k2) ไม่สวยเหมือนมืออาชีพ แต่ก็พอถูไถน่ะ ใครอยากสวยก็แก้ไขเอาตามใจชอบ
ธีมนี้ยังไม่ใช่ธีมสำเร็จรูปที่ใช้ได้เลย เพราะยังขาดเรื่องแก้สีพื่นหลังล้น แต่จุดประสงค์ของการพัฒนาคือ จะเอาไปทำเป็นธีมต้นแบบ จึงหยุดเรื่องสีพื้นหลังไว้แค่นี้
update: 2006-10-31 ปรับปรุงให้ใช้งานได้จริง
ขมายมาจาก bluemarine ( ธีมตั้งต้นของ drupal ) แต่งนิดหน่อย ลบสีเทาด้านซ้ายขวาออก เพิ่มสีน้ำเงินและฟ้าตรึม
ที่เหลือแทบจะไม่ได้แตะต้องอะไร คงใช้งานได้เหมือน bluemarine น่ะ
Download blueboxie (2006-07-30)
เอามาจาก bluemarine เหมือนกัน แต่ได้ความคิดเรื่องสีและรูปแบบจาก www.stopdesign.com
รโชหรณัง ทุกอย่างย่อมมีข้อบกพร่องทั้งสิ้น เนื่องจากธีม bluemarine ใช้ตารางในการจัดคอลัมน์ ธีมนี้ก็เลยได้รับผลเสียของการใช้ตารางเหมือนกัน คือมันจะล้นหน้า ดังนั้น ควรใช้กับจอที่มีความกว้างกว่า 1024 จุด หรือให้ปรับบล๊อกให้ลอยเพียงแค่แถบซ้ายหรือขวา แถบใดแถบหนึ่งเพียงแถบเดียว ก็จะลดปัญหาความกว้างตารางล้นจอได้ครับ :)
Download bluebox2 (2006-08-03)
screenshot
เมื่อได้ลองใช้ tableblue กับ IE แล้ว พบว่าแสดงผลเพี้ยนมาก เลยต้องกลับมาหาตารางเหมือนเดิม เลยเอา bluebox2 มาล้างใหม่ แก้ปัญหาตารางล้นจอ แล้วอีกแต่งนิดหน่อย
Download bluebox3 (2006-08-06)
สำหรับธีมนี้ ลองแล้ว แสดงผลรวดเร็วดี เลยแต่งสีน้ำตาลพ่วงมาด้วย เผื่อได้ไช้เอง
ปรับได้ทั้ง 2 และ 3 คอลัมน์ เหมือนเดิม
ทำเสร็จวันแม่พอดี ดูร้อนไปหน่อย เพราะตั้งใจจะให้เอารูปมาใส่ตรงส่วนหัว
เหลือข้อผิดพลาดกับ IE นิดหน่อย แต่ก็พอดูได้ (เว็บดัง ๆ ยังมีเลย นับประสาอะไรกับเราเนอะ :P )
Download palapanyo2 (2006-08-12)
ตกแต่งให้ดูร้อนน้อยลง
แก้ข้อผิดพลาดกับ IE เรื่องสีล้น ด้วยการบังคับขอบคงที่
Download palapanyo3 (2006-08-19)
พยายามประณีประนอมหลาย ๆ อย่างเข้าด้วยกัน
เนื้องจากต้องการความเข้ากันได้กับ IE รุ่นเก่า จึงใช้แท็ก <table> แทน
เติมความสามารถในการแสดงภาพแบบสุ่ม บวกการเปลี่ยนการแสดงผลแบบจางหาย ดังนั้น ธีมนี้จึงต้องเอาไปแก้ไขโค๊ดภายในเอาเองอีกมาก ไม่ใช่ธีมที่แตกไฟล์แล้วใช้ได้เลย
ภายในมีโค๊ดจาวาสคริปต์ (AJAX) ที่ไม่เกี่ยวกับธีมอยู่ด้วย แต่ขอเก็บรวมไว้ด้วยกัน เพราะขี้เกียจแยกน่ะ
Download palapanyo4 (2006-08-30)
เอาต้นแบบจากธีม basett
พยายามทำให้ดูเรียบ โล่ง หรูเล็กน้อย
เป็นธีมแบบ fluid width ทั้งสามคอลัมน์ และบังคับว่าต้องมีคอลัมน์ขวา เพราะดัน secondary links และ search box รวมทั้ง mission ลงมาที่ sidebar ทางขวา
(เนื่องจากธีมที่จะใช้งานจริง จะนำรูปไปใส่ตรงนั้น จึงต้องทำส่วน header ให้โล่ง ๆ)
และก็เหมือนเดิม คือ IE แสดงไม่ค่อยดีนัก
Download palapanyo5 (2006-09-27)
ธีมนี้เกิดจากการศึกษาการทำ tab แบบขอบมน เอาฐานจากธีม basett ลอกหน้าตาจากหลายแห่งมาประชุมกัน เพื่อจะทำให้ใช้กับ drupal-4.7
ธีมที่ทำหลัง ๆ มานี่ พยายามทำให้สามารถปรับได้ทั้ง 2 และ 3 คอลัมน์
ค่อนข้างตั้งใจทำมาก เพราะจะเอาไปใช้จริง แต่ก็ต้องยังมีข้อผิดพลาดอยู่อย่างแน่นอน
Download palapanyo6 (2006-12-14)
ข้อคิดที่ได้จากการทำธีมนี้
เหล้าเก่าในขวดใหม่ ขยายมาจาก tashmina อีกที ปรับนิดเดียว
Download storyteller (2006-08-01)
แถมอีกหน่อยนึง ทำเป็นบล๊อกพื้นใส แต่ว่า... IE ดูไม่จืด :P
Download storyglass (2006-08-01)
เอารูปแบบมาจาก bluebox2 แต่พยายามจะแก้ปัญหาตารางล้น ด้วยการใช้แท็ก <div> แทนตารางแบบเดิม แล้วแก้ปัญหาสีตารางด้วยการใช้ภาพพื้นหลังเป็นไฟล์ gif
แสดงได้ 3 คอลัมน์ ถ้าแสดงเพียง 2 คอลัมน์ จะชดเชยสายตาด้วยคอลัมน์ว่าง
การแสดงผลใน konqueror ช้า เนื่องมาจากการแสดงภาพพื้นหลังเป็นเปอร์เซนต์ การแสดงผลใน IE เพี้ยนมาก
Download tableblue (2006-08-06)
screenshot
แก้ปัญหาแการแสดงผลช้า ด้วยการกำหนดคอลัมน์ซ้ายขวาให้ความกว้างคงที่
การแสดงผลใน konqueror เร็วกว่าเดิมมาก ๆ (แต่ยังช้ากว่าแบบใช้ตาราง) การแสดงผลใน IE เรื่อง overflow:auto เพี้ยน ดันขอบขวาตกจอ
นอกจากสีฟ้าแล้วยังทำสีน้ำตาล
และสีเขียวด้วย
ใช้หลักการเดิมของ table2 มาปรับแต่ง เลือกใช้สีจากสีตั้งต้นคือ #112266 เอามาปรับเปอร์เซนต์ความใสใน gimp แล้วเอาสีมาตัดกัน เลยทำให้ไม่อยากใส่รูปในส่วนหัว เพราะดูแล้วไม่ค่อยเข้า
เนื่องจากหลีกเลี่ยงคำสั่ง css คือ position: relative; การแสดงผลใน konqueror และ IE จึงเร็วดี การแสดงผลใน IE เพี้ยนเล็กน้อย คอลัมน์ขวาสุด เลื่อนมาทางซ้ายเล็กน้อย
เลือกได้ทั้ง 2 และ 3 คอลัมน์ (ผมชอบแบบเอาคอลัมน์ขวาไว้ สีมันเข้มสวยดี)
ลองทำจากรูปผ้าพาชมินา ปรับมาจากธีม slashin แต่แปลงเยอะมาก
Download tashmina (2006-07-31)
ดัดมาจาก basett ปรับเป็น 2 คอลัมน์แบบความกว้างคงที่
เอาสไตล์ชีตจากธีม tt2-inv
เอาหน้าตามาจาก เว็บ Activate ของคุณ Steve Leggat
เป็นธีมแบบ fixed width สองคอลัมน์ (sidebar-left มาอยู่บน sidebar-right)
ทีแรกจะแต่งให้สวย แต่บังเอิญขี้เกียจเสียก่อน เลยออกมาเหลี่ยม ๆ
Download tt2-act (2006-10-24)
ลอกรูปแบบมาจาก OpenWebDesign: Invention Template
เขียนขึ้นมาจากธีม tt2 โดยพยายามแต่งรูปให้น้อยที่สุด
การศึกษาครั้งนี้ได้รู้ว่า
เอาสไตล์ชีตจากธีม tt2-inv
เอาหน้าตามาจาก openwebdesign: Zenlike1.0 by NodeThirtyThree
เป็นธีมแบบ fixed width สองคอลัมน์ (sidebar-left มาอยู่บน sidebar-right)
พยายามทำให้เป็นธีมสำเร็จรูป ไม่ต้องแก้แล้วก็สามารถใช้งานได้เลย
ออกแนวลิเกนิดหน่อย เลยแก้ด้วยรูปที่ดูจริงจังหน่อย ;D
Download tt2-zl (2006-10-09)
เดี๋ยวจะทยอยเอาอันอื่นมารวม :)
ช่วงนี้วิญญาณออกแบบเข้าสิง รีบ ๆ ทำไว้หลาย ๆ ธีม ต้องได้ไช้จริงซักธีมนึงละน่า
เอาโครงจาก tt16-cssmania โดยต้องการทำธีม 3 สดมภ์ ที่ดูคล้าย 2 สดมภ์ เรียบง่าย
เอาความคิดทางหน้าตาจาก Bracewell & Giuliani แต่ทำไปแล้วมันไม่สวย ถ้าจะให้สวยก็ต้องเหมือนต้นแบบ เกรงใจเจ้าของเว็ป เลยทดลองเปลี่ยนไปมาเลยกลายเป็นแบบที่เห็น
*** ยังทำไม่เสร็จ *** ขาดตรงเมื่อยุบเหลือ 2 สดมภ์และ 1 สดมภ์แล้ว พื้นหลังยังเหมือนเดิม และ IE แสดงผลเพี้ยน ๆ หน่อย ถ้ามีเวลาคงได้กลับมาปรับปรุงใหม่ให้สมบูรณ์
สดมภ์ทั้ง 3 จะเปลี่ยนความกว้างตามความกว้างของขนาดหน้าต่าง ใช้เทคนิกพื้นหลังเป็นไฟล์ GIF แสดงผลเป็นเปอร์เซนต์ตามแนวนนอน โดยเจาะโปร่งเป็นส่วน ๆ จึงทำให้สามารถแสดงผลแบบ liquid ได้
เอาโครงจาก tt16-cssmania โดยต้องการทำธีม 3 สดมภ์ สีฟ้า
ทำ ๆ ไปแล้วหมดแรง เอาแค่นี้พอแล้ว (ธีมนี้ไม่ค่อยดีเท่าไหร่)
สดมภ์ทั้ง 3 จะเปลี่ยนความกว้างตามความกว้างของขนาดหน้าต่าง ใช้เทคนิกเงาของบล๊อกแบบใช้ภาพพื้นหลังเป็น gif
เอาธีมเก่าจาก tt15-palapanyo6 มาปรับปรุง
เป็น 3 สดมภ์ที่ปรับความกว้างของสดมภ์ซ้ายขวาตามความกว้างของบราวเซอร์ ปรับปรุงให้ดูมีมิติขึ้น ทนทานขึ้น ตั้งใจทำ logo ให้คาบระหว่างสองส่วนใน header เพื่อให้ดูมีมิติ ซึ่งอาจทำ site-name เป็นไฟล์ภาพ ซึ่งจะทำให้ดูดีขึ้นอีก
อยากได้ธีมที่ค่อนข้างโล่งแบบนี้มานานแล้ว เพิ่งมีรูปที่กลับจากไปเที่ยวมา เลยทำไว้ก่อนทิ้งไปทำงานอื่น ทำลวก ๆ เช่นเดิม จึงไม่ค่อยถูกกับ IE เท่าไหร่ บล๊อกกลางหล่นง่ายไปหน่อย
เป็น 3 สดมภ์ แบบความกว้างของบล๊อกซ้ายขวาคงที่ ที่ 210px กว้างรวมน้อยสุดที่ 800px และมากสุดที่ 1080px
เอาโครงจาก tt26-dhamportal
เอาแบบจาก Blaugallery
เป็น 3 สดมภ์ที่ตรึงความกว้างของสดมภ์ซ้ายขวาให้คงที่ ปรับให้เนื้อความดูแน่น และคลายความอืดอัดด้วยการลดความแตกต่างของสีลง
เอาโครงจาก tt26-dhamportal
ตั้งต้นจาก Green Renaissance แต่ทำไปทำมาดันไปลอกธีมของเว็บ Drupal
ทำไม่รอด ดังนั้นจึงยังไม่เสร็จ ใช้กับ IE ไม่ผ่าน
เป็น 3 สดมภ์ที่ปรับทุกสดมภ์พร้อมกัน ตามความกว้างของบราวเซอร์
เอาธีมเก่าจาก tt25-saupadisesa มาปรับปรุงให้กลมกลืน (รวมทั้งถือโอกาสปรับปรุงธีมเก่าของ Drupal-5 ด้วย)
เป็น 3 สดมภ์ที่ปรับความกว้างของสดมภ์ซ้ายขวาตามความกว้างของบราวเซอร์ โดยแบ่ง sidebar-left sidebar-right และ main ให้อยู่ในระนาบเดียวกัน ทำให้แสดงผลได้เร็ว (ใช้โครงของ tt2) ปรับปรุงใหม่ให้กลมกลืนและมีมิติขึ้นเล็กน้อย
เอา ธีม tt26-Freebird มาปรับปรุง
เป็น 3 สดมภ์ แบบความกว้างของบล๊อกซ้ายขวาคงที่ ที่ 210px กว้างรวมน้อยสุดที่ 800px และมากสุดที่ 1080px สดมภ์ขวาบังคับแสดงแค่หน้าแรก ส่วนหน้าอื่นจะถูกดันลงมาแสดงที่ด้านล่างแทน
เอาโครงจาก tt36-carrot
เที่ยวนี้คิดหน้าตาเอง ทำให้พื้นหลังดูโปร่งตาเล็กน้อย โดยหนีปัญหา IE ไม่รองรับไฟล์กราฟฟิก png ด้วยการทำพื้นหลังเป็นหลายชั้น แล้วนำมาซ้อนทับกัน
ใจจริงอยากทำให้พิสดารมีตัวแบ่งพื้นที่เยอะ ๆ แต่ทำไปทำมาหมดแรงเสียก่อน ได้แค่นี้เลยเอาแค่นี้ครับ
บล๊อกซ้ายอยู่ปกติ และบล๊อกขวาจะอยู่เลื่อนลงมาข้างใต้
เอาโครงจาก tt36-carrot
เอาความคิดทางหน้าตาจาก Spektacle ทำให้หัวดูโปร่งตาเล็กน้อย เอาพื้นหลังจากกระเบื้องพื้นวัดที่มัณฑะเลย์ ภาพหัวจากตลาดผ้าจันปาธในนิวเดลลี (ธีมเดียว 3 ประเทศ)
ทำค่อนข้างหยาบ เลยทำให้ IE แสดงผลเพี้ยน ๆ หน่อย
บล๊อกซ้ายอยู่ปกติ และบล๊อกขวาจะอยู่เลื่อนลงมาข้างใต้ โดยแต่ละบล๊อกจะแสดงได้ 10 สี จาก 0-9 โดยเอาตัวเลขมาจากตัวแปร $block->delta
โดยแปลงที่ไฟล์ block.tpl.php
ต้องการให้เรียบ โล่ง นุ่ม สงบ และไม่ให้จืดตา แต่เนื่องจากเรื้อเวทีนานไปหน่อย กอรปกับทำลวกไปหน่อย จึงดูหยาบเล็กน้อย
เป็น 2+1 สดมภ์ บล๊อกซ้ายอยู่ซ้าย บล๊อกขวาจะอยู่เลื่อนลงมาข้างใต้ กว้างน้อยสุดที่ 800px และมากสุดที่ 980px
เอาโครงจาก tt36-carrot
เอาความคิดทางหน้าตาจาก Artworking โดยตั้งใจจะทำให้มีความเปรียบต่างสูง เพื่อถนอมสายตา พื้นหลังเป็นเทาดำเพื่อการประหยัดพลังงาน
อีตอนกำลังสร้างส่วนหัว (header) ลูกสาวถามว่ากำลังทำเว็บมารเหรอ เลยเอามาตั้งชื่อธีมเสียเลย ว่าธีมมาร (Mara) แต่เติม Black ด้วย เพราะว่าอาจเปลี่ยนสีพื้นไปได้หลายสีครับ
บล๊อกซ้ายอยู่ปกติ และบล๊อกขวาจะอยู่เลื่อนลงมาข้างใต้
เอาโครงจาก tt36-Palapanyo8
ต้องการทำเป็นธีมเลียนธรรมชาติ กึ่งเทคโนโลยีหน่อย ๆ แต่ทำออกมาแล้วดูการ์ตูนไปนิดนึง
เป็น 2+1 สดมภ์ บล๊อกซ้ายย้ายมาอยู่ขวา เนื้อหาหลักจะเลื่อนไหลไปเต็มพื้นที่ และบล๊อกขวาจะอยู่เลื่อนลงมาข้างใต้ ตรง mission
เว้นเผื่อใส่ภาพเคลื่อนไหว
สำหรับไซต์ www.somdechsuk.org
สำหรับไซต์ www.somdechsuk.org
เอาโครงจาก tt36-carrot
ลอกสีมาจากเว็บ Borneo Tropical Rainforce Foundation
บล๊อกซ้ายอยู่ปกติ และบล๊อกขวาจะอยู่เลื่อนลงมาข้างใต้
ทำไว้สำหรับ drupal-6 โดยเอาโครงมาจาก tt35-carrot ปรับแต่งเรื่องภาพเล็กน้อยจาก web: แสดงรูปแบบสุ่ม - revised
ทีแรกกะจะใช้ flash แต่ทำไม่เป็น ถ้าทำได้ก็ขนาดไฟล์ใหญ่มาก เลยใช้วิธีเดิมดีกว่า render ภาพได้นิ่มนวลด้วย
บล๊อกซ้ายจะถูกเลื่อนไปทางขวาบน และบล๊อกขวาจะอยู่เลื่อนลงมาข้างใต้
เอาโครงจาก tt36-carrot
เอาแรงบันดาลใจเรื่องหน้าตาจากเว็บพระป่า forestsangha.org
อยากให้ดูสงบ สะอาด
บล๊อกซ้ายอยู่ปกติ และบล๊อกขวาจะอยู่เลื่อนลงมาข้างใต้
เอาโครงจาก tt36-carrot
เอาความคิดทางหน้าตาจาก เว็บหลวงพ่อชุมพล พลปญฺโญ
ตั้งใจจะทำเป็น 2 สดมภ์ ที่สามารถปูเนื้อหาให้เต็มพื้นที่ ซึ่งจะมีข้อดีคือไม่เหลือพื้นที่โล่งเกินไปและมีกรอบภาพในการนำสายตา แต่การที่เนื้อหาสามารถเลื่อนไหลไปเต็มพื้นที่ก็มีข้อเสียคือเนื้อหาไม่อยู่ตรงที่เดิม และต้องปรับแต่ง default css เยอะมาก ทำให้อาจไม่เสถียรเท่าที่ควร และการโพสต์เนื้อหาต้องระมัดระวังกว่าปกติ
เป็น 2+1 สดมภ์ บล๊อกซ้ายย้ายมาอยู่ขวา เนื้อหาหลักจะเลื่อนไหลไปเต็มพื้นที่ และบล๊อกขวาจะอยู่เลื่อนลงมาข้างใต้
เนื่องจากปัญหาข้อจำกัดในการแสดงเนื้อหา จึงปรับปรุงมาเป็นแบบมาตรฐานแทนครับ
เอา ธีม Bartik มาปรับปรุง
เอาธีม basett มาทำใหม่ สำหรับให้ใช้กับ Drupal-5.0
update
แก้ไขให้บล๊อกหล่นยากขึ้น ทำให้เริ่มซับซ้อนขึ้น
ผมได้มีโอกาสเข้าไปที่เว็บ debianclub.com รู้สึกขอบธีม New Portal ซึ่งมีข้อดีคือโครงสร้างเรียบง่าย แสดงผลได้เร็ว และเป็นธีมที่มีลักษณะ self align คือจะปรับตัวเพื่อเว้นช่องสำหรับบล๊อกทางซ้าย หรือทางขวา แบบอัตโนมัติโดยไม่ต้องเขียนสคริปต์ช่วย
จึงลองเอาธีม bluemarine จาก drupal-5 beta 2 มาทดลองสร้างใหม่ขึ้นเลียนแบบ ก็ใช้ได้ผลดี
แต่เสียดายที่ใช้กับ Konqueror ไม่ได้ เพราะเกิดข้อผิดพลาดคือ
เมื่อเปิดเพจครั้งแรก การแสดงผลจะถูกต้อง (ยกเว้นกรณี content ล้นขวา - ถึงแม้จะดักด้วย width: 95% ไว้แล้ว ก็ไม่ได้ผล)
แต่เมื่อเคลื่อนเมาส์ไปมา content จะเลื่อนมาทางซ้าย ไปหลบอยู่หลังบล๊อกด้านซ้ายทันที
ยังไม่ทราบว่าเป็นข้อผิดพลาดที่ Konqueror หรือเปล่า
จึงตัดสินใจ freeze code ไว้ก่อน เพื่อจะดูผลของ CSS ในแบบต่าง ๆ
Download base2tt5 (2006-12-01)
ได้ความคิดหน้าตาจาก CSSmania โดยมาทำแบบไม่ต้องใช้ไฟล์ภาพ แต่ใช้ css ล้วน ๆ ในการปรับหน้าตาให้ดูมีมิติ
ร้างเวทีมานานแล้ว กลับมาทำใหม่มันงง ๆ ก็เลยทำลวก ๆ เหมือนเดิมครับ
Download tt15-cssmania (2007-07-18)
คล้าย ๆ ธีม tt35-cw
หมดแรงบันดาลใจแล้ว สมองเลยเริ่มตัน แหะ ๆ
ปรับส่วนหัวให้ขยายและหดได้ จัด sidebar ซ้ายมาขวา ขวาไปล่าง ทำให้เข้าที่เข้าทางมากขึ้นหน่อย
ทำลวก ๆ มาก ถ้าเอาไปใช้จริง คงต้องปรับแก้เอาเองครับ
Download tt15-greenorange (2007-01-02)
กลับจากเที่ยวเชียงใหม่ เลยเอาธีม tt15-palapanyo6 มาทำใหม่ ตกแต่งเพ่ิมเติมนิดหน่อยยามหัวกำลังตัน :P
ldv: Long Distance Voyager สะกดถูกหรือเปล่าก็ไม่รู้ ;D
Download tt15-ldv (2007-01-18)
เอาหน้าตามาจาก University of Notre Dame - Department of History ขโมยมาตรงหัวนิดเดียว ที่เหลือขี้เกียจแล้ว เพราะงานเยอะเกินไป
Download tt15-ndhistory (2007-01-20)
ธีมนี้เกิดจากการศึกษาการทำ tab แบบขอบมน เอาฐานจากธีม basett ลอกหน้าตาจากหลายแห่งมาประชุมกัน เพื่อจะทำให้ใช้กับ drupal-4.7
ธีมที่ทำหลัง ๆ มานี่ พยายามทำให้สามารถปรับได้ทั้ง 2 และ 3 คอลัมน์
ค่อนข้างตั้งใจทำมาก เพราะจะเอาไปใช้จริง แต่ก็ต้องยังมีข้อผิดพลาดอยู่อย่างแน่นอน
Download tt15-palapanyo6 (2006-12-14)
ข้อคิดที่ได้จากการทำธีมนี้
เอาธีมเก่าจาก tt15-palapanyo6 มาปรับปรุง
เป็น 3 สดมภ์ที่ปรับความกว้างของสดมภ์ซ้ายขวาตามความกว้างของบราวเซอร์ ปรับปรุงให้ดูมีมิติขึ้น ทนทานขึ้น ตั้งใจทำ logo ให้คาบระหว่างสองส่วนใน header เพื่อให้ดูมีมิติ ซึ่งอาจทำ site-name เป็นไฟล์ภาพ ซึ่งจะทำให้ดูดีขึ้นอีก
Download tt15-Palapanyo 6.1 (2007-12-25)
เอาธีม base2tt5 มาปรับแต่งให้ใช้กับ Konqueror ได้ แล้วลองปรับเปลี่ยนดู ได้ออกมาเป็นธีมนี้ครับ
ลองใช้ดูสักหลายวันนะครับ
Download tt25-dhamportal (2006-12-05)
ลอกแบบมาจากโฮมเพจของเว็บเฟรมเวิร์ก Django แต่เปลี่ยนเป็นสีแดงแทน
ท่อนบนเหมือนแบบ แต่ท่อนล่างไม่ค่อยสวย เพราะต้นแบบคือธีม base2tt ไม่เอื้อให้ใช้ layout แบบนี้
Download tt25-django (2006-12-09)
ตบแต่งเพิ่มเติมนะครับ ธีมเดิมดูเข้มไปหน่อย
Download tt25-saupadisesa-old (2006-12-06)
ปรับปรุง
ปรับแต่งให้กลมกลืนขึ้น
Download tt25-saupadisesa (2007-12-27)
หมายเหตุ
ชื่อยากไปหน่อย อธิบายเผื่อคุณหมอนะครับ ;D
saupadisesa ย่อมาจาก สอุปาทิเสสะนิพพาน แปลว่าจิตถึงนิพพานแล้ว แต่ยังมีอุปาทิ(ขันธ์ ๕)เหลืออยู่ ทำให้ยังต้องบริหารขันธ์ที่ยังเหลืออยู่
เปรียบเสมือนกับน้ำบนใบบัว น้ำเปรียบเหมือนอุปาทิ ใบบัวเปรียบเหมือนจิต
อุปาทิไม่สามารถแทรกซึมเข้าถึงจิตได้ เหมือนน้ำเพียงแค่กลิ้งไปมาอยู่บนใบบัวฉันนั้น
เอวัง :)
ลอกแบบจาก http://www.cabedge.com โดยเอาธีม tt35-cw มาปรับใช้
เอาภาพมาจากที่ไปเที่ยวพม่า สถานที่คือพระธาตุอินทร์แขวน
สามารถดูภาพได้ที่ www.palapanyo.com/gal/burma
บล๊อกซ้ายจะถูกเลื่อนไปทางขวาบน และบล๊อกขวาจะอยู่เลื่อนลงมาข้างใต้
Download tt35-carrot (2007-10-09)
เกิดจากความอยากลองบล๊อกแบบลอยขัี้นกับลอยไปทางซ้ายได้ สร้างขึ้นแบบหยาบ ๆ เพื่อจะใช้งานกับ Drupal-5 (ถ้าจะเอามาใช้งานจริง คงต้องปรับบล๊อกให้เหมาะสมกับธีมด้วย)
บล๊อกซ้ายจะถูกเลื่อนไปทางขวาบน และบล๊อกขวาจะอยู่ใต้ลงมา และถ้ามีที่เหลือทางซ้าย จะสามารถปรับตัวเองให้เลื่อนไปทางซ้ายได้ด้วย
เอาหน้าตามาจาก Cal Wineries ซึ่งโยงลิงก์ไปจาก cssvault.com อีกทีนึง
ปรับปรุงนิดหน่อย Download tt35-cw2 (2006-12-21)
เอาโครงจาก tt36-carrot
เอาความคิดทางหน้าตาจาก เว็บหลวงพ่อชุมพล พลปญฺโญ
ตั้งใจจะทำเป็น 2 สดมภ์ ที่สามารถปูเนื้อหาให้เต็มพื้นที่ ซึ่งจะมีข้อดีคือไม่เหลือพื้นที่โล่งเกินไปและมีกรอบภาพในการนำสายตา แต่การที่เนื้อหาสามารถเลื่อนไหลไปเต็มพื้นที่ก็มีข้อเสียคือเนื้อหาไม่อยู่ตรงที่เดิม และต้องปรับแต่ง default css เยอะมาก ทำให้อาจไม่เสถียรเท่าที่ควร และการโพสต์เนื้อหาต้องระมัดระวังกว่าปกติ
เป็น 2+1 สดมภ์ บล๊อกซ้ายย้ายมาอยู่ขวา เนื้อหาหลักจะเลื่อนไหลไปเต็มพื้นที่ และบล๊อกขวาจะอยู่เลื่อนลงมาข้างใต้
ลอกแบบจาก http://www.cabedge.com โดยเอาธีม tt35-cw มาปรับใช้
เอาภาพมาจากที่ไปเที่ยวพม่า สถานที่คือพระธาตุอินทร์แขวน
สามารถดูภาพได้ที่ www.palapanyo.com/gal/burma
บล๊อกซ้ายจะถูกเลื่อนไปทางขวาบน และบล๊อกขวาจะอยู่เลื่อนลงมาข้างใต้
ศึกษาและทดลอง Drupal ในห้องนี้ครับ
บันทึกการติดตั้งมอดูล และการแก้ปัญหา
ถ้าเราต้องการให้ผู้อ่านมีสิทธิ์ที่ไม่เท่ากัน โดยให้กำหนดด้วย role
ในการเข้าถึงเอกสารใน node ต่าง ๆ สามารถทำได้โดยใช้มอดูล Taxonomy Access Control Lite ดังนี้
$ cd /var/www/drupal/sites/all/modules
$ wget http://ftp.drupal.org/files/projects/tac_lite-5.x-1.1.tar.gz
$ tar xfz tac_lite-5.x-1.1.tar.gz
admin/build/modules
admin/content/taxonomy
Add vocaburaly
ตั้งชื่อว่า Privacy
terms
ชื่อว่า Public
และ Private
ตามลำดับ โดยอย่าให้เป็นฟรีแท็กส์admin/user/access
Access control by taxonomy
ให้เลือก Vocaburary ว่า Privacy
Role based privileges
และเล็อกกำหนดสิทธิ์ให้แต่ละ role ได้ตามต้องการ เช่น anonymous เป็น Public และ authenticated เป็นทั้ง Public และ Private เป็นต้นเสร็จแล้ว หลังจากนี้ ในขั้นตอนของการสร้างเอกสาร จะมีที่เลือก Privacy
ในหัวข้อ Categories ให้เราเลือกว่าจะให้เป็น Public หรือ Private
อ้างอิง - Content Type access permisions
ถ้าใช้ Drupal-5.5 และ Drupal-5.6 และใช้ฐานข้อมูล Postgresql อาจมีข้อผิดพลาดเกิดขึ้น ต้องปรับแก้ Core ที่ไฟล์ includes/database.pgsql.inc
ดังนี้
$ vi includes/database.pgsql.inc
... function db_distinct_field($table, $field, $query) { if (!preg_match('/FROM\s+\S+\s+AS/si', $query) && !preg_match('/DISTINCT\s+ON\s*\(\s*('. $table .'\s*\.\s*)?'. $field .'\s*\)/si', $query) && preg_match('/(.*FROM\s+)(.*?\s)(\s*(WHERE|GROUP|HAVING|ORDER|LIMIT|FOR).*)/Asi', $query, $m)) { $query = $m[1]; $query .= preg_replace('/([\{\w+\}]+)\s+(' . $table . ')\s/Usi', '(SELECT DISTINCT ON (' . $field . ') * FROM \1) \2 ', $m[2]); $query .= $m[3]; } return $query; } ...
admin/builds/modules
admin/user/permissions
admin/settings/geshifilter
<pre>
[foo]
(ลดข้อจำกัดการแสดงผล HTML)Use <div> container (enables automatic line wrapping)
Languages
Input Format
ต้องเข้าไปเปิดให้ใช้งาน GeSHi ด้วย จาก URL: admin/settings/filters
จะใช้ตัวไหนบ้าง ก็ต้องเข้าไปเปิดในทุก ๆ ตัว...CODE...[/pre] ตัวจริงให้เปลี่ยนจาก < เป็น [
admin/settings/geshifilter
pre
Use <div> container
$ vi sites/all/modules/geshifilter/geshifilter.pages.inc
เพิ่มการ decode สองครั้ง (โดยเพิ่มบรรทัดที่เป็นตัวหนา):129
... $source_code = decode_entities($source_code); $source_code = decode_entities($source_code); ...
... $source_code = decode_entities($source_code); $source_code = decode_entities($source_code); //ADD THIS LINE ...ตัวอย่างข้างต้น ต้องกรอกในฟอร์มว่า
<div class="geshifilter"> <pre language="drupal6"> ... $source_code = decode_entities($source_code); $source_code = decode_entities($source_code); //ADD THIS LINE ... </pre> </div>มอดูลนี้ใหญ่และซับซ้อนมาก แฮ็กไม่ไหว เอาเท่าที่ได้แล้วกันครับ update
admin/settings/filters
GeSHi Filtered HTML
ต้องแฮกเล็กน้อย คือ เอาเนื้อความจาก Add a "Recent posts" block to the tracker module
ไปต่อท้ายไฟล์ modules/tracker/tracker.module
แล้วจะสามารถใช้มอดูล tracker แสดง Recent posts เป็นบล๊อกได้เลย
(แจ๋ว เพราะเขียน Recent Week เอง มีปัญหากับ Drupal-6)
จากคราวก่อนที่ทดลองใส่โค๊ดตัวอย่างลงในเอกสารต้นฉบับเรื่อง python: Adodb Function Reference with example รู้สึกว่าอ่านง่ายดี คล้ายเอกสารของ php ที่เขามีตัวอย่างประกอบตลอด
เลยคิดว่า ถ้าเราสามารถเอาเอกสาร HTML ต้นฉบับ ที่เป็นคู่มือไพธอน เอามาใส่ตัวอย่างเล็ก ๆ น้อย ๆ ด้วยก็น่าจะดี
ลองศึกษา Drupal ดู พบว่ามีโมดูลนึงที่เข้าข่าย คือโมดูล Import HTML
โมดูลนี้ยังไม่สามารถใช้กับ Drupal-5 ได้ และดูท่าว่ายังไม่มีวีแววพัฒนาต่อ
เลยจะทดลองตั้งไซต์ใหม่ เป็น Drupal-4.7 และ import เอกสารเข้ามายังไซต์นี้ และหาทาง Export มายัง Drupal-5 ต่อไป
ทดลองทำดู ยังไม่สำเร็จ แต่ต้องบันทึกเอาไว้ก่อน ไม่งั้นลืมหมด
ผลที่ยังไม่สำเร็จคือ
เริ่มงาน
ไล่ดูตามเอกสารและค้นกูเกิล ได้ผลดังนี้
ติดตั้ง apache2-dev
# aptitude install apache2-dev libapr1-dev libaprutil1-dev libapache2-modxslt php5-xsl php5-tidy
แก้ php.ini ให้รับ xslt (เพราะ php5 ไม่มี xslt)
# vi /etc/php5/apache2/php.ini
... extension=domxml.so extension=xsl.so ...
เริ่ม apache2 ใหม่
# /etc/init.d/apache2 restart
จะข้ามขั้นตอนติดตั้ง Drupal-4.7 ไปถึงตอนติดตั้งโมดูลเลย
เปลี่ยนเป็น webmaster และติดตั้งโมดูล
# su webmaster
$ cd /var/www/drupal/modules
$ wget http://ftp.osuosl.org/pub/drupal/files/projects/import_html-4.7.x-1.x-dev.tar.gz
$ tar xfz import_html-4.7.x-1.x-dev.tar.gz
$ cd import_html
ต้องแก้โค๊ดนิดนึง ตามเอกสาร Drupal - Fatal Error on Line 1225
$ vi import_html.module
:1225
... //dsm($node); drupal_set_message($node); ...
เสร็จแล้ว
ตั้งค่าโมดูลด้วย
administer -> modules -> เปิดให้ import_html ทำงาน
ตั้งค่าการใช้งานด้วย
administer -> access control -> เลือก role ที่จะเปิดให้ใช้
ตรวจสอบและตั้งค่าการอิมพอร์ต
administer -> settings -> import_html
ถ้าไม่ฟ้องข้อผิดพลาด ก็แสดงว่าใช้ได้แล้ว
เรียกใช้งานได้ทางเดียว
administer -> Import HTML Site
บันทึกการแก้ไขด้วยมือ
จากครั้งก่อน ผมได้สร้างหน้าเปล่าขึ้นมา แล้วแปลงให้เป็น book เตรียมไว้
แล้วจึงนำเข้าเอกสาร ด้วยโมดูล import HTML ดังนั้น หน้าเปล่าของเราจะเป็น node/1 และเอกสารที่นำเข้า จะเริ่มต้นตั้งแต่ node/2 เป็นต้นไป จนถึงหน้าสุดท้าย สมมุติว่าเป็น node/100 แล้วกัน
เมื่อทำการอิมพอร์ตเข้ามาแล้ว จะพบจุดผิดพลาดมากมาย ขอบันทึกการแก้ไขไว้นิดนึง เผื่อจะขยายขอบเขตไปถึงการแก้ไขโค๊ดต้นฉบับ
ตอนอิมพอร์ต ผมเลือกที่จะไม่เพิ่มรายการในเมนูแบบอัตโนมัติ
เราจึงต้องยกเลิกหัวข้อ Add each page to menu
ตอนนี้ศึกษาเรื่อง adodb อยู่ เลยใช้ไพธอนในการทำงานไปด้วยเลย
เริ่มต้น
$ python
Python 2.4.4 (#2, Oct 20 2006, 00:23:25) [GCC 4.1.2 20061015 (prerelease) (Debian 4.1.1-16.1)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import adodb >>> driver = 'mysql' >>> host = 'localhost' >>> user = 'user' # MYSQL USER >>> password = 'password' # MYSQL PASSWORD >>> db = 'testdrupal47' # DRUPAL-4.7 DATABASENAME >>> conn = adodb.NewADOConnection(driver) >>> cur = conn.Connect(host,user,password,db)
งานที่ทำคือ
string.strip()
ขั้นตอน
>>> for i in range(2, 101, 1): ... cur = conn.Execute('INSERT INTO book (vid, nid, parent, weight) VALUE (%s, %s, 0, 0)' % (i, i))
>>> cur=conn.Execute('UPDATE node_revisions SET format=1 WHERE nid>1')
>>> cur=conn.Execute('UPDATE node SET type="book" WHERE nid>1')
>>> cur=conn.Execute('UPDATE book SET parent=1 WHERE nid>1')
>>> cur=conn.Execute('UPDATE node SET comment=2 WHERE nid>1')
>>> cur=conn.Execute('UPDATE node SET promote=0 WHERE nid>1')
string.strip()
>>> for i in range(2, 101, 1): ... cur=conn.Execute('SELECT title FROM node WHERE nid=%s' % (i)) ... title=cur.fields[0].strip() ... cur=conn.Execute('UPDATE node SET title="%s" WHERE nid=%s' % (title, i))
ตาราง node_revisions
>>> for i in range(2, 101, 1): ... cur=conn.Execute('SELECT title FROM node_revisions WHERE nid=%s' % (i)) ... title=cur.fields[0].strip() ... cur=conn.Execute('UPDATE node_revisions SET title="%s" WHERE nid=%s' % (title, i))
เรียงลำดับหน้าหนังสือใหม่ ด้วยมือ
อันนี้นานที่สุด แต่ก็คุ้มกว่าการนำเข้าด้วยมือทั้งหมด
มีความจำเป็นที่จะต้องโพสต์เนื้อความยาว ๆ ในเนื้อหาของฟอรั่ม
พบว่า ถ้าข้อความยาวมากเกินไป จะไม่สามารถแสดงผลได้ คือแสดงผลออกมาเหมือนกับไม่มีเนื้อหา
ค้นไปค้นมา พบว่าเป็นกับโมดูล filter
ถ้าเราเลือกใช้ Input format แบบที่ใช้ Line break converter จะทำให้เกิดข้อผิดพลาดดังกล่าว
ทางแก้ชั่วคราวคือ
ให้สร้าง Input format ขึ้นมาใหม่ ไม่ให้ใช้ Line break converter แล้วเลือกใช้ Input format ใหม่นี้
ส่วนตัวเนื้อหาเองนั้น เนื่องจากไม่มีการขึ้นบรรทัดใหม่อัตโนมัติแล้ว เราจึงต้องมาสร้างแท็ก HTML ให้ขึ้นบรรทัดใหม่เอง
อาจทำใน Gedit ก็ได้ โดยใช้การ Find&Replace ธรรมดาครับ
ทางแก้ถาวรคงต้องแจ้ง Bug ไปที่ Drupal ต่อไป
ลองใช้งานกับ postgresql
$ pg_dump -dc -U user database > dumpfile.sql
-d
หรือ --inserts
คือให้ใช้คำสั่ง INSERT
มาตรฐาน ทำให้ช้าแต่แลกเปลี่ยนข้อมูลกับฐานข้อมูลแบบอื่นได้ง่ายกว่า-c
หรือ --clean
คือให้ล้างตารางและ Sequence ก่อนสร้างตารางใหม่ ดีสำหรับเวลาซ่อมฐานข้อมูล$ psql -f dumpfile.sql -U user database
$ psql -c "COPY tablename (column,...) FROM filename DELIMITER E\t database"
$ psql -U user dbname
$ SELECT relname, relpages FROM pg_class ORDER BY relpages DESC limit line; /* tables size */
$ SELECT pg_database_size('dbname'); /* database size */
$ \q
UPDATE table SET field=convert_to(field,'UTF8') WHERE field != cast(convert_to(field,'UTF8') AS field_type);
UPDATE table SET field=replace(field,'\\302\\222','\'') WHERE cast(field as bytea) LIKE '%\\302\\222%';
UPDATE table SET field=replace(field,'\\302\\221','`') WHERE cast(field as bytea) LIKE '%\\302\\221%';
ต้องการใช้ฐานข้อมูล postgresql เพราะงานส่วนใหญ่ลงใน postgresql
ตอนศึกษา Drupal เลือก mysql เพราะหลายมอดูลใช้กับ postgresql ไม่ได้ เลยใช้ mysql มาเรื่อย
ตอนนี้ต้องการเปลี่ยนกลับแล้ว เลยศึกษาวิธีโอนข้อมูลเตรียมไว้
เที่ยวนี้ต้องทำด้วยมือเป็นส่วนใหญ่ เพราะลองค้นจากกูเกิลแล้ว สคริปต์ต่าง ๆ ที่ค้นได้ ส่วนใหญ่จะเขียนด้วย perl และล้าสมัยหมดแล้ว รันแล้วเกิดข้อผิดพลาดทุกอัน
บันทึกการย้ายฐานข้อมูล Drupal รุ่น 5.6 จาก MySQL-5.0.32 มาเป็น PostgreSQL-8.1.11 บนเดเบียน Etch
$ cd /var/www/
$ cp -xa drupal newdrupal
$ cd newdrupal
$ vi sites/default/setting.php
... //$db_url = 'mysql://myuser:mypassword@localhost/drupal-mysql'; $db_url = 'pgsql://pguser:pgpassword@localhost/newdrupal-pgsql'; ...
http://www.example.com/install.php
ติดตั้งพร้อมเพิ่มผู้ใช้คือ admin ให้เรียบร้อย admin/build/modules
เลือกกาทุกมอดูล$ mysqldump -c -t --skip-opt -u myuser -p drupal-mysql > mydrupal.sql
-c
คือ complete insert เพื่อให้เหมาะกับการนำเข้าไปยัง postgresql-t
คือ --no-create-info ไม่ต้องเขียนการสร้างตารางใหม่--skip-opt
คือไม่ต้องเขียนการล๊อกตาราง`
) ทิ้ง$ vi mydrupal.sql
:1,$ s/`//g :wq
node_revision
สดมถ์ log
ให้เป็น NULL
ได้$ psql -f mydrupal.sql -U pguser newdrupal-pgsql
เสร็จแล้ว
แต่หากยังมีข้อผิดพลาดแบบเล็กน้อย เราอาจเริ่มสร้างตารางใหม่ทั้งหมดผ่าน pg_dump ได้ คือ
$ pg_dump -c -U pguser newdrupal-pgsql > newpg.sql
-c
ให้สร้างคำสั่งสร้างตารางด้วย$ psql -f newpg.sql -U pguser newdrupal-pgsql
เสร็จแล้ว
update 51-01-22
ยังไม่เสร็จจริง พอตรวจจริงแล้วเกิดข้อผิดพลาดคือ postgresql ไม่ยอมปรับค่า sequence ให้ตามจริง ไม่รู้ว่าเป็นเพราะอะไร ต้องมานั่งปรับประโยคใน newpg.sql ให้เลข sequence เป็นตัวเลขล่าสุด ต้องทำสักประมาณ 20 ตาราง
ขั้นตอนตั้งแต่เริ่ม (นับตั้งแต่ผ่านขั้นตอนก่อนหน้านี้มาแล้ว) คือ
$ pg_dump -c -U pguser newdrupal-pgsql > newpg.sql
INSERT
ทิ้งไป คือดูตั้งแต่คำสั่ง COPY ...
เป็นต้นไป$ vi newpg.sql
/COPY dG :wq
accesslog
มีฟีลด์ที่เป็น SERIAL คือ aid
โดยมีค่าที่มากที่สุดเป็น 12345 (ดูจาก phppgadmin) ก็ให้ไปปรับแก้ประโยค sql ตรงส่วนปรับ sequence ให้เป็นตัวเลขนี้ด้วย เช่น$ vi pgnew.sql
... SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('accesslog', 'aid'), 12345, true); ...
ทำให้ครบทุกตารางที่มีฟิลด์เป็น SERIAL
$ psql -f newpg.sql -U pguser newdrupal-pgsql
$ psql -f mydrupal.sql -U pguser newdrupal-pgsql
เสร็จจริง ๆ แล้ว
ยุ่งเป็นลิงถุง สงสัยเที่ยวหน้าทำเป็นสคริปต์ดูจากข้อมูลดัมป์ของ mysql แล้วโยนไปสร้างสดที่ postgresql เลยดีกว่า
ปรับปรุงจากครั้งก่อน แต่เพื่อให้สมบูรณ์ในตัวและอ้างอิงภายหลังได้ เลยเขียนใหม่ทั้งหมด
บันทึกการย้ายฐานข้อมูล Drupal รุ่น 5.6 จาก MySQL-5.0.32 มาเป็น PostgreSQL-8.1.11 บนเดเบียน Etch
$ createdb --encoding=UNICODE -U PG_USER NEW_DRUPAL_PG_DB
$ cd /var/www/
$ cp -xa OLD_DRUPAL_DIR NEW_DRUPAL_DIR
$ cd NEW_DRUPAL_DIR
$ vi sites/default/setting.php
... //$db_url = 'mysql://MY_USER:MY_PASSWORD@localhost/OLD_DRUPAL_MY_DB'; $db_url = 'pgsql://PG_USER:PG_PASSWORD@localhost/NEW_DRUPAL_PG_DB'; ...
http://WWW.EXAMPLE.COM/install.php
ติดตั้งพร้อมเพิ่มผู้ใช้คือ admin ให้เรียบร้อย (create new user)admin/build/modules
เลือกกาทุกมอดูล (enable all modules)$ pg_dump -cs -U PG_USER NEW_DRUPAL_PG_DB > NEW_PG.SQL
-c
คือ --clean ล้างตารางเก่า ก่อนสร้างตารางใหม่
-s
คือ --schema-only เอาแต่ส่วนหัวของการสร้างตาราง ไม่เอาข้อมูล เพราะเราต้องการล้างอยู่แล้ว
$ psql -f NEW_PG.SQL -U PG_USER NEW_DRUPAL_PG_DB > temp.log
$ mysqldump -c -t --skip-opt -u MY_USER -p OLD_DRUPAL_MY_DB > OLD_MY.SQL
-c
คือ complete insert เพื่อให้เหมาะกับการนำเข้าไปยัง postgresql-t
คือ --no-create-info ไม่ต้องเขียนการสร้างตารางใหม่--skip-opt
คือไม่ต้องเขียนการล๊อกตาราง`
) ทิ้ง$ vi OLD_MY.SQL
:1,$ s/`//g :wq
node_revisions
สดมถ์ log
ให้เป็น NULL
ได้$ psql -f OLD_MY.SQL -U PG_USER NEW_DRUPAL_PG_DB > temp.log
public/Sequences
เอาทั้งหมดมาสร้างเป็น sql สมมุติตั้งชื่อว่า fixseq.sql$ vi fixseq.sql
SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('access', 'aid'), (SELECT MAX(aid) FROM access), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('accesslog', 'aid'), (SELECT MAX(aid) FROM accesslog), true); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('aggregator_category', 'cid'), (SELECT MAX(cid) FROM aggregator_category), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('aggregator_feed', 'fid'), (SELECT MAX(fid) FROM aggregator_feed), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('aggregator_item', 'iid'), (SELECT MAX(iid) FROM aggregator_item), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('authmap', 'aid'), (SELECT MAX(aid) FROM authmap), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('boxes', 'bid'), (SELECT MAX(bid) FROM boxes), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('client', 'cid'), (SELECT MAX(cid) FROM client), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('comments', 'cid'), (SELECT MAX(cid) FROM comments), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('contact', 'cid'), (SELECT MAX(cid) FROM contact), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('files', 'fid'), (SELECT MAX(fid) FROM files), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('filter_formats', 'format'), (SELECT MAX(format) FROM filter_formats), true); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('locales_source', 'lid'), (SELECT MAX(lid) FROM locales_source), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('menu', 'mid'), (SELECT MAX(mid) FROM menu), true); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('node_comment_statistics', 'nid'), (SELECT MAX(nid) FROM node_comment_statistics), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('node', 'nid'), (SELECT MAX(nid) FROM node), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('node_revisions', 'vid'), (SELECT MAX(vid) FROM node_revisions), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('poll_choices', 'chid'), (SELECT MAX(chid) FROM poll_choices), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('profile_fields', 'fid'), (SELECT MAX(fid) FROM profile_fields), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('"role"', 'rid'), (SELECT MAX(rid) FROM role), true); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('term_data', 'tid'), (SELECT MAX(tid) FROM term_data), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('url_alias', 'pid'), (SELECT MAX(pid) FROM url_alias), false); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('users', 'uid'), (SELECT MAX(uid) FROM users), true); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('vocabulary', 'vid'), (SELECT MAX(vid) FROM vocabulary), true); SELECT pg_catalog.setval(pg_catalog.pg_get_serial_sequence('watchdog', 'wid'), (SELECT MAX(wid) FROM watchdog), true);
สั่งรันด้วย psql ให้มาซ่อมเรื่อง sequence
$ psql -f fixseq.sql -U PG_USER NEW_DRUPAL_PG_DB > temp.log
ขั้นตอนนี้ไม่ควรมีข้อผิดพลาด
http://www.example.com/drupal
และเปลี่ยนชื่อไดเรกทอรี่ด้วยlocales_source
ให้ข้อมูลเป็นอันใหม่www.example.com/OLDDRUPAL
อันใหม่ชื่อ www.example.com/NEWDRUPAL
ต้องเขียน sql ดังนี้$ vi fixlocale.sql
UPDATE locales_source SET location = '/NEWDRUPAL/' || substr(location,12) WHERE location LIKE '/OLDDRUPAL/%'
ตัวเลข 12 ของฟังก์ชั่น substr(location, NUM)
เป็นความยาวของอักขระไดเรกทอรี่เก่า คือ OLDDRUPAL
บวก 3 คือเครื่องหมาย '/' หน้าหลังและบวกกับตำแหน่งถัดไป
สั่งรันได้เลย
$ psql -f fixlocale.sql -U PG_USER NEW_DRUPAL_PG_DB > temp.log
ต้องไม่มีข้อผิดพลาด
เสร็จจริง ๆ แล้ว
จะมีรายงานข้อผิดพลาด ที่หน้า Administer หนึ่งครั้ง
และตอนสร้างเนื้อหาใหม่อีกหนึ่งครั้ง ก็สามารถใช้งานได้แล้ว
บันทึกการย้ายฐานข้อมูล Drupal รุ่น 6.13 จาก MySQL-5.0.51a-24+lenny1 มาเป็น PostgreSQL-8.3.7-0lenny1
เที่ยวนี้ได้สคริปต์ดีจาก Drupal 6.x. site Migration from MySQL to PostgreSQL (howto) จึงสามารถทำตรงกับฐานข้อมูลได้เลย
$ mysqldump -u my_admin -p my_drupal_db > drupal.mysql.sql
$ vi sites/default/settings.php
... #$db_url = 'mysql://my_admin:my_password@localhost/my_drupal_db'; $db_url = 'pgsql://pg_admin:pg_password@localhost/pg_drupal_db'; ...
http://www.example.com/install.php http://www.example.com/?q=user/login http://www.example.com/?q=admin/build/modules
อย่าลืมเปิดมอดูลทุกอัน
$ wget http://blog.lexa.ru/files/drupal-mysql2pgsql.pl.gz $ gunzip drupal-mysql2pgsql.pl.gz $ vi drupal-mysql2pgsql.pl
... #my $srcdb = DBI->connect("dbi:mysql:database=$ARGV[0]") or die; my $srcdb = DBI->connect("dbi:mysql:database=$ARGV[0]","my_admin","my_password") or die; #my $destdb = DBI->connect("dbi:Pg:dbname=$ARGV[1]") or die; my $destdb = DBI->connect("dbi:Pg:dbname=$ARGV[1]","pg_admin","pg_password") or die; ...
$ ./drupal-mysql2pgsql.pl my_drupal_db pg_drupal_db
เสร็จแล้วครับ เข้าไซต์ที่ใช้ฐานข้อมูลใหม่ได้เลย
สมมุติว่าเว็บคือ example.com
ไปที่ไดเรกทอรี่ทำงาน
$ cd /var/www/example.com
สมมุติไดเรกทรอรี่เก่าคือ drupal ฐานข้อมูลก็ชื่อ example_com_drupal
แบ็กอัพไฟล์
$ tar cfz drupal-old.tar.gz drupal
แบ็กอัพฐานข้อมูล
$ mysqldump -u webmaster -p example_com_drupal > example_com_drupal.mysql.bak
ดาวน์โหลด 6.0-beta-1 และแตกไฟล์
$ wget http://ftp.drupal.org/files/projects/drupal-6.0-beta1.tar.gz
$ tar xfz drupal-6.0-beta1.tar.gz
คัดลอกไฟล์ไปยังไดเรกทอรี่เก่า
$ cp -xa drupal-6.0-beta1/* drupal
แก้ไขตามจำเป็น
$ vi drupal/site/default/setting.php
...
ล้างธีมและโมดูลเก่า พร้อมทั้งคัดลอกมาใหม่
ธีม
$ for i in drupal-6.0-beta1/themes/*; do aaa=`basename $i`; rm -rf drupal/themes/$aaa; cp -xa $i drupal/themes; done
โมดูล
$ for i in drupal-6.0-beta1/modules/*; do aaa=`basename $i`; rm -rf drupal/modules/$aaa; cp -xa $i drupal/modules; done
อัปเดตที่บราวเซอร์
http://www.example.com/update.php
ตรวจสอบว่าธีมและโมดูลที่เราติดตั้งเพิ่มเติมในครั้งก่อน ได้ถูกปรับปรุงขึ้นมาเป็นรุ่น 6 ทั้งหมดหรือยัง
ถ้ายังไม่ครบ ก็ดาวน์โหลดรุ่น 6 มาติดตั้งให้เรียบร้อย
เสร็จแล้ว
บันทึกการทำงานเกี่ยวกับการสร้างมอดูล
ค้นภาษาไทยได้ โดยมีการให้น้ำหนักคำค้น และสามารถใส่คำค้นได้หลายคำ
แปลง มอดูล Thai Search ของรุ่น 6 มาเป็น ของรุ่น 5
ความสามารถเหมือนกันคือ ค้นคำไทยได้ ทำบล๊อกได้ ค้นได้หลายคำค้นพร้อมกัน
ติดตั้งปกติเหมือนมอดูลของ Drupal ทั่วไป
ดาวน์โหลดได้ที่นี่ครับ
หมายเหตุ
GROUP BY
กับฟังก์ชั่น pager
ไม่ได้ แต่กับรุ่น 5 นี้ ยังไม่ได้ทดลองครับมีโจทย์เรื่องต้องค้นข้อมูลใน Drupal เป็นภาษาไทยในไซต์ที่เป็นอินทราเน็ตให้ได้
การค้นข้อมูลใน Drupal เขาทำเป็น Full Text Search โดยทำเป็น cron ในการจัดเรียงดัชนีข้อมูล เวลามาค้นก็จะหาได้รวดเร็ว
แต่ปัญหาคือ
สำหรับไซต์ที่เป็นอินเตอร์เน็ต คือข้อมูลออกสู่โลกภายนอก สามารถแก้ได้โดยใช้กูเกิล โดยพิมพ์ต่อท้ายคำค้นว่า site:example.com
หรือสร้าง custom search engine เอง
สำหรับไซต์ที่เป็นอินทราเน็ต คือข้อมูลอยู่แต่ภายใน หรือไม่อนุญาตให้ crawler เข้ามาค้นข้อมูลในไซต์ ทางแก้คือลงมอดูล solr (ท่าทางจะลงยาก เป็นจาวา) หรือมอดูลอื่น (ที่อาจยุ่งพอกัน)
ทางออกคือทำเองแบบง่าย ๆ พอใช้งานได้ดีกว่า โดยพยายามทำคือ
ดูตัวอย่างจาก page_example.module และตัวอย่างจาก Core Module ได้ออกมาดังนี้
ตามขั้นตอนปกติคือ
site/all/modules/
ตั้งชื่อมอดูลว่า thaisearch
admin/build/modules
amdin/user/permissions
เนื่องจากเป็นรุ่นแรก ทำพอใช้งานได้ จึงทุลักทุเลพอควร
thaisearch/keywords
GROUP BY
เลยตัดทิ้งหมดเลย และจำกัดข้อมูลค้นแค่หน้าเดียว 50 รายการหวังว่าคงจะมีเวลาศึกษา SQL และ PHP เพิ่มเติมเพื่อนำมาปรับปรุงในรุ่นหน้า หรือรอท่านผู้ใจบุญปรับปรุงแล้วแจกจ่ายต่อไป
สร้างไฟล์ info
$ cd /var/www/drupal
$ mkdir -p sites/all/modules/thaisearch
$ cd sites/all/modules/thaisearch
$ vi thaisearch.info
; $Id$ name = Thai search description = Search Thai words in node/comment. core = 6.x version = "6.0-rc2"
สร้างไฟล์ module
$ vi thaisearch.module
<?php // $Id: thaisearch.module,v 1.13 2007/10/17 19:38:36 litwol Exp $ // wd's: modify to thaisearch.module /** * @file * This is an example outlining how a module can be used to display a * custom page at a given URL. */ /** * Implementation of hook_help(). * * Throughout Drupal, hook_help() is used to display help text at the top of * pages. Some other parts of Drupal pages get explanatory text from these hooks * as well. We use it here to illustrate how to add help text to the pages your * module defines. */ function thaisearch_help($path, $arg) { switch ($path) { case 'thaisearch': // Here is some help text for a custom page. return t('Search thai words. Type in URL: <code><strong>thaisearch/<em>WORD1 WORD2 ...</em></strong></code> separated by space.'); } } /** * Implementation of hook_perm(). * * Since the access to our new custom pages will be granted based on * special permissions, we need to define what those permissions are here. * This ensures that they are available to enable on the user role * administration pages. */ function thaisearch_perm() { return array('access thaisearch'); } /** * Implementation of hook_menu(). * * You must implement hook_menu() to emit items to place in the main menu. * This is a required step for modules wishing to display their own pages, * because the process of creating the links also tells Drupal what * callback function to use for a given URL. The menu items returned * here provide this information to the menu system. * * With the below menu definitions, URLs will be interpreted as follows: * * If the user accesses http://example.com/?q=foo, then the menu system * will first look for a menu item with that path. In this case it will * find a match, and execute thaisearch_foo(). * * If the user accesses http://example.com/?q=bar, no match will be found, * and a 404 page will be displayed. * * If the user accesses http://example.com/?q=bar/baz, the menu system * will find a match and execute thaisearch_baz(). * * If the user accesses http://example.com/?q=bar/baz/1/2, the menu system * will first look for bar/baz/1/2. Not finding a match, it will look for * bar/baz/1/%. Again not finding a match, it will look for bar/baz/%/2. Yet * again not finding a match, it will look for bar/baz/%/%. This time it finds * a match, and so will execute thaisearch_baz(1, 2). Note the parameters * being passed; this is a very useful technique. */ function thaisearch_menu() { // By using the MENU_CALLBACK type, we can register the callback for this // path but not have the item show up in the menu; the admin is not allowed // to enable the item in the menu, either. // // Notice that the 'page arguments' is an array of numbers. These will be // replaced with the corresponding parts of the menu path. In this case a 0 // would be replaced by 'thaisearch', and 1 will be Thai words to search. // These will be passed as arguments to the thaisearch_thaisearch() function. $items['thaisearch/%'] = array( 'title' => 'Thai Search', 'page callback' => 'thaisearch_thaisearch', 'page arguments' => array(1), 'access arguments' => array('access thaisearch'), // 'type' => MENU_CALLBACK, ); return $items; } /** * A more complex page callback that takes arguments. * * The arguments are passed in from the page URL. The in our hook_menu * implementation we instructed the menu system to extract the last two * parameters of the path and pass them to this function as arguments. */ function thaisearch_thaisearch($words) { $max_item = 50; $keys = explode(" ", $words); $qnt = "SELECT 2 AS score, n.nid, 0 AS cid, n.title, n.body FROM {node_revisions} n WHERE n.title LIKE '%%%s%%' "; $qnb = "SELECT 1 AS score, n.nid, 0 AS cid, n.title, n.body FROM {node_revisions} n WHERE n.body LIKE '%%%s%%' "; $qc = "SELECT 1 AS score, c.nid, c.cid, n.title, c.comment AS body FROM {comments} c INNER JOIN {node_revisions} n ON n.nid = c.nid WHERE c.comment LIKE '%%%s%%' "; $q = 'SELECT SUM(score) AS score, nid, cid, title, body FROM ('.$qnt.' UNION ALL '.$qnb.' UNION ALL '.$qc.' ) t GROUP BY t.nid, t.cid, t.title, t.body ORDER BY score, nid DESC'; $query = db_query_range($q, $keys[0], $keys[0], $keys[0], 0, $max_item); $sql_count = 'SELECT COUNT(*) AS num FROM ('.$q.') t'; $count = db_fetch_object(db_query($sql_count, $keys[0], $keys[0], $keys[0])); $found = $count->num; $return = thaisearch_help('thaisearch','').'<br />'; $return .= t('Search words: <strong>'.$words.'</strong>, found: <strong>'.$found.'</strong><br />'); $dlist = ''; $max_words = 20; $words_between = 10; while ($links = db_fetch_object($query)) { // highlight words $ar = explode($keys[0], $links->body); if ($ar[0]) { $len_left = drupal_strlen($ar[0]); $ar[0] = ($max_words > $len_left) ? $ar[0] : '...'.drupal_substr($ar[0], $len_left-$max_words, $max_words); } for ($i = 1; $i < count($ar); $i++) { $len_right = drupal_strlen($ar[$i]); if ($i == count($ar)-1) { $ar[$i] = ($max_words > $len_right) ? $ar[$i] : drupal_substr($ar[$i], 0, $max_words).'...'; } else { $ar[$i] = ($max_words > $len_right) ? $ar[$i] : drupal_substr($ar[$i], 0, $words_between).'...'.drupal_substr($ar[$i], $len_right-$word_between, $word_between); } } if ($links->cid != 0) { $dlist .= '<dt>'.l($links->title, 'node/'.$links->nid, array('fragment' => 'comment-'.$links->cid)).'</dt>'; } else { $dlist .= '<dt>'.l($links->title, 'node/'.$links->nid).'</dt>'; } $dlist .= '<dd>'.implode('<strong>'.$keys[0].'</strong>', $ar).'</dd><br />'; } if ($dlist) { $return .= theme('box', t('Search results'), '<br />'.$dlist, 10, 0); } else { $return .= theme('box', t('Your search yielded no results')); } return $return; } ?>
รันได้รวดเร็วดีพอควร
(รอผู้ใจบุญ)
เพื่อความสุขสวัสดี อย่าลืมรัน update.php
ด้วย ไม่งั้นอาจมีปัญหาเรื่อง HTTP request failed
ยังไม่เข้าใจการทำงานมอดูลอย่างจริงจัง ตอนนี้ใช้วิธีตัดแปะจาก Core Module ไปก่อน
block
เพิ่ม css เพิ่ม watchdog
admin/reports/updates/check
-> ย้ายมอดูลกลับดาวน์โหลดได้แล้ว
ติดตั้งตามวิธีปกติ
$ cd /var/www/drupal/sites/all/modules
$ wget http://www.thaitux.info/files/modules/thaisearch-6.x-0.1.tar.gz
$ tar xfz thaisearch-6.x-0.1.tar.gz
หรือเขียนโค๊ดเอง
เพิ่มไฟล์ css
$ cd /var/www/drupal/sites/all/modules/thaisearch
$ vi thaisearch.css
/* $Id: thaisearch.css,v 1.3 2007/10/31 18:06:38 dries Exp $ */ .block-thaisearch .form-item { display: inline; margin: 0; padding: 0; }
แก้เฉพาะไฟล์ thaisearch.module
$ vi thaisearch.module
<?php // $Id: thaisearch.module,v 1.13 2007/10/17 19:38:36 litwol Exp $ // wd's: modify to thaisearch.module, simple search Thai words. v.2008/1/24. /** * @file * This is an example outlining how a module can be used to display a * custom page at a given URL. */ /** * Implementation of hook_help(). * * Throughout Drupal, hook_help() is used to display help text at the top of * pages. Some other parts of Drupal pages get explanatory text from these hooks * as well. We use it here to illustrate how to add help text to the pages your * module defines. */ function thaisearch_help($path, $arg) { switch ($path) { case 'thaisearch': // Here is some help text for a custom page. return t('Search thai words. Type in URL: <code><strong>thaisearch/<em>WORD1 WORD2 ...</em></strong></code><br />Words separated by space, quote or double-quote are allowed.<br /><code>a "b c" d</code> gives <code>"<strong>a</strong>"</code> , <code>"<strong>b c</strong>"</code> , <code>"<strong>d</strong>"</code>'); case 'search#noresults': return t('<ul> <li>Check if your spelling is correct.</li> <li>Remove quotes around phrases to match each word individually: <em>"blue smurf"</em> will match less than <em>blue smurf</em>.</li> <li>Consider loosening your query with <em>OR</em>: <em>blue smurf</em> will match less than <em>blue OR smurf</em>.</li> </ul>'); } } /** * Implementation of hook_perm(). * * Since the access to our new custom pages will be granted based on * special permissions, we need to define what those permissions are here. * This ensures that they are available to enable on the user role * administration pages. */ function thaisearch_perm() { return array('access thaisearch'); } /** * Implementation of hook_block(). */ function thaisearch_block($op='list', $delta=0) { // listing of blocks, such as on the admin/block page if ($op == "list") { $block[0]["info"] = t('Thai Search'); return $block; } else if ($op == 'view' && user_access('access thaisearch')) { $block['content'] = drupal_get_form('thaisearch_form', $keys, $size=15); $block['subject'] = t('Thai Search'); return $block; } } /** * Implementation of hook_menu(). * * You must implement hook_menu() to emit items to place in the main menu. * This is a required step for modules wishing to display their own pages, * because the process of creating the links also tells Drupal what * callback function to use for a given URL. The menu items returned * here provide this information to the menu system. * * With the below menu definitions, URLs will be interpreted as follows: * * If the user accesses http://example.com/?q=foo, then the menu system * will first look for a menu item with that path. In this case it will * find a match, and execute thaisearch_foo(). * * If the user accesses http://example.com/?q=bar, no match will be found, * and a 404 page will be displayed. * * If the user accesses http://example.com/?q=bar/baz, the menu system * will find a match and execute thaisearch_baz(). * * If the user accesses http://example.com/?q=bar/baz/1/2, the menu system * will first look for bar/baz/1/2. Not finding a match, it will look for * bar/baz/1/%. Again not finding a match, it will look for bar/baz/%/2. Yet * again not finding a match, it will look for bar/baz/%/%. This time it finds * a match, and so will execute thaisearch_baz(1, 2). Note the parameters * being passed; this is a very useful technique. */ function thaisearch_menu() { // By using the MENU_CALLBACK type, we can register the callback for this // path but not have the item show up in the menu; the admin is not allowed // to enable the item in the menu, either. // // Notice that the 'page arguments' is an array of numbers. These will be // replaced with the corresponding parts of the menu path. In this case a 0 // would be replaced by 'thaisearch', and 1 will be Thai words to search. // These will be passed as arguments to the thaisearch_thaisearch() function. $items['thaisearch/%'] = array( 'title' => 'Thai Search', 'page callback' => 'thaisearch_thaisearch', 'page arguments' => array(1), 'access arguments' => array('access thaisearch'), // 'type' => MENU_CALLBACK, ); $items['thaisearch'] = array( 'title' => 'Thai Search', 'page callback' => 'thaisearch_view', 'access arguments' => array('access thaisearch'), 'type' => MENU_SUGGESTED_ITEM, ); return $items; } /** * Form builder; Output a search form for the search block and the theme's search box. * * @ingroup forms * @see search_box_form_submit() * @see theme_search_box_form() */ function thaisearch_form(&$form_state, $form_id, $size=25) { // Add CSS drupal_add_css(drupal_get_path('module', 'thaisearch') .'/thaisearch.css', 'module', 'all', FALSE); $form['keys'] = array( '#title' => t('Search Thai words'), '#type' => 'textfield', '#size' => $size, '#default_value' => '', '#attributes' => array('title' => t('Enter the terms you wish to search for.')), ); $form['submit'] = array('#type' => 'submit', '#value' => t('Search')); $form['#submit'][] = 'thaisearch_form_submit'; $form['#validate'][] = 'thaisearch_form_validate'; return $form; } /** * Process a block search form submission. */ function thaisearch_form_submit($form, &$form_state) { $keys = $form['keys']['#value']; $form_state['redirect'] = 'thaisearch/'. $keys; } /** * Helper function for grabbing search keys. */ function thaisearch_get_keys() { static $return; if (!isset($return)) { // Extract keys as remainder of path // Note: support old GET format of searches for existing links. $path = explode('/', $_GET['q'], 2); $keys = empty($_REQUEST['keys']) ? '' : $_REQUEST['keys']; $return = count($path) == 2 ? $path[1] : $keys; } return $return; } /** * Menu callback; presents the search form and/or search results. */ function thaisearch_view() { // Search form submits with POST but redirects to GET. This way we can keep // the search query URL clean as a whistle: // search/type/keyword+keyword if (!isset($_POST['form_id'])) { $keys = thaisearch_get_keys(); // Only perform search if there is non-whitespace search term: $results = ''; if (trim($keys)) { // Collect the search results: $results = thaisearch_thaisearch($keys); if ($results) { $results = theme('box', t('Search results'), $results); } else { $results = theme('box', t('Your search yielded no results'), thaisearch_help('search#noresults', drupal_help_arg())); } } // Construct the search form. $output = drupal_get_form('thaisearch_form', $keys); $output .= $results; return $output; } return drupal_get_form('thaisearch_form', empty($keys) ? '' : $keys); } /** * Helper function: is word in string */ function _in_string($string, $word) { if (strpos($string, $word) === FALSE) { return FALSE; } else { return TRUE; } } /** * Helper function: get array of wordlist from words * 'a "b c" d' => ['a "b c" d', 'a', 'b c', 'd'] */ function _get_wordlist($string) { $temp = trim($string); $wordlist = array(); $wordlist[] = $string; while (strlen($temp) > 0) { $m = 9999; if (_in_string($temp, " ")) { $p1 = strpos($temp, " "); $m = ($m < $p1) ? $m : $p1; } if (_in_string($temp, '"')) { $p2 = strpos($temp, '"'); $m = ($m < $p2) ? $m : $p2; } if (_in_string($temp, "'")) { $p3 = strpos($temp, "'"); $m = ($m < $p3) ? $m : $p3; } if ($m == $p1) { $a = explode(" ",$temp,2); $wordlist[] = $a[0]; $temp = trim($a[1]); } elseif ($m == $p2 and substr_count($temp, '"') > 1 and (_in_string($temp, '" ') or substr($temp, -1) == '"')) { $a = explode('"',$temp,3); $wordlist[] = $a[1]; $temp = trim($a[2]); } elseif ($m == $p3 and substr_count($temp, "'") > 1 and (_in_string($temp, "' ") or substr($temp, -1) == "'")) { $a = explode("'",$temp,3); $wordlist[] = $a[1]; $temp = trim($a[2]); } else { $wordlist[] = $temp; $temp = ''; } } return($wordlist); } /** * Helper function for array_walk in thaisearch_except. */ function _thaisearch_excerpt_replace(&$text) { $text = preg_quote($text, '/'); } /** * Helper function for trim text */ function _trim_text($text, $pos = 'first', $length = 50) { $l = drupal_strlen($text); if ($l > $length) { switch ($pos) { case 'first': return ' ...'.drupal_substr($text, $l-$length, $length); case 'middle': $b = intval($length / 2); return drupal_substr($text, 0, $b).'...'.drupal_substr($text, $l-$b, $b); case 'last': return drupal_substr($text, 0, $length).'... '; } } return $text; } /** * A more complex page callback that takes arguments. * * The arguments are passed in from the page URL. The in our hook_menu * implementation we instructed the menu system to extract the last two * parameters of the path and pass them to this function as arguments. * do search from node_revision.title, node_revision.body and comments.comment */ function thaisearch_thaisearch($keys) { // Log the search keys: watchdog('thaisearchsearch', '%keys (@type).', array('%keys' => $keys, '@type' => module_invoke($type, 'thaisearch', 'name')), WATCHDOG_NOTICE, l(t('results'), 'thaisearch/'. $type .'/'. $keys)); $max_item = 50; $wordlist = _get_wordlist($keys); $qarray = array(); $arguments = array(); for ($iwl = 0; $iwl < count($wordlist); $iwl++) { // first argument in full word: allscore*10 switch ($iwl) { case 0: $w = 10; case 1: $w = 2; default: $w = 1; } // node.title: score*10 $qarray[] = " SELECT ".strval($w*10)." AS score, n.nid, 0 AS cid, n.title, n.body, n.timestamp FROM {node_revisions} n WHERE n.title LIKE '%%%s%%' "; // node.body: score*2 $qarray[] = " SELECT ".strval($w*2)." AS score, n.nid, 0 AS cid, n.title, n.body, n.timestamp FROM {node_revisions} n WHERE n.body LIKE '%%%s%%' "; // comments.comment: score*1 $qarray[] = " SELECT ".strval($w*1)." AS score, c.nid, c.cid, n.title, c.comment, c.timestamp AS body FROM {comments} c INNER JOIN {node_revisions} n ON n.nid = c.nid WHERE c.comment LIKE '%%%s%%' "; $arguments = array_merge($arguments, array($wordlist[$iwl], $wordlist[$iwl], $wordlist[$iwl])); } $q = 'SELECT SUM(score) AS score, nid, cid, title, body, timestamp FROM ('.implode(' UNION ALL ',$qarray).') t GROUP BY t.nid, t.cid, t.title, t.body, t.timestamp ORDER BY score DESC, nid DESC, timestamp DESC'; $sql_count = 'SELECT COUNT(*) AS num FROM ('.$q.') t'; $count = db_fetch_object(db_query($sql_count, $arguments)); $found = $count->num; $query = db_query_range($q, $arguments, 0, $max_item); // highlight words in trimmed output $dlist = ''; $max_length= 50; while ($links = db_fetch_object($query)) { // highlight words $text = ' '. strip_tags(str_replace(array('<', '>'), array(' <', '> '), $links->body)) .' '; array_walk($wordlist, '_thaisearch_excerpt_replace'); $text = preg_replace('/('. implode('|', $wordlist) .')/iu', '<strong>\0</strong>', $text); // trim output $text_front = ''; if (_in_string($text, '<strong>')) { $item_count = 0; $text_array = explode('<strong>', $text, 2); $text_front = _trim_text($text_array[0], 'first', $max_length).'<strong>'; $text_array = explode('</strong>', $text_array[1], 2); $text_front .= $text_array[0].'</strong>'; $text = $text_array[1]; while (_in_string($text, '<strong>') and $item_count < 5) { $item_count++; $text_array = explode('<strong>', $text, 2); $text_front .= _trim_text($text_array[0], 'middle', $max_length).'<strong>'; $text_array = explode('</strong>', $text_array[1], 2); $text_front .= $text_array[0].'</strong>'; if (_in_string($text_array[1], '<strong>')) { $text = $text_array[1]; } else { $text = _trim_text($text_array[1], 'last', $max_length); } } $text_front .= _trim_text($text, 'last', $max_length); } else { $text_front = truncate_utf8($text, $max_length, TRUE); } $dlist .= '<dt><strong>'.l($links->title, 'node/'.$links->nid).'</strong></dt>'; $dlist .= '<dd>'.$text_front.'</dd></strong><br />'; } $output = thaisearch_help('thaisearch','').'<br />'; $output .= drupal_get_form('thaisearch_form', $keys); $output .= t('Search words: <strong>'.$keys.'</strong>, found: <strong>'.$found.'</strong><br />'); if ($dlist) { $output .= theme('box', t('Search results'), '<br /><dl>'.$dlist.'</dl>', 10, 0); } else { $output .= theme('box', t('Your search yielded no results')); } return $output; } ?>
ผ่านไปนานแล้ว แต่มอดูล Search ของ drupal ก็ยังค้นภาษาไทยได้ไม่ดีขึ้นเลย จึงปรับปรุงของเก่าให้ใช้งานได้ดีขึ้นครับ
ยังตามมาหลอกหลอนถึง Drupal 7
เอามาจาก Creating modules - a tutorial: Drupal 6.x
พยายามเขียนให้เป็นเรื่องเป็นราว แต่ให้สั้น ๆ
ตามตัวอย่างเป็นการสร้างมอดูลชื่อ onthisdate
เพื่อจะทำเป็นบล๊อกแสดง "วันนี้ในอดีตเมื่ออาทิตย์ก่อน"
ลิงก์ที่ต้องไป
ควรเขียนไว้ภายใต้ site/all/module/onthisdate
เพื่อไม่ให้ปนกับของ Drupal เอง และปลอดภัยจากการอัปเกรด
$ cd /var/www/drupal
$ mkdir -p site/all/module/onthisdate
โครงสร้างชื่อฟังก์ชั่นในมอดูลจะเป็น
function {modulename}_{hook}
เช่น onthisdate_help
เพื่อข่วยเหลือ หรือ onthisdate_menu
เพื่อแสดงเมนู เป็นต้น
ต้องสร้างไฟล์ module_name.info
สำหรับบอก Drupal ตัวอย่างนี้คือ
$ vi onthisdate.info
; $Id$ name = On this date description = A block module that lists links to content such as blog entries or forum discussions that were created one week ago. core = 6.x
ต้องมีหัวข้อดังนี้
description = This is my "crazy@email.com" email address instead of description = This is my "crazy@email.com" email address
อาจมีหัวข้อดังนี้
dependencies[] = taxonomy dependencies[] = comment
package = "ชื่อมอดูลที่ร่วม"
ลองฟังก์ชั่นแรกคือ help
$ vi onthisdate.module
<?php /** * Display help and module information * @param path which path of the site we're displaying help * @param arg array that holds the current path as would be returned from arg() function * @return help text for the path */ function onthisdate_help($path, $arg) { $output = ''; switch ($path) { case "admin/help#onthisdate": $output = '<p>'. t("Displays links to nodes created on this date") .'</p>'; break; } return $output; } // function onthisdate_help ?>
ตัวแปร $path
แทนพาธว่า help
ของเราจะไปอยู่ตรงไหน
ดูเพิ่มที่
เขียนชื่อฟังก์ชั่นในรูป hook_perm
รูปแบบฟังก์ชั่นคือ
<?php function newmodule_perm() { return array('access newmodule', 'create newmodule', 'administer newmodule'); } / function newmodule_perm ?>
ตามตัวอย่างนี้คือ
<?php /** * Valid permissions for this module * @return array An array of valid permissions for the onthisdate module */ function onthisdate_perm() { return array('access onthisdate content', 'administer onthisdate'); } // function onthisdate_perm ?>
ดูเพิ่มที่
ถ้ามอดูลเราทำบล๊อกด้วย เราต้องเขียนฟังก์ชั่นในรูป hook_block
ดังนี้
<?php /** * Generate HTML for the onthisdate block * @param op the operation from the URL * @param delta offset * @returns block HTML */ function onthisdate_block($op='list', $delta=0) { // listing of blocks, such as on the admin/block page if ($op == "list") { $block[0]["info"] = t('On This Date'); return $block; } } // end onthisdate_block ?>
ตัวแปร $op
เรียกว่า operation บอกว่าข้อมูลบล๊อกของเราอยู่ในรูปไหน ในที่นี้เป็น list
ตัวแปร $delta
เรียกว่า offset บอกว่าระหว่างการแสดงผลเป็นบล๊อก หรือแสดงผลในรูปอื่นมีข้อแตกต่างกันหรือเปล่า
ดูเพิ่ม
มอดูลนี้ เราจะสร้างรายการของเนื้อหา (nodes
) ของวันนี้ในสัปดาห์ก่อน เวลาเราจะดึงรายการมา เราดูจากเวลาที่เนื้อหาถูกสร้าง โดยเราทำในรูปวินาที (ดู php เรื่องเวลา)
<?php /** * Generate HTML for the onthisdate block * @param op the operation from the URL * @param delta offset * @returns block HTML */ function onthisdate_block($op='list', $delta=0) { // listing of blocks, such as on the admin/block page if ($op == "list") { $block[0]["info"] = t('On This Date'); return $block; } else if ($op == 'view') { // our block content // Get today's date $today = getdate(); // calculate midnight one week ago $start_time = mktime(0, 0, 0, $today['mon'], ($today['mday'] - 7), $today['year']); // we want items that occur only on the day in question, so // calculate 1 day $end_time = $start_time + 86400; // 60 * 60 * 24 = 86400 seconds in a day } //We'll use db_query() to get the records (i.e. the database rows) with our SQL query $result = db_query("SELECT nid, title, created FROM {node} WHERE created >= '%s' AND created <= '%s'", $start_time, $end_time); // content variable that will be returned for display $block_content = ''; while ($links = db_fetch_object($result)) { $block_content .= l($links->title, 'node/'. $links->nid) .'<br />'; } // check to see if there was any content before setting up // the block if ($block_content == '') { /* No content from a week ago. If we return nothing, the block * doesn't show, which is what we want. */ return; } // set up the block $block['subject'] = 'On This Date'; $block['content'] = $block_content; return $block; } ?>
db_query()
โดยให้ชื่อตารางในฐานข้อมูลอยู่ในรูป {node}
ดูรายละเอียดจาก Table Prefix (and sharing tables across instances)db_fetch_object()
l()
ให้อยู่ในรูปของ <li><a href="node/nid">title</li>
onthisdate
(มีไฟล์ onthisdate.info
และ onthisdate.module
) ไปไว้ที่ sites/all/modules หรือ sites/hostname/modules
admin/build/modules
แล้วกาถูกadmin/build/block
onthisdate_admin
array( '#name => 'value', ... )
<?php function onthisdate_admin() { $form['onthisdate_maxdisp'] = array( '#type' => 'textfield', '#title' => t('Maximum number of links'), '#default_value' => variable_get('onthisdate_maxdisp', 3), '#size' => 2, '#maxlength' => 2, '#description' => t("The maximum number of links to display in the block."), '#required' => TRUE, ); return system_settings_form($form); } ?>
t()
ในการแสดงผลอักขระvariable_get('variable_name',default_value)
ในการรับค่าตัวแปรจากระบบ ซึ่งในที่นี้เรากำหนดค่าปริยายให้ onthisdate_maxdisp
เป็น 3system_settings_form()
เอาส่วนของการตั้งค่านี้ คือตัวเลข onthisdate_maxdisp
ไปใส่ในฟังก์ชั่น onthisdate_block()
ดังนี้
//--- onthisdate_block function --- ... $limitnum = variable_get("onthisdate_maxdisp", 3); $query = "SELECT nid, title, created FROM " . "{node} WHERE created >= %d " . "AND created <= %d"; $queryResult = db_query_range($query, $start_time, $end_time, 0, $limitnum); ...
onthisdate_menu
<?php function onthisdate_menu() { $items = array(); $items['admin/settings/onthisdate'] = array( 'title' => 'On this date module settings', 'description' => 'Description of your On this date settings control', 'page callback' => 'drupal_get_form', 'page arguments' => array('onthisdate_admin'), 'access arguments' => array('access administration pages'), 'type' => MENU_NORMAL_ITEM, ); return $items; } ?>
onthisdate_admin_validate
<?php function onthisdate_admin_validate($form, &$form_state) { $maxdisp = $form_state['values']['onthisdate_maxdisp']; if (!is_numeric($maxdisp)) { form_set_error('onthisdate_maxdisp', t('You must select a number for the maximum number of links.')); } else if ($maxdisp <= 0) { form_set_error('onthisdate_maxdisp', t('Maximum number of links must be positive.')); } } ?>
ดูเพิ่ม
โค๊ดหลักมีแต่การแสดงเนื้อในบล๊อกซึ่งมีเนื้อที่จำกัด คราวนี้เรามาเพิ่มให้แสดงเนื้อในหน้าหลักได้ โดยเราสามารถแสดงได้ไม่จำกัดจำนวนหัวข้อ
ทำผ่านฟังก์ชั่น onthisdate_all()
<?php function onthisdate_all() { // content variable that will be returned for display $page_content = ''; // Get today's date $today = getdate(); // calculate midnight one week ago $start_time = mktime(0, 0, 0, $today['mon'], ($today['mday'] - 7), $today['year']); // we want items that occur only on the day in question, // so calculate 1 day $end_time = $start_time + 86400; // 60 * 60 * 24 = 86400 seconds in a day $query = "SELECT nid, title, created FROM " . "{node} WHERE created >= '%d' " . " AND created <= '%d'"; // get the links (no range limit here) $queryResult = db_query($query, $start_time, $end_time); while ($links = db_fetch_object($queryResult)) { $page_content .= l($links->title, 'node/'.$links->nid).'<br />'; } // check to see if there was any content before // setting up the block if ($page_content == '') { // no content from a week ago, let the user know $page_content = "No events occurred on this site on this date in history."; } return $page_content; } ?>
onthisdate_all
ไม่ใช่ฮุก ถ้ามอดูลอื่นจะเรียกใช้ ต้องเรียกผ่านฟังก์ชั่นระบบ module_invoke()
_
ถ้าฟังก์ชั่นไหนของเราไม่ใช่ฮุก เราต้องบอกให้ Drupal รับรู้ถึงฟังก์ชั่นเราเสมอ
ทำได้ผ่านฮุก onthisdate_menu()
โดยกลับไปแก้ไขงานจากคราวก่อน
<?php function onthisdate_menu() { $items = array(); //this was created earlier in tutorial 7. $items['admin/settings/onthisdate'] = array( 'title' => 'On this date module settings', 'description' => 'Description of your On this date settings control', 'page callback' => 'drupal_get_form', 'page arguments' => array('onthisdate_admin'), 'access arguments' => array('access administration pages'), 'type' => MENU_NORMAL_ITEM, ); //this is added for this current tutorial. $items['onthisdate'] = array( 'title' => 'On this date', 'page callback' => 'onthisdate_all', 'access arguments' => array('access onthisdate content'), 'type' => MENU_CALLBACK ); return $items; } ?>
onthisdate
ฟังก์ชั่น onthisdate_all()
จะทำงานtype
) ของเมนู คือ
MENU_NORMAL_ITEM
เป็นเมนูปกติที่ผู้ใช้มองเห็นMENU_CALLBACK
ไม่แสดงในเมนูจริง ๆ แต่จะถูกเรียกใช้ผ่านทาง URL เท่านั้นดูเพิ่ม
เพิ่มลิงก์ 'more' หรือ 'มีต่อ' ในการแสดงหัวข้อเพิ่มเติมจากที่ล้นเนื้อบล๊อก
กลับไปแก้งานในส่วนของ onthisdate_block
<?php // add a more link to our page that displays all the links $block_content .= "<div class=\"more-link\">". l( t("more"), "onthisdate", array( "title" => t("More events on this day.") ) )."</div>"; ?>
ดูเพิ่ม
ได้ดังนี้
$ cd /var/www/drupal
$ mkdir -p sites/all/module/onthisdate
ไฟล์ info
$ vi onthisdate.info
; $Id$ name = On this date description = A block module that lists links to content such as blog entries or forum discussions that were created one week ago. core = 6.x
ไฟล์ module
$ vi onthisdate.module
<?php /** * Display help and module information * @param path which path of the site we're displaying help * @param arg array that holds the current path as would be returned from arg() function * @return help text for the path */ function onthisdate_help($path, $arg) { $output = ''; switch ($path) { case "admin/help#onthisdate": $output = '<p>'. t("Displays links to nodes created on this date") .'</p>'; break; } return $output; } // function onthisdate_help /** * Valid permissions for this module * @return array An array of valid permissions for the onthisdate module */ function onthisdate_perm() { return array('access onthisdate content', 'administer onthisdate'); } // function onthisdate_perm /** * Generate HTML for the onthisdate block * @param op the operation from the URL * @param delta offset * @returns block HTML */ function onthisdate_block($op='list', $delta=0) { // listing of blocks, such as on the admin/block page if ($op == "list") { $block[0]["info"] = t('On This Date'); return $block; } else if ($op == 'view') { // our block content // Get today's date $today = getdate(); // calculate midnight one week ago $start_time = mktime(0, 0, 0, $today['mon'], ($today['mday'] - 7), $today['year']); // we want items that occur only on the day in question, so // calculate 1 day $end_time = $start_time + 86400; // 60 * 60 * 24 = 86400 seconds in a day } //We'll use db_query() to get the records (i.e. the database rows) with our SQL query $limitnum = variable_get("onthisdate_maxdisp", 3); $query = "SELECT nid, title, created FROM " . "{node} WHERE created >= %d " . "AND created <= %d"; $queryResult = db_query_range($query, $start_time, $end_time, 0, $limitnum); // content variable that will be returned for display $block_content = '<ul>'; while ($links = db_fetch_object($queryResult)) { $block_content .= '<li>'.l($links->title, 'node/'. $links->nid) .'</li>'; } // check to see if there was any content before setting up // the block if ($block_content == '<ul>') { /* No content from a week ago. If we return nothing, the block * doesn't show, which is what we want. */ return; } $block_content .= '</ul>'; // add a more link to our page that displays all the links $block_content .= "<div class=\"more-link\">". l( t("more"), "onthisdate", array( "title" => t("More events on this day.") ) )."</div>"; // set up the block $block['subject'] = 'On This Date'; $block['content'] = $block_content; return $block; } // end onthisdate_block function onthisdate_admin() { $form['onthisdate_maxdisp'] = array( '#type' => 'textfield', '#title' => t('Maximum number of links'), '#default_value' => variable_get('onthisdate_maxdisp', 3), '#size' => 2, '#maxlength' => 2, '#description' => t("The maximum number of links to display in the block."), '#required' => TRUE, ); return system_settings_form($form); } function onthisdate_menu() { $items = array(); //this was created earlier in tutorial 7. $items['admin/settings/onthisdate'] = array( 'title' => 'On this date module settings', 'description' => 'Description of your On this date settings control', 'page callback' => 'drupal_get_form', 'page arguments' => array('onthisdate_admin'), 'access arguments' => array('access administration pages'), 'type' => MENU_NORMAL_ITEM, ); //this is added in tutorial 9. $items['onthisdate'] = array( 'title' => 'On this date', 'page callback' => 'onthisdate_all', 'access arguments' => array('access onthisdate content'), 'type' => MENU_CALLBACK, ); return $items; } function onthisdate_admin_validate($form, &$form_state) { $maxdisp = $form_state['values']['onthisdate_maxdisp']; if (!is_numeric($maxdisp)) { form_set_error('onthisdate_maxdisp', t('You must select a number for the maximum number of links.')); } else if ($maxdisp <= 0) { form_set_error('onthisdate_maxdisp', t('Maximum number of links must be positive.')); } } function onthisdate_all() { // content variable that will be returned for display $page_content = ''; // Get today's date $today = getdate(); // calculate midnight one week ago $start_time = mktime(0, 0, 0, $today['mon'], ($today['mday'] - 7), $today['year']); // we want items that occur only on the day in question, // so calculate 1 day $end_time = $start_time + 86400; // 60 * 60 * 24 = 86400 seconds in a day $query = "SELECT nid, title, created FROM " . "{node} WHERE created >= '%d' " . " AND created <= '%d'"; // get the links (no range limit here) $queryResult = db_query($query, $start_time, $end_time); while ($links = db_fetch_object($queryResult)) { $page_content .= l($links->title, 'node/'.$links->nid).'<br />'; } // check to see if there was any content before // setting up the block if ($page_content == '') { // no content from a week ago, let the user know $page_content = "No events occurred on this site on this date in history."; } return $page_content; } ?>
ติดตั้ง
ติดอยู่แล้ว
เปิดใช้
admin/build/module
กาถูกหน้า onthisdateadmin/user/permissions
เลือกกาถูกข้ออนุญาตที่เกี่ยวข้องกับ onthisdateตั้งค่า
admin/settings/onthisdate
ตั้งจำนวนหัวข้อadmin/build/block
เลือกกาถูก On this dateทดสอบ
ดูที่บล๊อก On this date ตามต้องการ
มีตัวอย่างการสร้าง node และ page แอบอยู่ที่ api.drupal.org คือ
สามารถดูซอร์ส แล้วเอามาทดลองสร้างมอดูลเองได้
จะแปลงมอดูลจากตัวอย่าง คือ On This Date ซึ่งดู "วันนี้ในอาทิตย์ก่อน" มาเป็น Recent Week คือดูหัวข้อใหม่ในสัปดาห์นี้ (เหมือนกับ tracker แต่ทำเป็นบล๊อกได้)
เริ่มเลย
$ cd /var/www/drupal/sites/all/modules
$ cp -xa onthisdate recentweek
$ cd recentweek
$ mv onthisdate.info recentweek.info
$ mv onthisdate.module recentweek.module
$ sed -i "s/onthisdate/recentweek/g" *
$ sed -i "s/on this date/recent week/g" *
$ sed -i "s/On this date/Recent week/g" *
$ sed -i "s/On This Date/Recent Week/g" *
$ vi recentweek.module
<?php /** * Display help and module information * @param path which path of the site we're displaying help * @param arg array that holds the current path as would be returned from arg() function * @return help text for the path */ function recentweek_help($path, $arg) { $output = ''; switch ($path) { case "admin/help#recentweek": $output = '<p>'. t("Displays links to nodes created recent week") .'</p>'; break; } return $output; } // function recentweek_help /** * Valid permissions for this module * @return array An array of valid permissions for the recentweek module */ function recentweek_perm() { return array('access recentweek content', 'administer recentweek'); } // function recentweek_perm /** * Generate HTML for the recentweek block * @param op the operation from the URL * @param delta offset * @returns block HTML */ function recentweek_block($op='list', $delta=0) { // listing of blocks, such as on the admin/block page if ($op == "list") { $block[0]["info"] = t('Recent Week'); return $block; } else if ($op == 'view') { // our block content // Get today's date $today = getdate(); // calculate midnight one week ago $start_time = mktime(0, 0, 0, $today['mon'], ($today['mday'] - 7), $today['year']); /* wd's mod // we want items that occur only on the day in question, so // calculate 1 day $end_time = $start_time + 86400; // 60 * 60 * 24 = 86400 seconds in a day */ #wd#// delete parameter $end_time, $query changed } //We'll use db_query() to get the records (i.e. the database rows) with our SQL query $limitnum = variable_get("recentweek_maxdisp", 3); /* wd's mod $query = "SELECT nid, title, created FROM " . "{node} WHERE created >= %d " . "AND created <= %d"; $queryResult = db_query_range($query, $start_time, $end_time, $limitnum); */ #wd#// delete parameter $end_time, $query changed $query = "SELECT nid, title, created FROM " . "{node} WHERE created >= %d ORDER BY created DESC" ; $queryResult = db_query_range($query, $start_time, 0, $limitnum); // content variable that will be returned for display $block_content = '<ul>'; while ($links = db_fetch_object($queryResult)) { $block_content .= '<li>'.l($links->title, 'node/'. $links->nid) .'</li>'; } // check to see if there was any content before setting up // the block if ($block_content == '<ul>') { /* No content from a week ago. If we return nothing, the block * doesn't show, which is what we want. */ return; } $block_content .= '</ul>'; // add a more link to our page that displays all the links $block_content .= "<div class=\"more-link\">". l( t("more"), "recentweek", array( "title" => t("More events on this day.") ) )."</div>"; // set up the block $block['subject'] = 'Recent Week'; $block['content'] = $block_content; return $block; } // end recentweek_block function recentweek_admin() { $form['recentweek_maxdisp'] = array( '#type' => 'textfield', '#title' => t('Maximum number of links'), '#default_value' => variable_get('recentweek_maxdisp', 3), '#size' => 2, '#maxlength' => 2, '#description' => t("The maximum number of links to display in the block."), '#required' => TRUE, ); return system_settings_form($form); } function recentweek_menu() { $items = array(); //this was created earlier in tutorial 7. $items['admin/settings/recentweek'] = array( 'title' => 'Recent week module settings', 'description' => 'Description of your Recent week settings control', 'page callback' => 'drupal_get_form', 'page arguments' => array('recentweek_admin'), 'access arguments' => array('access administration pages'), 'type' => MENU_NORMAL_ITEM, ); //this is added in tutorial 9. $items['recentweek'] = array( 'title' => 'Recent week', 'page callback' => 'recentweek_all', 'access arguments' => array('access recentweek content'), 'type' => MENU_CALLBACK, ); return $items; } function recentweek_admin_validate($form, &$form_state) { $maxdisp = $form_state['values']['recentweek_maxdisp']; if (!is_numeric($maxdisp)) { form_set_error('recentweek_maxdisp', t('You must select a number for the maximum number of links.')); } else if ($maxdisp <= 0) { form_set_error('recentweek_maxdisp', t('Maximum number of links must be positive.')); } } function recentweek_all() { // content variable that will be returned for display $page_content = ''; // Get today's date $today = getdate(); // calculate midnight one week ago $start_time = mktime(0, 0, 0, $today['mon'], ($today['mday'] - 7), $today['year']); /* wd's // we want items that occur only on the day in question, // so calculate 1 day $end_time = $start_time + 86400; // 60 * 60 * 24 = 86400 seconds in a day #wd#// delete parameter $end_time, $query changed $query = "SELECT nid, title, created FROM " . "{node} WHERE created >= '%d' " . " AND created <= '%d'"; $queryResult = db_query($query, $start_time, $end_time); */ $query = "SELECT nid, title, created FROM " . "{node} WHERE created >= '%d' ORDER BY created DESC"; // get the links (no range limit here) $queryResult = db_query($query, $start_time); $page_content = '<ul>'; while ($links = db_fetch_object($queryResult)) { $page_content .= '<li>'.l($links->title, 'node/'.$links->nid).'</li>'; } // check to see if there was any content before // setting up the block if ($page_content == '<ul>') { // no content from a week ago, let the user know $page_content = "No events occurred on this site recent week in history."; } else $page_content .= '</ul>'; return $page_content; } ?>
เสร็จแล้ว
เพื่อความสุขสวัสดี อย่าลืมรัน update.php
ด้วย ไม่งั้นอาจมีปัญหาเรื่อง HTTP request failed
บันทึกแก้และแปลงโมดูล
เพิ่งสังเกตุว่าโมดูล Switch Theme เขาไม่ยอมเปลี่ยนธีมสำหรับผู้ใช้ทั่วไป (anonymous users)
ลองค้นดู bug ของ switchtheme พบ patch แก้ปัญหาไว้แล้ว แต่ยังไม่มีสำหรับ Drupal-6
เลยทดลองแก้ไขดู ได้ความแพตช์ดังนี้ครับ
$ patch -u -p switchtheme.module
--- switchtheme.module 2007-08-31 21:56:14.000000000 +0700 +++ switchtheme.module 2007-12-09 19:46:14.000000000 +0700 @@ -148,11 +148,17 @@ function switchtheme_switch_form_submit( // save the setting in the db for logged in users // save the setting in the session for all others if ($user->uid > 0) { - variable_set('theme_default', $form_values['custom_theme']); + // FIX DEFAULT THEME CHANGED + //variable_set('theme_default', $form_values['custom_theme']); if (user_save($user, array('theme' => $form_state['values']['custom_theme']))) { $user->theme = $form_state['values']['custom_theme']; } } + // FIX ANONYMOUS SWITCH THEME + elseif (user_access('switch theme')) { + // save the setting in the variable for all others + $_SESSION['custom_theme'] = $form_state['values']['custom_theme']; + } } @@ -184,4 +190,4 @@ function switchtheme_select() { } asort($select); return $select; -} \ No newline at end of file +}
ทดลองแปลงโมดูล On This Date ของ Drupal-5 Modules Tutorial
โมดูลในรุ่น 5 ควรเขียนในไดเรกทอรี่ ./sites/all/modules/
ดังนั้นต้องสร้างไดเรกทอรี่ให้โมดูลเราก่อน โดยใช้ชื่อ onthisdate เหมือนเดิม
$ cd /var/www/drupal
$ mkdir -p sites/all/modules/onthisdate
$ cd sites/all/modules/onthisdate
สำหรับรุ่น 5 ต้องมีไฟล์ info
$ vi onthisdate.info
; $Id$ name = On this date description = "A block module that lists links to content such as blog entries or forum discussions that were created one week ago. (wd's: Modify to recent 3 days post)"
คราวนี้ก็สร้างไฟล์โมดูลจริง ๆ ได้แล้ว
$ vi onthisdate.module
<?php /** * @file: onthisdate.module.5.txt Drupal tutorial on how to write modules * @author: Kitt Hodsden (http://kitt.hodsden.org/) * @license: GPL:v2, CC:by-nc-sa, contact for commercial uses */ /** * Display help and module information * @param section which section of the site we're displaying help * @return help text for section */ function onthisdate_help($section='') { $output = ''; switch ($section) { case "admin/help#onthisdate": $output = '<p>'. t("Displays links to nodes created on this date"). '</p>'; break; } return $output; } // function onthisdate_help /** * menu hook * @return array of menu items */ function onthisdate_menu() { $items = array(); //this was created earlier in tutorial 7. $items[] = array( 'path' => 'admin/settings/onthisdate', 'title' => t('On this date module settings'), 'callback' => 'drupal_get_form', 'callback arguments' => 'onthisdate_admin', 'access' => user_access('access administration pages'), 'type' => MENU_NORMAL_ITEM, ); //this is added for this current tutorial. $items[] = array( 'path' => 'onthisdate', 'title' => t('on this date'), 'callback' => 'onthisdate_all', 'access' => user_access('access content'), 'type' => MENU_CALLBACK ); return $items; } function onthisdate_admin() { $form['onthisdate_maxdisp'] = array( '#type' => 'textfield', '#title' => t('Maximum number of links'), '#default_value' => variable_get('onthisdate_maxdisp', 3), '#size' => 2, '#maxlength' => 2, '#description' => t("The maximum number of links to display in the block.") ); return system_settings_form($form); } /** * Valid permissions for this module * @return An array of valid permissions for the onthisdate module */ function onthisdate_perm() { return array('access onthisdate', 'administer onthisdate'); } // function onthisdate_perm() /** * Generate HTML for the onthisdate block * @param op the operation from the URL * @param delta offset * @returns block HTML */ function onthisdate_block($op='list', $delta=0) { // listing of blocks, such as on the admin/block page if ($op == "list") { $block[0]["info"] = t("On This Date"); return $block; } else if ($op == 'view') { // our block content // content variable that will be returned for display $block_content = ''; // Get today's date $today = getdate(); // calculate midnight one week ago //$start_time = mktime(0, 0, 0,$today['mon'], // ($today['mday'] - 7), $today['year']); //wd's: MODIFY TO LAST 3 DAYS $start_time = mktime(0, 0, 0,$today['mon'], ($today['mday'] - 3) , $today['year']); // we want items that occur only on the day in question, so //calculate 1 day //$end_time = $start_time + 86400; // 60 * 60 * 24 = 86400 seconds in a day //wd's EXPAND TO 3 DAYS $end_time = $start_time + 259200; $limitnum = variable_get("onthisdate_maxdisp", 3); $query = "SELECT nid, title, created FROM " . "{node} WHERE created >= %d " . "AND created <= %d ORDER BY created DESC"; // get the links $queryResult = db_query_range($query, $start_time, $end_time, 0, $limitnum); while ($links = db_fetch_object($queryResult)) { //wd's: MODIFY TO LIST //$block_content .= l($links->title, 'node/'.$links->nid) . '<br />'; $block_content .= '<li>'.l($links->title, 'node/'.$links->nid) . '</li>'; } // check to see if there was any content before setting up the block if ($block_content == '') { // no content from a week ago, return nothing. return; } else { //wd's: ADD <ul> $block_content = '<ul>'.$block_content.'</ul>'; } // add a more link to our page that displays all the links $block_content .= "<div class=\"more-link\">". l( t("more"), "onthisdate", array( "title" => t("More events on this day.")))."</div>"; // set up the block //$block['subject'] = 'On This Date'; //wd's EXPAND TO 3 DAYS $block['subject'] = 'These 3 Days'; $block['content'] = $block_content; return $block; } } // end onthisdate_block /** * Settings for the onthisdate module * @return form contents for this module */ function onthisdate_settings() { // only administrators can access this function if (!user_access('access administration pages')) { return message_access(); } // 4.7 forms API $form['onthisdate_maxdisp'] = array('#type' => 'textfield', '#title' => t('Maximum number of links'), '#default_value' => variable_get('onthisdate_maxdisp', 3), '#description' => t("The maximum number of links to display in the block."), '#maxlength' => '2', '#size' => '2'); return $form; } /** * Render a page listing links to all the content from a week ago */ function onthisdate_all() { // content variable that will be returned for display $page_content = ''; // Get today's date $today = getdate(); // calculate midnight one week ago //$start_time = mktime(0, 0, 0, $today['mon'], ($today['mday'] - 7), $today['year']); //wd's: CHANGE TO LAST 3 DAYS $start_time = mktime(0, 0, 0, $today['mon'], ($today['mday'] - 3), $today['year']); // we want items that occur only on the day in question, // so calculate 1 day //$end_time = $start_time + 86400; // 60 * 60 * 24 = 86400 seconds in a day //wd's EXPAND TO 3 DAYS $end_time = $start_time + 259200; $query = "SELECT nid, title, created FROM " . "{node} WHERE created >= '" . $start_time . "' AND created <= '". $end_time . " ORDER BY created DESC'"; // get the links (no range limit here) $queryResult = db_query($query); while ($links = db_fetch_object($queryResult)) { //$page_content .= l($links->title, 'node/'.$links->nid).'<br />'; //wd's: ADD LIST $page_content .= '<li>'.l($links->title, 'node/'.$links->nid).'</li>'; } // check to see if there was any content before // setting up the block if ($page_content == '') { // no content from a week ago, let the user know $page_content = "No events occurred on this site on this date in history."; } else { //wd's: ADD <ul> $page_content = '<ul>'.$page_content.'</ul>'; } print theme("page", $page_content); } ?>
เสร็จแล้ว
ต่อไปก็ทำตามขั้นตอนคือ
admin/build/module -> enable onthisdate
admin/user/access -> เปิดให้ทำงานตาม roles
admin/build/block -> enable onthisdate