From 81b1fd57527b945c1d416cdf36d9b46de8265d74 Mon Sep 17 00:00:00 2001 From: MinSoo Kim Date: Tue, 23 Feb 2016 00:56:05 +0900 Subject: [PATCH 001/205] support i18n jQuery-UI datepicker --- common/js/plugins/ui/i18n/datepicker-af.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-ar-DZ.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-ar.js | 38 ++++++++++ common/js/plugins/ui/i18n/datepicker-az.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-be.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-bg.js | 38 ++++++++++ common/js/plugins/ui/i18n/datepicker-bs.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-ca.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-cs.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-cy-GB.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-da.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-de.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-el.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-en-AU.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-en-GB.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-en-NZ.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-eo.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-es.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-et.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-eu.js | 36 +++++++++ common/js/plugins/ui/i18n/datepicker-fa.js | 73 +++++++++++++++++++ common/js/plugins/ui/i18n/datepicker-fi.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-fo.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-fr-CA.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-fr-CH.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-fr.js | 39 ++++++++++ common/js/plugins/ui/i18n/datepicker-gl.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-he.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-hi.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-hr.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-hu.js | 36 +++++++++ common/js/plugins/ui/i18n/datepicker-hy.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-id.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-is.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-it-CH.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-it.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-ja.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-ka.js | 35 +++++++++ common/js/plugins/ui/i18n/datepicker-kk.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-km.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-ko.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-ky.js | 38 ++++++++++ common/js/plugins/ui/i18n/datepicker-lb.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-lt.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-lv.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-mk.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-ml.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-ms.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-nb.js | 36 +++++++++ common/js/plugins/ui/i18n/datepicker-nl-BE.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-nl.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-nn.js | 36 +++++++++ common/js/plugins/ui/i18n/datepicker-no.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-pl.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-pt-BR.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-pt.js | 36 +++++++++ common/js/plugins/ui/i18n/datepicker-rm.js | 35 +++++++++ common/js/plugins/ui/i18n/datepicker-ro.js | 40 ++++++++++ common/js/plugins/ui/i18n/datepicker-ru.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-sk.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-sl.js | 38 ++++++++++ common/js/plugins/ui/i18n/datepicker-sq.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-sr-SR.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-sr.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-sv.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-ta.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-th.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-tj.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-tr.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-uk.js | 38 ++++++++++ common/js/plugins/ui/i18n/datepicker-vi.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-zh-CN.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-zh-HK.js | 37 ++++++++++ common/js/plugins/ui/i18n/datepicker-zh-TW.js | 37 ++++++++++ common/js/plugins/ui/plugin.load | 2 +- common/js/plugins/ui/rx_datepicker.js | 7 ++ 76 files changed, 2783 insertions(+), 1 deletion(-) create mode 100644 common/js/plugins/ui/i18n/datepicker-af.js create mode 100644 common/js/plugins/ui/i18n/datepicker-ar-DZ.js create mode 100644 common/js/plugins/ui/i18n/datepicker-ar.js create mode 100644 common/js/plugins/ui/i18n/datepicker-az.js create mode 100644 common/js/plugins/ui/i18n/datepicker-be.js create mode 100644 common/js/plugins/ui/i18n/datepicker-bg.js create mode 100644 common/js/plugins/ui/i18n/datepicker-bs.js create mode 100644 common/js/plugins/ui/i18n/datepicker-ca.js create mode 100644 common/js/plugins/ui/i18n/datepicker-cs.js create mode 100644 common/js/plugins/ui/i18n/datepicker-cy-GB.js create mode 100644 common/js/plugins/ui/i18n/datepicker-da.js create mode 100644 common/js/plugins/ui/i18n/datepicker-de.js create mode 100644 common/js/plugins/ui/i18n/datepicker-el.js create mode 100644 common/js/plugins/ui/i18n/datepicker-en-AU.js create mode 100644 common/js/plugins/ui/i18n/datepicker-en-GB.js create mode 100644 common/js/plugins/ui/i18n/datepicker-en-NZ.js create mode 100644 common/js/plugins/ui/i18n/datepicker-eo.js create mode 100644 common/js/plugins/ui/i18n/datepicker-es.js create mode 100644 common/js/plugins/ui/i18n/datepicker-et.js create mode 100644 common/js/plugins/ui/i18n/datepicker-eu.js create mode 100644 common/js/plugins/ui/i18n/datepicker-fa.js create mode 100644 common/js/plugins/ui/i18n/datepicker-fi.js create mode 100644 common/js/plugins/ui/i18n/datepicker-fo.js create mode 100644 common/js/plugins/ui/i18n/datepicker-fr-CA.js create mode 100644 common/js/plugins/ui/i18n/datepicker-fr-CH.js create mode 100644 common/js/plugins/ui/i18n/datepicker-fr.js create mode 100644 common/js/plugins/ui/i18n/datepicker-gl.js create mode 100644 common/js/plugins/ui/i18n/datepicker-he.js create mode 100644 common/js/plugins/ui/i18n/datepicker-hi.js create mode 100644 common/js/plugins/ui/i18n/datepicker-hr.js create mode 100644 common/js/plugins/ui/i18n/datepicker-hu.js create mode 100644 common/js/plugins/ui/i18n/datepicker-hy.js create mode 100644 common/js/plugins/ui/i18n/datepicker-id.js create mode 100644 common/js/plugins/ui/i18n/datepicker-is.js create mode 100644 common/js/plugins/ui/i18n/datepicker-it-CH.js create mode 100644 common/js/plugins/ui/i18n/datepicker-it.js create mode 100644 common/js/plugins/ui/i18n/datepicker-ja.js create mode 100644 common/js/plugins/ui/i18n/datepicker-ka.js create mode 100644 common/js/plugins/ui/i18n/datepicker-kk.js create mode 100644 common/js/plugins/ui/i18n/datepicker-km.js create mode 100644 common/js/plugins/ui/i18n/datepicker-ko.js create mode 100644 common/js/plugins/ui/i18n/datepicker-ky.js create mode 100644 common/js/plugins/ui/i18n/datepicker-lb.js create mode 100644 common/js/plugins/ui/i18n/datepicker-lt.js create mode 100644 common/js/plugins/ui/i18n/datepicker-lv.js create mode 100644 common/js/plugins/ui/i18n/datepicker-mk.js create mode 100644 common/js/plugins/ui/i18n/datepicker-ml.js create mode 100644 common/js/plugins/ui/i18n/datepicker-ms.js create mode 100644 common/js/plugins/ui/i18n/datepicker-nb.js create mode 100644 common/js/plugins/ui/i18n/datepicker-nl-BE.js create mode 100644 common/js/plugins/ui/i18n/datepicker-nl.js create mode 100644 common/js/plugins/ui/i18n/datepicker-nn.js create mode 100644 common/js/plugins/ui/i18n/datepicker-no.js create mode 100644 common/js/plugins/ui/i18n/datepicker-pl.js create mode 100644 common/js/plugins/ui/i18n/datepicker-pt-BR.js create mode 100644 common/js/plugins/ui/i18n/datepicker-pt.js create mode 100644 common/js/plugins/ui/i18n/datepicker-rm.js create mode 100644 common/js/plugins/ui/i18n/datepicker-ro.js create mode 100644 common/js/plugins/ui/i18n/datepicker-ru.js create mode 100644 common/js/plugins/ui/i18n/datepicker-sk.js create mode 100644 common/js/plugins/ui/i18n/datepicker-sl.js create mode 100644 common/js/plugins/ui/i18n/datepicker-sq.js create mode 100644 common/js/plugins/ui/i18n/datepicker-sr-SR.js create mode 100644 common/js/plugins/ui/i18n/datepicker-sr.js create mode 100644 common/js/plugins/ui/i18n/datepicker-sv.js create mode 100644 common/js/plugins/ui/i18n/datepicker-ta.js create mode 100644 common/js/plugins/ui/i18n/datepicker-th.js create mode 100644 common/js/plugins/ui/i18n/datepicker-tj.js create mode 100644 common/js/plugins/ui/i18n/datepicker-tr.js create mode 100644 common/js/plugins/ui/i18n/datepicker-uk.js create mode 100644 common/js/plugins/ui/i18n/datepicker-vi.js create mode 100644 common/js/plugins/ui/i18n/datepicker-zh-CN.js create mode 100644 common/js/plugins/ui/i18n/datepicker-zh-HK.js create mode 100644 common/js/plugins/ui/i18n/datepicker-zh-TW.js create mode 100644 common/js/plugins/ui/rx_datepicker.js diff --git a/common/js/plugins/ui/i18n/datepicker-af.js b/common/js/plugins/ui/i18n/datepicker-af.js new file mode 100644 index 000000000..c75688884 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-af.js @@ -0,0 +1,37 @@ +/* Afrikaans initialisation for the jQuery UI date picker plugin. */ +/* Written by Renier Pretorius. */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.af = { + closeText: "Selekteer", + prevText: "Vorige", + nextText: "Volgende", + currentText: "Vandag", + monthNames: [ "Januarie","Februarie","Maart","April","Mei","Junie", + "Julie","Augustus","September","Oktober","November","Desember" ], + monthNamesShort: [ "Jan", "Feb", "Mrt", "Apr", "Mei", "Jun", + "Jul", "Aug", "Sep", "Okt", "Nov", "Des" ], + dayNames: [ "Sondag", "Maandag", "Dinsdag", "Woensdag", "Donderdag", "Vrydag", "Saterdag" ], + dayNamesShort: [ "Son", "Maa", "Din", "Woe", "Don", "Vry", "Sat" ], + dayNamesMin: [ "So","Ma","Di","Wo","Do","Vr","Sa" ], + weekHeader: "Wk", + dateFormat: "dd/mm/yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.af ); + +return datepicker.regional.af; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-ar-DZ.js b/common/js/plugins/ui/i18n/datepicker-ar-DZ.js new file mode 100644 index 000000000..6035ce292 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-ar-DZ.js @@ -0,0 +1,37 @@ +/* Algerian Arabic Translation for jQuery UI date picker plugin. (can be used for Tunisia)*/ +/* Mohamed Cherif BOUCHELAGHEM -- cherifbouchelaghem@yahoo.fr */ + +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional[ "ar-DZ" ] = { + closeText: "إغلاق", + prevText: "<السابق", + nextText: "التالي>", + currentText: "اليوم", + monthNames: [ "جانفي", "فيفري", "مارس", "أفريل", "ماي", "جوان", + "جويلية", "أوت", "سبتمبر","أكتوبر", "نوفمبر", "ديسمبر" ], + monthNamesShort: [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" ], + dayNames: [ "الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت" ], + dayNamesShort: [ "الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت" ], + dayNamesMin: [ "الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت" ], + weekHeader: "أسبوع", + dateFormat: "dd/mm/yy", + firstDay: 6, + isRTL: true, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional[ "ar-DZ" ] ); + +return datepicker.regional[ "ar-DZ" ]; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-ar.js b/common/js/plugins/ui/i18n/datepicker-ar.js new file mode 100644 index 000000000..025d98484 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-ar.js @@ -0,0 +1,38 @@ +/* Arabic Translation for jQuery UI date picker plugin. */ +/* Used in most of Arab countries, primarily in Bahrain, Kuwait, Oman, Qatar, Saudi Arabia and the United Arab Emirates, Egypt, Sudan and Yemen. */ +/* Written by Mohammed Alshehri -- m@dralshehri.com */ + +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.ar = { + closeText: "إغلاق", + prevText: "<السابق", + nextText: "التالي>", + currentText: "اليوم", + monthNames: [ "يناير", "فبراير", "مارس", "أبريل", "مايو", "يونيو", + "يوليو", "أغسطس", "سبتمبر", "أكتوبر", "نوفمبر", "ديسمبر" ], + monthNamesShort: [ "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12" ], + dayNames: [ "الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت" ], + dayNamesShort: [ "أحد", "اثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت" ], + dayNamesMin: [ "ح", "ن", "ث", "ر", "خ", "ج", "س" ], + weekHeader: "أسبوع", + dateFormat: "dd/mm/yy", + firstDay: 0, + isRTL: true, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.ar ); + +return datepicker.regional.ar; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-az.js b/common/js/plugins/ui/i18n/datepicker-az.js new file mode 100644 index 000000000..2ebdcfa8b --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-az.js @@ -0,0 +1,37 @@ +/* Azerbaijani (UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* Written by Jamil Najafov (necefov33@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.az = { + closeText: "Bağla", + prevText: "<Geri", + nextText: "İrəli>", + currentText: "Bugün", + monthNames: [ "Yanvar","Fevral","Mart","Aprel","May","İyun", + "İyul","Avqust","Sentyabr","Oktyabr","Noyabr","Dekabr" ], + monthNamesShort: [ "Yan","Fev","Mar","Apr","May","İyun", + "İyul","Avq","Sen","Okt","Noy","Dek" ], + dayNames: [ "Bazar","Bazar ertəsi","Çərşənbə axşamı","Çərşənbə","Cümə axşamı","Cümə","Şənbə" ], + dayNamesShort: [ "B","Be","Ça","Ç","Ca","C","Ş" ], + dayNamesMin: [ "B","B","Ç","С","Ç","C","Ş" ], + weekHeader: "Hf", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.az ); + +return datepicker.regional.az; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-be.js b/common/js/plugins/ui/i18n/datepicker-be.js new file mode 100644 index 000000000..7d96dd1da --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-be.js @@ -0,0 +1,37 @@ +/* Belarusian initialisation for the jQuery UI date picker plugin. */ +/* Written by Pavel Selitskas */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.be = { + closeText: "Зачыніць", + prevText: "←Папяр.", + nextText: "Наст.→", + currentText: "Сёньня", + monthNames: [ "Студзень","Люты","Сакавік","Красавік","Травень","Чэрвень", + "Ліпень","Жнівень","Верасень","Кастрычнік","Лістапад","Сьнежань" ], + monthNamesShort: [ "Сту","Лют","Сак","Кра","Тра","Чэр", + "Ліп","Жні","Вер","Кас","Ліс","Сьн" ], + dayNames: [ "нядзеля","панядзелак","аўторак","серада","чацьвер","пятніца","субота" ], + dayNamesShort: [ "ндз","пнд","аўт","срд","чцв","птн","сбт" ], + dayNamesMin: [ "Нд","Пн","Аў","Ср","Чц","Пт","Сб" ], + weekHeader: "Тд", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.be ); + +return datepicker.regional.be; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-bg.js b/common/js/plugins/ui/i18n/datepicker-bg.js new file mode 100644 index 000000000..cb066a4c9 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-bg.js @@ -0,0 +1,38 @@ +/* Bulgarian initialisation for the jQuery UI date picker plugin. */ +/* Written by Stoyan Kyosev (http://svest.org). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.bg = { + closeText: "затвори", + prevText: "<назад", + nextText: "напред>", + nextBigText: ">>", + currentText: "днес", + monthNames: [ "Януари","Февруари","Март","Април","Май","Юни", + "Юли","Август","Септември","Октомври","Ноември","Декември" ], + monthNamesShort: [ "Яну","Фев","Мар","Апр","Май","Юни", + "Юли","Авг","Сеп","Окт","Нов","Дек" ], + dayNames: [ "Неделя","Понеделник","Вторник","Сряда","Четвъртък","Петък","Събота" ], + dayNamesShort: [ "Нед","Пон","Вто","Сря","Чет","Пет","Съб" ], + dayNamesMin: [ "Не","По","Вт","Ср","Че","Пе","Съ" ], + weekHeader: "Wk", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.bg ); + +return datepicker.regional.bg; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-bs.js b/common/js/plugins/ui/i18n/datepicker-bs.js new file mode 100644 index 000000000..b9f2e2869 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-bs.js @@ -0,0 +1,37 @@ +/* Bosnian i18n for the jQuery UI date picker plugin. */ +/* Written by Kenan Konjo. */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.bs = { + closeText: "Zatvori", + prevText: "<", + nextText: ">", + currentText: "Danas", + monthNames: [ "Januar","Februar","Mart","April","Maj","Juni", + "Juli","August","Septembar","Oktobar","Novembar","Decembar" ], + monthNamesShort: [ "Jan","Feb","Mar","Apr","Maj","Jun", + "Jul","Aug","Sep","Okt","Nov","Dec" ], + dayNames: [ "Nedelja","Ponedeljak","Utorak","Srijeda","Četvrtak","Petak","Subota" ], + dayNamesShort: [ "Ned","Pon","Uto","Sri","Čet","Pet","Sub" ], + dayNamesMin: [ "Ne","Po","Ut","Sr","Če","Pe","Su" ], + weekHeader: "Wk", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.bs ); + +return datepicker.regional.bs; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-ca.js b/common/js/plugins/ui/i18n/datepicker-ca.js new file mode 100644 index 000000000..9febd90ee --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-ca.js @@ -0,0 +1,37 @@ +/* Inicialització en català per a l'extensió 'UI date picker' per jQuery. */ +/* Writers: (joan.leon@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.ca = { + closeText: "Tanca", + prevText: "Anterior", + nextText: "Següent", + currentText: "Avui", + monthNames: [ "gener","febrer","març","abril","maig","juny", + "juliol","agost","setembre","octubre","novembre","desembre" ], + monthNamesShort: [ "gen","feb","març","abr","maig","juny", + "jul","ag","set","oct","nov","des" ], + dayNames: [ "diumenge","dilluns","dimarts","dimecres","dijous","divendres","dissabte" ], + dayNamesShort: [ "dg","dl","dt","dc","dj","dv","ds" ], + dayNamesMin: [ "dg","dl","dt","dc","dj","dv","ds" ], + weekHeader: "Set", + dateFormat: "dd/mm/yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.ca ); + +return datepicker.regional.ca; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-cs.js b/common/js/plugins/ui/i18n/datepicker-cs.js new file mode 100644 index 000000000..c2f79cf9e --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-cs.js @@ -0,0 +1,37 @@ +/* Czech initialisation for the jQuery UI date picker plugin. */ +/* Written by Tomas Muller (tomas@tomas-muller.net). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.cs = { + closeText: "Zavřít", + prevText: "<Dříve", + nextText: "Později>", + currentText: "Nyní", + monthNames: [ "leden","únor","březen","duben","květen","červen", + "červenec","srpen","září","říjen","listopad","prosinec" ], + monthNamesShort: [ "led","úno","bře","dub","kvě","čer", + "čvc","srp","zář","říj","lis","pro" ], + dayNames: [ "neděle", "pondělí", "úterý", "středa", "čtvrtek", "pátek", "sobota" ], + dayNamesShort: [ "ne", "po", "út", "st", "čt", "pá", "so" ], + dayNamesMin: [ "ne","po","út","st","čt","pá","so" ], + weekHeader: "Týd", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.cs ); + +return datepicker.regional.cs; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-cy-GB.js b/common/js/plugins/ui/i18n/datepicker-cy-GB.js new file mode 100644 index 000000000..26643b50c --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-cy-GB.js @@ -0,0 +1,37 @@ +/* Welsh/UK initialisation for the jQuery UI date picker plugin. */ +/* Written by William Griffiths. */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional[ "cy-GB" ] = { + closeText: "Done", + prevText: "Prev", + nextText: "Next", + currentText: "Today", + monthNames: [ "Ionawr","Chwefror","Mawrth","Ebrill","Mai","Mehefin", + "Gorffennaf","Awst","Medi","Hydref","Tachwedd","Rhagfyr" ], + monthNamesShort: [ "Ion", "Chw", "Maw", "Ebr", "Mai", "Meh", + "Gor", "Aws", "Med", "Hyd", "Tac", "Rha" ], + dayNames: [ "Dydd Sul", "Dydd Llun", "Dydd Mawrth", "Dydd Mercher", "Dydd Iau", "Dydd Gwener", "Dydd Sadwrn" ], + dayNamesShort: [ "Sul", "Llu", "Maw", "Mer", "Iau", "Gwe", "Sad" ], + dayNamesMin: [ "Su","Ll","Ma","Me","Ia","Gw","Sa" ], + weekHeader: "Wy", + dateFormat: "dd/mm/yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional[ "cy-GB" ] ); + +return datepicker.regional[ "cy-GB" ]; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-da.js b/common/js/plugins/ui/i18n/datepicker-da.js new file mode 100644 index 000000000..273f0e3ff --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-da.js @@ -0,0 +1,37 @@ +/* Danish initialisation for the jQuery UI date picker plugin. */ +/* Written by Jan Christensen ( deletestuff@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.da = { + closeText: "Luk", + prevText: "<Forrige", + nextText: "Næste>", + currentText: "Idag", + monthNames: [ "Januar","Februar","Marts","April","Maj","Juni", + "Juli","August","September","Oktober","November","December" ], + monthNamesShort: [ "Jan","Feb","Mar","Apr","Maj","Jun", + "Jul","Aug","Sep","Okt","Nov","Dec" ], + dayNames: [ "Søndag","Mandag","Tirsdag","Onsdag","Torsdag","Fredag","Lørdag" ], + dayNamesShort: [ "Søn","Man","Tir","Ons","Tor","Fre","Lør" ], + dayNamesMin: [ "Sø","Ma","Ti","On","To","Fr","Lø" ], + weekHeader: "Uge", + dateFormat: "dd-mm-yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.da ); + +return datepicker.regional.da; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-de.js b/common/js/plugins/ui/i18n/datepicker-de.js new file mode 100644 index 000000000..a67790844 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-de.js @@ -0,0 +1,37 @@ +/* German initialisation for the jQuery UI date picker plugin. */ +/* Written by Milian Wolff (mail@milianw.de). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.de = { + closeText: "Schließen", + prevText: "<Zurück", + nextText: "Vor>", + currentText: "Heute", + monthNames: [ "Januar","Februar","März","April","Mai","Juni", + "Juli","August","September","Oktober","November","Dezember" ], + monthNamesShort: [ "Jan","Feb","Mär","Apr","Mai","Jun", + "Jul","Aug","Sep","Okt","Nov","Dez" ], + dayNames: [ "Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag" ], + dayNamesShort: [ "So","Mo","Di","Mi","Do","Fr","Sa" ], + dayNamesMin: [ "So","Mo","Di","Mi","Do","Fr","Sa" ], + weekHeader: "KW", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.de ); + +return datepicker.regional.de; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-el.js b/common/js/plugins/ui/i18n/datepicker-el.js new file mode 100644 index 000000000..f08d6f27d --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-el.js @@ -0,0 +1,37 @@ +/* Greek (el) initialisation for the jQuery UI date picker plugin. */ +/* Written by Alex Cicovic (http://www.alexcicovic.com) */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.el = { + closeText: "Κλείσιμο", + prevText: "Προηγούμενος", + nextText: "Επόμενος", + currentText: "Σήμερα", + monthNames: [ "Ιανουάριος","Φεβρουάριος","Μάρτιος","Απρίλιος","Μάιος","Ιούνιος", + "Ιούλιος","Αύγουστος","Σεπτέμβριος","Οκτώβριος","Νοέμβριος","Δεκέμβριος" ], + monthNamesShort: [ "Ιαν","Φεβ","Μαρ","Απρ","Μαι","Ιουν", + "Ιουλ","Αυγ","Σεπ","Οκτ","Νοε","Δεκ" ], + dayNames: [ "Κυριακή","Δευτέρα","Τρίτη","Τετάρτη","Πέμπτη","Παρασκευή","Σάββατο" ], + dayNamesShort: [ "Κυρ","Δευ","Τρι","Τετ","Πεμ","Παρ","Σαβ" ], + dayNamesMin: [ "Κυ","Δε","Τρ","Τε","Πε","Πα","Σα" ], + weekHeader: "Εβδ", + dateFormat: "dd/mm/yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.el ); + +return datepicker.regional.el; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-en-AU.js b/common/js/plugins/ui/i18n/datepicker-en-AU.js new file mode 100644 index 000000000..f15277c37 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-en-AU.js @@ -0,0 +1,37 @@ +/* English/Australia initialisation for the jQuery UI date picker plugin. */ +/* Based on the en-GB initialisation. */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional[ "en-AU" ] = { + closeText: "Done", + prevText: "Prev", + nextText: "Next", + currentText: "Today", + monthNames: [ "January","February","March","April","May","June", + "July","August","September","October","November","December" ], + monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ], + dayNames: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], + dayNamesShort: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], + dayNamesMin: [ "Su","Mo","Tu","We","Th","Fr","Sa" ], + weekHeader: "Wk", + dateFormat: "dd/mm/yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional[ "en-AU" ] ); + +return datepicker.regional[ "en-AU" ]; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-en-GB.js b/common/js/plugins/ui/i18n/datepicker-en-GB.js new file mode 100644 index 000000000..c961c1865 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-en-GB.js @@ -0,0 +1,37 @@ +/* English/UK initialisation for the jQuery UI date picker plugin. */ +/* Written by Stuart. */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional[ "en-GB" ] = { + closeText: "Done", + prevText: "Prev", + nextText: "Next", + currentText: "Today", + monthNames: [ "January","February","March","April","May","June", + "July","August","September","October","November","December" ], + monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ], + dayNames: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], + dayNamesShort: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], + dayNamesMin: [ "Su","Mo","Tu","We","Th","Fr","Sa" ], + weekHeader: "Wk", + dateFormat: "dd/mm/yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional[ "en-GB" ] ); + +return datepicker.regional[ "en-GB" ]; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-en-NZ.js b/common/js/plugins/ui/i18n/datepicker-en-NZ.js new file mode 100644 index 000000000..704636225 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-en-NZ.js @@ -0,0 +1,37 @@ +/* English/New Zealand initialisation for the jQuery UI date picker plugin. */ +/* Based on the en-GB initialisation. */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional[ "en-NZ" ] = { + closeText: "Done", + prevText: "Prev", + nextText: "Next", + currentText: "Today", + monthNames: [ "January","February","March","April","May","June", + "July","August","September","October","November","December" ], + monthNamesShort: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ], + dayNames: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], + dayNamesShort: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], + dayNamesMin: [ "Su","Mo","Tu","We","Th","Fr","Sa" ], + weekHeader: "Wk", + dateFormat: "dd/mm/yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional[ "en-NZ" ] ); + +return datepicker.regional[ "en-NZ" ]; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-eo.js b/common/js/plugins/ui/i18n/datepicker-eo.js new file mode 100644 index 000000000..25f6162b5 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-eo.js @@ -0,0 +1,37 @@ +/* Esperanto initialisation for the jQuery UI date picker plugin. */ +/* Written by Olivier M. (olivierweb@ifrance.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.eo = { + closeText: "Fermi", + prevText: "<Anta", + nextText: "Sekv>", + currentText: "Nuna", + monthNames: [ "Januaro","Februaro","Marto","Aprilo","Majo","Junio", + "Julio","Aŭgusto","Septembro","Oktobro","Novembro","Decembro" ], + monthNamesShort: [ "Jan","Feb","Mar","Apr","Maj","Jun", + "Jul","Aŭg","Sep","Okt","Nov","Dec" ], + dayNames: [ "Dimanĉo","Lundo","Mardo","Merkredo","Ĵaŭdo","Vendredo","Sabato" ], + dayNamesShort: [ "Dim","Lun","Mar","Mer","Ĵaŭ","Ven","Sab" ], + dayNamesMin: [ "Di","Lu","Ma","Me","Ĵa","Ve","Sa" ], + weekHeader: "Sb", + dateFormat: "dd/mm/yy", + firstDay: 0, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.eo ); + +return datepicker.regional.eo; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-es.js b/common/js/plugins/ui/i18n/datepicker-es.js new file mode 100644 index 000000000..ea7116e0b --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-es.js @@ -0,0 +1,37 @@ +/* Inicialización en español para la extensión 'UI date picker' para jQuery. */ +/* Traducido por Vester (xvester@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.es = { + closeText: "Cerrar", + prevText: "<Ant", + nextText: "Sig>", + currentText: "Hoy", + monthNames: [ "enero","febrero","marzo","abril","mayo","junio", + "julio","agosto","septiembre","octubre","noviembre","diciembre" ], + monthNamesShort: [ "ene","feb","mar","abr","may","jun", + "jul","ago","sep","oct","nov","dic" ], + dayNames: [ "domingo","lunes","martes","miércoles","jueves","viernes","sábado" ], + dayNamesShort: [ "dom","lun","mar","mié","jue","vie","sáb" ], + dayNamesMin: [ "D","L","M","X","J","V","S" ], + weekHeader: "Sm", + dateFormat: "dd/mm/yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.es ); + +return datepicker.regional.es; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-et.js b/common/js/plugins/ui/i18n/datepicker-et.js new file mode 100644 index 000000000..691675a98 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-et.js @@ -0,0 +1,37 @@ +/* Estonian initialisation for the jQuery UI date picker plugin. */ +/* Written by Mart Sõmermaa (mrts.pydev at gmail com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.et = { + closeText: "Sulge", + prevText: "Eelnev", + nextText: "Järgnev", + currentText: "Täna", + monthNames: [ "Jaanuar","Veebruar","Märts","Aprill","Mai","Juuni", + "Juuli","August","September","Oktoober","November","Detsember" ], + monthNamesShort: [ "Jaan", "Veebr", "Märts", "Apr", "Mai", "Juuni", + "Juuli", "Aug", "Sept", "Okt", "Nov", "Dets" ], + dayNames: [ "Pühapäev", "Esmaspäev", "Teisipäev", "Kolmapäev", "Neljapäev", "Reede", "Laupäev" ], + dayNamesShort: [ "Pühap", "Esmasp", "Teisip", "Kolmap", "Neljap", "Reede", "Laup" ], + dayNamesMin: [ "P","E","T","K","N","R","L" ], + weekHeader: "näd", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.et ); + +return datepicker.regional.et; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-eu.js b/common/js/plugins/ui/i18n/datepicker-eu.js new file mode 100644 index 000000000..8ea1ef9e5 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-eu.js @@ -0,0 +1,36 @@ +/* Karrikas-ek itzulia (karrikas@karrikas.com) */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.eu = { + closeText: "Egina", + prevText: "<Aur", + nextText: "Hur>", + currentText: "Gaur", + monthNames: [ "urtarrila","otsaila","martxoa","apirila","maiatza","ekaina", + "uztaila","abuztua","iraila","urria","azaroa","abendua" ], + monthNamesShort: [ "urt.","ots.","mar.","api.","mai.","eka.", + "uzt.","abu.","ira.","urr.","aza.","abe." ], + dayNames: [ "igandea","astelehena","asteartea","asteazkena","osteguna","ostirala","larunbata" ], + dayNamesShort: [ "ig.","al.","ar.","az.","og.","ol.","lr." ], + dayNamesMin: [ "ig","al","ar","az","og","ol","lr" ], + weekHeader: "As", + dateFormat: "yy-mm-dd", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.eu ); + +return datepicker.regional.eu; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-fa.js b/common/js/plugins/ui/i18n/datepicker-fa.js new file mode 100644 index 000000000..71da4981d --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-fa.js @@ -0,0 +1,73 @@ +/* Persian (Farsi) Translation for the jQuery UI date picker plugin. */ +/* Javad Mowlanezhad -- jmowla@gmail.com */ +/* Jalali calendar should supported soon! (Its implemented but I have to test it) */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.fa = { + closeText: "بستن", + prevText: "<قبلی", + nextText: "بعدی>", + currentText: "امروز", + monthNames: [ + "ژانویه", + "فوریه", + "مارس", + "آوریل", + "مه", + "ژوئن", + "ژوئیه", + "اوت", + "سپتامبر", + "اکتبر", + "نوامبر", + "دسامبر" + ], + monthNamesShort: [ "1","2","3","4","5","6","7","8","9","10","11","12" ], + dayNames: [ + "يکشنبه", + "دوشنبه", + "سه‌شنبه", + "چهارشنبه", + "پنجشنبه", + "جمعه", + "شنبه" + ], + dayNamesShort: [ + "ی", + "د", + "س", + "چ", + "پ", + "ج", + "ش" + ], + dayNamesMin: [ + "ی", + "د", + "س", + "چ", + "پ", + "ج", + "ش" + ], + weekHeader: "هف", + dateFormat: "yy/mm/dd", + firstDay: 6, + isRTL: true, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.fa ); + +return datepicker.regional.fa; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-fi.js b/common/js/plugins/ui/i18n/datepicker-fi.js new file mode 100644 index 000000000..a8386ff62 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-fi.js @@ -0,0 +1,37 @@ +/* Finnish initialisation for the jQuery UI date picker plugin. */ +/* Written by Harri Kilpiö (harrikilpio@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.fi = { + closeText: "Sulje", + prevText: "«Edellinen", + nextText: "Seuraava»", + currentText: "Tänään", + monthNames: [ "Tammikuu","Helmikuu","Maaliskuu","Huhtikuu","Toukokuu","Kesäkuu", + "Heinäkuu","Elokuu","Syyskuu","Lokakuu","Marraskuu","Joulukuu" ], + monthNamesShort: [ "Tammi","Helmi","Maalis","Huhti","Touko","Kesä", + "Heinä","Elo","Syys","Loka","Marras","Joulu" ], + dayNamesShort: [ "Su","Ma","Ti","Ke","To","Pe","La" ], + dayNames: [ "Sunnuntai","Maanantai","Tiistai","Keskiviikko","Torstai","Perjantai","Lauantai" ], + dayNamesMin: [ "Su","Ma","Ti","Ke","To","Pe","La" ], + weekHeader: "Vk", + dateFormat: "d.m.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.fi ); + +return datepicker.regional.fi; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-fo.js b/common/js/plugins/ui/i18n/datepicker-fo.js new file mode 100644 index 000000000..df1e08951 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-fo.js @@ -0,0 +1,37 @@ +/* Faroese initialisation for the jQuery UI date picker plugin */ +/* Written by Sverri Mohr Olsen, sverrimo@gmail.com */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.fo = { + closeText: "Lat aftur", + prevText: "<Fyrra", + nextText: "Næsta>", + currentText: "Í dag", + monthNames: [ "Januar","Februar","Mars","Apríl","Mei","Juni", + "Juli","August","September","Oktober","November","Desember" ], + monthNamesShort: [ "Jan","Feb","Mar","Apr","Mei","Jun", + "Jul","Aug","Sep","Okt","Nov","Des" ], + dayNames: [ "Sunnudagur","Mánadagur","Týsdagur","Mikudagur","Hósdagur","Fríggjadagur","Leyardagur" ], + dayNamesShort: [ "Sun","Mán","Týs","Mik","Hós","Frí","Ley" ], + dayNamesMin: [ "Su","Má","Tý","Mi","Hó","Fr","Le" ], + weekHeader: "Vk", + dateFormat: "dd-mm-yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.fo ); + +return datepicker.regional.fo; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-fr-CA.js b/common/js/plugins/ui/i18n/datepicker-fr-CA.js new file mode 100644 index 000000000..b590277d4 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-fr-CA.js @@ -0,0 +1,37 @@ +/* Canadian-French initialisation for the jQuery UI date picker plugin. */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional[ "fr-CA" ] = { + closeText: "Fermer", + prevText: "Précédent", + nextText: "Suivant", + currentText: "Aujourd'hui", + monthNames: [ "janvier", "février", "mars", "avril", "mai", "juin", + "juillet", "août", "septembre", "octobre", "novembre", "décembre" ], + monthNamesShort: [ "janv.", "févr.", "mars", "avril", "mai", "juin", + "juil.", "août", "sept.", "oct.", "nov.", "déc." ], + dayNames: [ "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi" ], + dayNamesShort: [ "dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam." ], + dayNamesMin: [ "D", "L", "M", "M", "J", "V", "S" ], + weekHeader: "Sem.", + dateFormat: "yy-mm-dd", + firstDay: 0, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" +}; +datepicker.setDefaults( datepicker.regional[ "fr-CA" ] ); + +return datepicker.regional[ "fr-CA" ]; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-fr-CH.js b/common/js/plugins/ui/i18n/datepicker-fr-CH.js new file mode 100644 index 000000000..d2f0584d6 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-fr-CH.js @@ -0,0 +1,37 @@ +/* Swiss-French initialisation for the jQuery UI date picker plugin. */ +/* Written Martin Voelkle (martin.voelkle@e-tc.ch). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional[ "fr-CH" ] = { + closeText: "Fermer", + prevText: "<Préc", + nextText: "Suiv>", + currentText: "Courant", + monthNames: [ "janvier", "février", "mars", "avril", "mai", "juin", + "juillet", "août", "septembre", "octobre", "novembre", "décembre" ], + monthNamesShort: [ "janv.", "févr.", "mars", "avril", "mai", "juin", + "juil.", "août", "sept.", "oct.", "nov.", "déc." ], + dayNames: [ "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi" ], + dayNamesShort: [ "dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam." ], + dayNamesMin: [ "D", "L", "M", "M", "J", "V", "S" ], + weekHeader: "Sm", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional[ "fr-CH" ] ); + +return datepicker.regional[ "fr-CH" ]; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-fr.js b/common/js/plugins/ui/i18n/datepicker-fr.js new file mode 100644 index 000000000..9e39fbd68 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-fr.js @@ -0,0 +1,39 @@ +/* French initialisation for the jQuery UI date picker plugin. */ +/* Written by Keith Wood (kbwood{at}iinet.com.au), + Stéphane Nahmani (sholby@sholby.net), + Stéphane Raimbault */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.fr = { + closeText: "Fermer", + prevText: "Précédent", + nextText: "Suivant", + currentText: "Aujourd'hui", + monthNames: [ "janvier", "février", "mars", "avril", "mai", "juin", + "juillet", "août", "septembre", "octobre", "novembre", "décembre" ], + monthNamesShort: [ "janv.", "févr.", "mars", "avr.", "mai", "juin", + "juil.", "août", "sept.", "oct.", "nov.", "déc." ], + dayNames: [ "dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi" ], + dayNamesShort: [ "dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam." ], + dayNamesMin: [ "D","L","M","M","J","V","S" ], + weekHeader: "Sem.", + dateFormat: "dd/mm/yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.fr ); + +return datepicker.regional.fr; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-gl.js b/common/js/plugins/ui/i18n/datepicker-gl.js new file mode 100644 index 000000000..276523074 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-gl.js @@ -0,0 +1,37 @@ +/* Galician localization for 'UI date picker' jQuery extension. */ +/* Translated by Jorge Barreiro . */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.gl = { + closeText: "Pechar", + prevText: "<Ant", + nextText: "Seg>", + currentText: "Hoxe", + monthNames: [ "Xaneiro","Febreiro","Marzo","Abril","Maio","Xuño", + "Xullo","Agosto","Setembro","Outubro","Novembro","Decembro" ], + monthNamesShort: [ "Xan","Feb","Mar","Abr","Mai","Xuñ", + "Xul","Ago","Set","Out","Nov","Dec" ], + dayNames: [ "Domingo","Luns","Martes","Mércores","Xoves","Venres","Sábado" ], + dayNamesShort: [ "Dom","Lun","Mar","Mér","Xov","Ven","Sáb" ], + dayNamesMin: [ "Do","Lu","Ma","Mé","Xo","Ve","Sá" ], + weekHeader: "Sm", + dateFormat: "dd/mm/yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.gl ); + +return datepicker.regional.gl; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-he.js b/common/js/plugins/ui/i18n/datepicker-he.js new file mode 100644 index 000000000..fb6238fda --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-he.js @@ -0,0 +1,37 @@ +/* Hebrew initialisation for the UI Datepicker extension. */ +/* Written by Amir Hardon (ahardon at gmail dot com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.he = { + closeText: "סגור", + prevText: "<הקודם", + nextText: "הבא>", + currentText: "היום", + monthNames: [ "ינואר","פברואר","מרץ","אפריל","מאי","יוני", + "יולי","אוגוסט","ספטמבר","אוקטובר","נובמבר","דצמבר" ], + monthNamesShort: [ "ינו","פבר","מרץ","אפר","מאי","יוני", + "יולי","אוג","ספט","אוק","נוב","דצמ" ], + dayNames: [ "ראשון","שני","שלישי","רביעי","חמישי","שישי","שבת" ], + dayNamesShort: [ "א'","ב'","ג'","ד'","ה'","ו'","שבת" ], + dayNamesMin: [ "א'","ב'","ג'","ד'","ה'","ו'","שבת" ], + weekHeader: "Wk", + dateFormat: "dd/mm/yy", + firstDay: 0, + isRTL: true, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.he ); + +return datepicker.regional.he; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-hi.js b/common/js/plugins/ui/i18n/datepicker-hi.js new file mode 100644 index 000000000..3b1209724 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-hi.js @@ -0,0 +1,37 @@ +/* Hindi initialisation for the jQuery UI date picker plugin. */ +/* Written by Michael Dawart. */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.hi = { + closeText: "बंद", + prevText: "पिछला", + nextText: "अगला", + currentText: "आज", + monthNames: [ "जनवरी ","फरवरी","मार्च","अप्रेल","मई","जून", + "जूलाई","अगस्त ","सितम्बर","अक्टूबर","नवम्बर","दिसम्बर" ], + monthNamesShort: [ "जन", "फर", "मार्च", "अप्रेल", "मई", "जून", + "जूलाई", "अग", "सित", "अक्ट", "नव", "दि" ], + dayNames: [ "रविवार", "सोमवार", "मंगलवार", "बुधवार", "गुरुवार", "शुक्रवार", "शनिवार" ], + dayNamesShort: [ "रवि", "सोम", "मंगल", "बुध", "गुरु", "शुक्र", "शनि" ], + dayNamesMin: [ "रवि", "सोम", "मंगल", "बुध", "गुरु", "शुक्र", "शनि" ], + weekHeader: "हफ्ता", + dateFormat: "dd/mm/yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.hi ); + +return datepicker.regional.hi; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-hr.js b/common/js/plugins/ui/i18n/datepicker-hr.js new file mode 100644 index 000000000..5e218c12d --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-hr.js @@ -0,0 +1,37 @@ +/* Croatian i18n for the jQuery UI date picker plugin. */ +/* Written by Vjekoslav Nesek. */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.hr = { + closeText: "Zatvori", + prevText: "<", + nextText: ">", + currentText: "Danas", + monthNames: [ "Siječanj","Veljača","Ožujak","Travanj","Svibanj","Lipanj", + "Srpanj","Kolovoz","Rujan","Listopad","Studeni","Prosinac" ], + monthNamesShort: [ "Sij","Velj","Ožu","Tra","Svi","Lip", + "Srp","Kol","Ruj","Lis","Stu","Pro" ], + dayNames: [ "Nedjelja","Ponedjeljak","Utorak","Srijeda","Četvrtak","Petak","Subota" ], + dayNamesShort: [ "Ned","Pon","Uto","Sri","Čet","Pet","Sub" ], + dayNamesMin: [ "Ne","Po","Ut","Sr","Če","Pe","Su" ], + weekHeader: "Tje", + dateFormat: "dd.mm.yy.", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.hr ); + +return datepicker.regional.hr; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-hu.js b/common/js/plugins/ui/i18n/datepicker-hu.js new file mode 100644 index 000000000..22bbe7031 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-hu.js @@ -0,0 +1,36 @@ +/* Hungarian initialisation for the jQuery UI date picker plugin. */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.hu = { + closeText: "bezár", + prevText: "vissza", + nextText: "előre", + currentText: "ma", + monthNames: [ "Január", "Február", "Március", "Április", "Május", "Június", + "Július", "Augusztus", "Szeptember", "Október", "November", "December" ], + monthNamesShort: [ "Jan", "Feb", "Már", "Ápr", "Máj", "Jún", + "Júl", "Aug", "Szep", "Okt", "Nov", "Dec" ], + dayNames: [ "Vasárnap", "Hétfő", "Kedd", "Szerda", "Csütörtök", "Péntek", "Szombat" ], + dayNamesShort: [ "Vas", "Hét", "Ked", "Sze", "Csü", "Pén", "Szo" ], + dayNamesMin: [ "V", "H", "K", "Sze", "Cs", "P", "Szo" ], + weekHeader: "Hét", + dateFormat: "yy.mm.dd.", + firstDay: 1, + isRTL: false, + showMonthAfterYear: true, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.hu ); + +return datepicker.regional.hu; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-hy.js b/common/js/plugins/ui/i18n/datepicker-hy.js new file mode 100644 index 000000000..95638b310 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-hy.js @@ -0,0 +1,37 @@ +/* Armenian(UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* Written by Levon Zakaryan (levon.zakaryan@gmail.com)*/ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.hy = { + closeText: "Փակել", + prevText: "<Նախ.", + nextText: "Հաջ.>", + currentText: "Այսօր", + monthNames: [ "Հունվար","Փետրվար","Մարտ","Ապրիլ","Մայիս","Հունիս", + "Հուլիս","Օգոստոս","Սեպտեմբեր","Հոկտեմբեր","Նոյեմբեր","Դեկտեմբեր" ], + monthNamesShort: [ "Հունվ","Փետր","Մարտ","Ապր","Մայիս","Հունիս", + "Հուլ","Օգս","Սեպ","Հոկ","Նոյ","Դեկ" ], + dayNames: [ "կիրակի","եկուշաբթի","երեքշաբթի","չորեքշաբթի","հինգշաբթի","ուրբաթ","շաբաթ" ], + dayNamesShort: [ "կիր","երկ","երք","չրք","հնգ","ուրբ","շբթ" ], + dayNamesMin: [ "կիր","երկ","երք","չրք","հնգ","ուրբ","շբթ" ], + weekHeader: "ՇԲՏ", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.hy ); + +return datepicker.regional.hy; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-id.js b/common/js/plugins/ui/i18n/datepicker-id.js new file mode 100644 index 000000000..5aef348af --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-id.js @@ -0,0 +1,37 @@ +/* Indonesian initialisation for the jQuery UI date picker plugin. */ +/* Written by Deden Fathurahman (dedenf@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.id = { + closeText: "Tutup", + prevText: "<mundur", + nextText: "maju>", + currentText: "hari ini", + monthNames: [ "Januari","Februari","Maret","April","Mei","Juni", + "Juli","Agustus","September","Oktober","Nopember","Desember" ], + monthNamesShort: [ "Jan","Feb","Mar","Apr","Mei","Jun", + "Jul","Agus","Sep","Okt","Nop","Des" ], + dayNames: [ "Minggu","Senin","Selasa","Rabu","Kamis","Jumat","Sabtu" ], + dayNamesShort: [ "Min","Sen","Sel","Rab","kam","Jum","Sab" ], + dayNamesMin: [ "Mg","Sn","Sl","Rb","Km","jm","Sb" ], + weekHeader: "Mg", + dateFormat: "dd/mm/yy", + firstDay: 0, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.id ); + +return datepicker.regional.id; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-is.js b/common/js/plugins/ui/i18n/datepicker-is.js new file mode 100644 index 000000000..f977de326 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-is.js @@ -0,0 +1,37 @@ +/* Icelandic initialisation for the jQuery UI date picker plugin. */ +/* Written by Haukur H. Thorsson (haukur@eskill.is). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.is = { + closeText: "Loka", + prevText: "< Fyrri", + nextText: "Næsti >", + currentText: "Í dag", + monthNames: [ "Janúar","Febrúar","Mars","Apríl","Maí","Júní", + "Júlí","Ágúst","September","Október","Nóvember","Desember" ], + monthNamesShort: [ "Jan","Feb","Mar","Apr","Maí","Jún", + "Júl","Ágú","Sep","Okt","Nóv","Des" ], + dayNames: [ "Sunnudagur","Mánudagur","Þriðjudagur","Miðvikudagur","Fimmtudagur","Föstudagur","Laugardagur" ], + dayNamesShort: [ "Sun","Mán","Þri","Mið","Fim","Fös","Lau" ], + dayNamesMin: [ "Su","Má","Þr","Mi","Fi","Fö","La" ], + weekHeader: "Vika", + dateFormat: "dd.mm.yy", + firstDay: 0, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.is ); + +return datepicker.regional.is; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-it-CH.js b/common/js/plugins/ui/i18n/datepicker-it-CH.js new file mode 100644 index 000000000..9895da4cc --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-it-CH.js @@ -0,0 +1,37 @@ +/* Italian initialisation for the jQuery UI date picker plugin. */ +/* Written by Antonello Pasella (antonello.pasella@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional[ "it-CH" ] = { + closeText: "Chiudi", + prevText: "<Prec", + nextText: "Succ>", + currentText: "Oggi", + monthNames: [ "Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno", + "Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre" ], + monthNamesShort: [ "Gen","Feb","Mar","Apr","Mag","Giu", + "Lug","Ago","Set","Ott","Nov","Dic" ], + dayNames: [ "Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato" ], + dayNamesShort: [ "Dom","Lun","Mar","Mer","Gio","Ven","Sab" ], + dayNamesMin: [ "Do","Lu","Ma","Me","Gi","Ve","Sa" ], + weekHeader: "Sm", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional[ "it-CH" ] ); + +return datepicker.regional[ "it-CH" ]; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-it.js b/common/js/plugins/ui/i18n/datepicker-it.js new file mode 100644 index 000000000..d67cb6c24 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-it.js @@ -0,0 +1,37 @@ +/* Italian initialisation for the jQuery UI date picker plugin. */ +/* Written by Antonello Pasella (antonello.pasella@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.it = { + closeText: "Chiudi", + prevText: "<Prec", + nextText: "Succ>", + currentText: "Oggi", + monthNames: [ "Gennaio","Febbraio","Marzo","Aprile","Maggio","Giugno", + "Luglio","Agosto","Settembre","Ottobre","Novembre","Dicembre" ], + monthNamesShort: [ "Gen","Feb","Mar","Apr","Mag","Giu", + "Lug","Ago","Set","Ott","Nov","Dic" ], + dayNames: [ "Domenica","Lunedì","Martedì","Mercoledì","Giovedì","Venerdì","Sabato" ], + dayNamesShort: [ "Dom","Lun","Mar","Mer","Gio","Ven","Sab" ], + dayNamesMin: [ "Do","Lu","Ma","Me","Gi","Ve","Sa" ], + weekHeader: "Sm", + dateFormat: "dd/mm/yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.it ); + +return datepicker.regional.it; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-ja.js b/common/js/plugins/ui/i18n/datepicker-ja.js new file mode 100644 index 000000000..52b10583c --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-ja.js @@ -0,0 +1,37 @@ +/* Japanese initialisation for the jQuery UI date picker plugin. */ +/* Written by Kentaro SATO (kentaro@ranvis.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.ja = { + closeText: "閉じる", + prevText: "<前", + nextText: "次>", + currentText: "今日", + monthNames: [ "1月","2月","3月","4月","5月","6月", + "7月","8月","9月","10月","11月","12月" ], + monthNamesShort: [ "1月","2月","3月","4月","5月","6月", + "7月","8月","9月","10月","11月","12月" ], + dayNames: [ "日曜日","月曜日","火曜日","水曜日","木曜日","金曜日","土曜日" ], + dayNamesShort: [ "日","月","火","水","木","金","土" ], + dayNamesMin: [ "日","月","火","水","木","金","土" ], + weekHeader: "週", + dateFormat: "yy/mm/dd", + firstDay: 0, + isRTL: false, + showMonthAfterYear: true, + yearSuffix: "年" }; +datepicker.setDefaults( datepicker.regional.ja ); + +return datepicker.regional.ja; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-ka.js b/common/js/plugins/ui/i18n/datepicker-ka.js new file mode 100644 index 000000000..6a4aa392f --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-ka.js @@ -0,0 +1,35 @@ +/* Georgian (UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* Written by Lado Lomidze (lado.lomidze@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.ka = { + closeText: "დახურვა", + prevText: "< წინა", + nextText: "შემდეგი >", + currentText: "დღეს", + monthNames: [ "იანვარი","თებერვალი","მარტი","აპრილი","მაისი","ივნისი", "ივლისი","აგვისტო","სექტემბერი","ოქტომბერი","ნოემბერი","დეკემბერი" ], + monthNamesShort: [ "იან","თებ","მარ","აპრ","მაი","ივნ", "ივლ","აგვ","სექ","ოქტ","ნოე","დეკ" ], + dayNames: [ "კვირა","ორშაბათი","სამშაბათი","ოთხშაბათი","ხუთშაბათი","პარასკევი","შაბათი" ], + dayNamesShort: [ "კვ","ორშ","სამ","ოთხ","ხუთ","პარ","შაბ" ], + dayNamesMin: [ "კვ","ორშ","სამ","ოთხ","ხუთ","პარ","შაბ" ], + weekHeader: "კვირა", + dateFormat: "dd-mm-yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.ka ); + +return datepicker.regional.ka; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-kk.js b/common/js/plugins/ui/i18n/datepicker-kk.js new file mode 100644 index 000000000..fa0121f8c --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-kk.js @@ -0,0 +1,37 @@ +/* Kazakh (UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* Written by Dmitriy Karasyov (dmitriy.karasyov@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.kk = { + closeText: "Жабу", + prevText: "<Алдыңғы", + nextText: "Келесі>", + currentText: "Бүгін", + monthNames: [ "Қаңтар","Ақпан","Наурыз","Сәуір","Мамыр","Маусым", + "Шілде","Тамыз","Қыркүйек","Қазан","Қараша","Желтоқсан" ], + monthNamesShort: [ "Қаң","Ақп","Нау","Сәу","Мам","Мау", + "Шіл","Там","Қыр","Қаз","Қар","Жел" ], + dayNames: [ "Жексенбі","Дүйсенбі","Сейсенбі","Сәрсенбі","Бейсенбі","Жұма","Сенбі" ], + dayNamesShort: [ "жкс","дсн","ссн","срс","бсн","жма","снб" ], + dayNamesMin: [ "Жк","Дс","Сс","Ср","Бс","Жм","Сн" ], + weekHeader: "Не", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.kk ); + +return datepicker.regional.kk; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-km.js b/common/js/plugins/ui/i18n/datepicker-km.js new file mode 100644 index 000000000..d8a4596bc --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-km.js @@ -0,0 +1,37 @@ +/* Khmer initialisation for the jQuery calendar extension. */ +/* Written by Chandara Om (chandara.teacher@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.km = { + closeText: "ធ្វើ​រួច", + prevText: "មុន", + nextText: "បន្ទាប់", + currentText: "ថ្ងៃ​នេះ", + monthNames: [ "មករា","កុម្ភៈ","មីនា","មេសា","ឧសភា","មិថុនា", + "កក្កដា","សីហា","កញ្ញា","តុលា","វិច្ឆិកា","ធ្នូ" ], + monthNamesShort: [ "មករា","កុម្ភៈ","មីនា","មេសា","ឧសភា","មិថុនា", + "កក្កដា","សីហា","កញ្ញា","តុលា","វិច្ឆិកា","ធ្នូ" ], + dayNames: [ "អាទិត្យ", "ចន្ទ", "អង្គារ", "ពុធ", "ព្រហស្បតិ៍", "សុក្រ", "សៅរ៍" ], + dayNamesShort: [ "អា", "ច", "អ", "ពុ", "ព្រហ", "សុ", "សៅ" ], + dayNamesMin: [ "អា", "ច", "អ", "ពុ", "ព្រហ", "សុ", "សៅ" ], + weekHeader: "សប្ដាហ៍", + dateFormat: "dd-mm-yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.km ); + +return datepicker.regional.km; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-ko.js b/common/js/plugins/ui/i18n/datepicker-ko.js new file mode 100644 index 000000000..8879a9950 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-ko.js @@ -0,0 +1,37 @@ +/* Korean initialisation for the jQuery calendar extension. */ +/* Written by DaeKwon Kang (ncrash.dk@gmail.com), Edited by Genie and Myeongjin Lee. */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.ko = { + closeText: "닫기", + prevText: "이전달", + nextText: "다음달", + currentText: "오늘", + monthNames: [ "1월","2월","3월","4월","5월","6월", + "7월","8월","9월","10월","11월","12월" ], + monthNamesShort: [ "1월","2월","3월","4월","5월","6월", + "7월","8월","9월","10월","11월","12월" ], + dayNames: [ "일요일","월요일","화요일","수요일","목요일","금요일","토요일" ], + dayNamesShort: [ "일","월","화","수","목","금","토" ], + dayNamesMin: [ "일","월","화","수","목","금","토" ], + weekHeader: "주", + dateFormat: "yy. m. d.", + firstDay: 0, + isRTL: false, + showMonthAfterYear: true, + yearSuffix: "년" }; +datepicker.setDefaults( datepicker.regional.ko ); + +return datepicker.regional.ko; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-ky.js b/common/js/plugins/ui/i18n/datepicker-ky.js new file mode 100644 index 000000000..f748bc606 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-ky.js @@ -0,0 +1,38 @@ +/* Kyrgyz (UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* Written by Sergey Kartashov (ebishkek@yandex.ru). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.ky = { + closeText: "Жабуу", + prevText: "<Мур", + nextText: "Кий>", + currentText: "Бүгүн", + monthNames: [ "Январь","Февраль","Март","Апрель","Май","Июнь", + "Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь" ], + monthNamesShort: [ "Янв","Фев","Мар","Апр","Май","Июн", + "Июл","Авг","Сен","Окт","Ноя","Дек" ], + dayNames: [ "жекшемби", "дүйшөмбү", "шейшемби", "шаршемби", "бейшемби", "жума", "ишемби" ], + dayNamesShort: [ "жек", "дүй", "шей", "шар", "бей", "жум", "ише" ], + dayNamesMin: [ "Жк","Дш","Шш","Шр","Бш","Жм","Иш" ], + weekHeader: "Жум", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" +}; +datepicker.setDefaults( datepicker.regional.ky ); + +return datepicker.regional.ky; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-lb.js b/common/js/plugins/ui/i18n/datepicker-lb.js new file mode 100644 index 000000000..a8fa03b8f --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-lb.js @@ -0,0 +1,37 @@ +/* Luxembourgish initialisation for the jQuery UI date picker plugin. */ +/* Written by Michel Weimerskirch */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.lb = { + closeText: "Fäerdeg", + prevText: "Zréck", + nextText: "Weider", + currentText: "Haut", + monthNames: [ "Januar","Februar","Mäerz","Abrëll","Mee","Juni", + "Juli","August","September","Oktober","November","Dezember" ], + monthNamesShort: [ "Jan", "Feb", "Mäe", "Abr", "Mee", "Jun", + "Jul", "Aug", "Sep", "Okt", "Nov", "Dez" ], + dayNames: [ "Sonndeg", "Méindeg", "Dënschdeg", "Mëttwoch", "Donneschdeg", "Freideg", "Samschdeg" ], + dayNamesShort: [ "Son", "Méi", "Dën", "Mët", "Don", "Fre", "Sam" ], + dayNamesMin: [ "So","Mé","Dë","Më","Do","Fr","Sa" ], + weekHeader: "W", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.lb ); + +return datepicker.regional.lb; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-lt.js b/common/js/plugins/ui/i18n/datepicker-lt.js new file mode 100644 index 000000000..35554764f --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-lt.js @@ -0,0 +1,37 @@ +/* Lithuanian (UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* @author Arturas Paleicikas */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.lt = { + closeText: "Uždaryti", + prevText: "<Atgal", + nextText: "Pirmyn>", + currentText: "Šiandien", + monthNames: [ "Sausis","Vasaris","Kovas","Balandis","Gegužė","Birželis", + "Liepa","Rugpjūtis","Rugsėjis","Spalis","Lapkritis","Gruodis" ], + monthNamesShort: [ "Sau","Vas","Kov","Bal","Geg","Bir", + "Lie","Rugp","Rugs","Spa","Lap","Gru" ], + dayNames: [ "sekmadienis","pirmadienis","antradienis","trečiadienis","ketvirtadienis","penktadienis","šeštadienis" ], + dayNamesShort: [ "sek","pir","ant","tre","ket","pen","šeš" ], + dayNamesMin: [ "Se","Pr","An","Tr","Ke","Pe","Še" ], + weekHeader: "SAV", + dateFormat: "yy-mm-dd", + firstDay: 1, + isRTL: false, + showMonthAfterYear: true, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.lt ); + +return datepicker.regional.lt; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-lv.js b/common/js/plugins/ui/i18n/datepicker-lv.js new file mode 100644 index 000000000..ec49c43a6 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-lv.js @@ -0,0 +1,37 @@ +/* Latvian (UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* @author Arturas Paleicikas */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.lv = { + closeText: "Aizvērt", + prevText: "Iepr.", + nextText: "Nāk.", + currentText: "Šodien", + monthNames: [ "Janvāris","Februāris","Marts","Aprīlis","Maijs","Jūnijs", + "Jūlijs","Augusts","Septembris","Oktobris","Novembris","Decembris" ], + monthNamesShort: [ "Jan","Feb","Mar","Apr","Mai","Jūn", + "Jūl","Aug","Sep","Okt","Nov","Dec" ], + dayNames: [ "svētdiena","pirmdiena","otrdiena","trešdiena","ceturtdiena","piektdiena","sestdiena" ], + dayNamesShort: [ "svt","prm","otr","tre","ctr","pkt","sst" ], + dayNamesMin: [ "Sv","Pr","Ot","Tr","Ct","Pk","Ss" ], + weekHeader: "Ned.", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.lv ); + +return datepicker.regional.lv; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-mk.js b/common/js/plugins/ui/i18n/datepicker-mk.js new file mode 100644 index 000000000..97864ab15 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-mk.js @@ -0,0 +1,37 @@ +/* Macedonian i18n for the jQuery UI date picker plugin. */ +/* Written by Stojce Slavkovski. */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.mk = { + closeText: "Затвори", + prevText: "<", + nextText: ">", + currentText: "Денес", + monthNames: [ "Јануари","Февруари","Март","Април","Мај","Јуни", + "Јули","Август","Септември","Октомври","Ноември","Декември" ], + monthNamesShort: [ "Јан","Фев","Мар","Апр","Мај","Јун", + "Јул","Авг","Сеп","Окт","Ное","Дек" ], + dayNames: [ "Недела","Понеделник","Вторник","Среда","Четврток","Петок","Сабота" ], + dayNamesShort: [ "Нед","Пон","Вто","Сре","Чет","Пет","Саб" ], + dayNamesMin: [ "Не","По","Вт","Ср","Че","Пе","Са" ], + weekHeader: "Сед", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.mk ); + +return datepicker.regional.mk; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-ml.js b/common/js/plugins/ui/i18n/datepicker-ml.js new file mode 100644 index 000000000..440e09e03 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-ml.js @@ -0,0 +1,37 @@ +/* Malayalam (UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* Written by Saji Nediyanchath (saji89@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.ml = { + closeText: "ശരി", + prevText: "മുന്നത്തെ", + nextText: "അടുത്തത് ", + currentText: "ഇന്ന്", + monthNames: [ "ജനുവരി","ഫെബ്രുവരി","മാര്‍ച്ച്","ഏപ്രില്‍","മേയ്","ജൂണ്‍", + "ജൂലൈ","ആഗസ്റ്റ്","സെപ്റ്റംബര്‍","ഒക്ടോബര്‍","നവംബര്‍","ഡിസംബര്‍" ], + monthNamesShort: [ "ജനു", "ഫെബ്", "മാര്‍", "ഏപ്രി", "മേയ്", "ജൂണ്‍", + "ജൂലാ", "ആഗ", "സെപ്", "ഒക്ടോ", "നവം", "ഡിസ" ], + dayNames: [ "ഞായര്‍", "തിങ്കള്‍", "ചൊവ്വ", "ബുധന്‍", "വ്യാഴം", "വെള്ളി", "ശനി" ], + dayNamesShort: [ "ഞായ", "തിങ്ക", "ചൊവ്വ", "ബുധ", "വ്യാഴം", "വെള്ളി", "ശനി" ], + dayNamesMin: [ "ഞാ","തി","ചൊ","ബു","വ്യാ","വെ","ശ" ], + weekHeader: "ആ", + dateFormat: "dd/mm/yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.ml ); + +return datepicker.regional.ml; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-ms.js b/common/js/plugins/ui/i18n/datepicker-ms.js new file mode 100644 index 000000000..58bc4f579 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-ms.js @@ -0,0 +1,37 @@ +/* Malaysian initialisation for the jQuery UI date picker plugin. */ +/* Written by Mohd Nawawi Mohamad Jamili (nawawi@ronggeng.net). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.ms = { + closeText: "Tutup", + prevText: "<Sebelum", + nextText: "Selepas>", + currentText: "hari ini", + monthNames: [ "Januari","Februari","Mac","April","Mei","Jun", + "Julai","Ogos","September","Oktober","November","Disember" ], + monthNamesShort: [ "Jan","Feb","Mac","Apr","Mei","Jun", + "Jul","Ogo","Sep","Okt","Nov","Dis" ], + dayNames: [ "Ahad","Isnin","Selasa","Rabu","Khamis","Jumaat","Sabtu" ], + dayNamesShort: [ "Aha","Isn","Sel","Rab","kha","Jum","Sab" ], + dayNamesMin: [ "Ah","Is","Se","Ra","Kh","Ju","Sa" ], + weekHeader: "Mg", + dateFormat: "dd/mm/yy", + firstDay: 0, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.ms ); + +return datepicker.regional.ms; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-nb.js b/common/js/plugins/ui/i18n/datepicker-nb.js new file mode 100644 index 000000000..128f6836d --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-nb.js @@ -0,0 +1,36 @@ +/* Norwegian Bokmål initialisation for the jQuery UI date picker plugin. */ +/* Written by Bjørn Johansen (post@bjornjohansen.no). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.nb = { + closeText: "Lukk", + prevText: "«Forrige", + nextText: "Neste»", + currentText: "I dag", + monthNames: [ "januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember" ], + monthNamesShort: [ "jan","feb","mar","apr","mai","jun","jul","aug","sep","okt","nov","des" ], + dayNamesShort: [ "søn","man","tir","ons","tor","fre","lør" ], + dayNames: [ "søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag" ], + dayNamesMin: [ "sø","ma","ti","on","to","fr","lø" ], + weekHeader: "Uke", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" +}; +datepicker.setDefaults( datepicker.regional.nb ); + +return datepicker.regional.nb; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-nl-BE.js b/common/js/plugins/ui/i18n/datepicker-nl-BE.js new file mode 100644 index 000000000..9ea22002d --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-nl-BE.js @@ -0,0 +1,37 @@ +/* Dutch (Belgium) initialisation for the jQuery UI date picker plugin. */ +/* David De Sloovere @DavidDeSloovere */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional[ "nl-BE" ] = { + closeText: "Sluiten", + prevText: "←", + nextText: "→", + currentText: "Vandaag", + monthNames: [ "januari", "februari", "maart", "april", "mei", "juni", + "juli", "augustus", "september", "oktober", "november", "december" ], + monthNamesShort: [ "jan", "feb", "mrt", "apr", "mei", "jun", + "jul", "aug", "sep", "okt", "nov", "dec" ], + dayNames: [ "zondag", "maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag" ], + dayNamesShort: [ "zon", "maa", "din", "woe", "don", "vri", "zat" ], + dayNamesMin: [ "zo", "ma", "di", "wo", "do", "vr", "za" ], + weekHeader: "Wk", + dateFormat: "dd/mm/yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional[ "nl-BE" ] ); + +return datepicker.regional[ "nl-BE" ]; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-nl.js b/common/js/plugins/ui/i18n/datepicker-nl.js new file mode 100644 index 000000000..7fcbff1ac --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-nl.js @@ -0,0 +1,37 @@ +/* Dutch (UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* Written by Mathias Bynens */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.nl = { + closeText: "Sluiten", + prevText: "←", + nextText: "→", + currentText: "Vandaag", + monthNames: [ "januari", "februari", "maart", "april", "mei", "juni", + "juli", "augustus", "september", "oktober", "november", "december" ], + monthNamesShort: [ "jan", "feb", "mrt", "apr", "mei", "jun", + "jul", "aug", "sep", "okt", "nov", "dec" ], + dayNames: [ "zondag", "maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag" ], + dayNamesShort: [ "zon", "maa", "din", "woe", "don", "vri", "zat" ], + dayNamesMin: [ "zo", "ma", "di", "wo", "do", "vr", "za" ], + weekHeader: "Wk", + dateFormat: "dd-mm-yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.nl ); + +return datepicker.regional.nl; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-nn.js b/common/js/plugins/ui/i18n/datepicker-nn.js new file mode 100644 index 000000000..dfaec387b --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-nn.js @@ -0,0 +1,36 @@ +/* Norwegian Nynorsk initialisation for the jQuery UI date picker plugin. */ +/* Written by Bjørn Johansen (post@bjornjohansen.no). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.nn = { + closeText: "Lukk", + prevText: "«Førre", + nextText: "Neste»", + currentText: "I dag", + monthNames: [ "januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember" ], + monthNamesShort: [ "jan","feb","mar","apr","mai","jun","jul","aug","sep","okt","nov","des" ], + dayNamesShort: [ "sun","mån","tys","ons","tor","fre","lau" ], + dayNames: [ "sundag","måndag","tysdag","onsdag","torsdag","fredag","laurdag" ], + dayNamesMin: [ "su","må","ty","on","to","fr","la" ], + weekHeader: "Veke", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" +}; +datepicker.setDefaults( datepicker.regional.nn ); + +return datepicker.regional.nn; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-no.js b/common/js/plugins/ui/i18n/datepicker-no.js new file mode 100644 index 000000000..559aa111b --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-no.js @@ -0,0 +1,37 @@ +/* Norwegian initialisation for the jQuery UI date picker plugin. */ +/* Written by Naimdjon Takhirov (naimdjon@gmail.com). */ + +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.no = { + closeText: "Lukk", + prevText: "«Forrige", + nextText: "Neste»", + currentText: "I dag", + monthNames: [ "januar","februar","mars","april","mai","juni","juli","august","september","oktober","november","desember" ], + monthNamesShort: [ "jan","feb","mar","apr","mai","jun","jul","aug","sep","okt","nov","des" ], + dayNamesShort: [ "søn","man","tir","ons","tor","fre","lør" ], + dayNames: [ "søndag","mandag","tirsdag","onsdag","torsdag","fredag","lørdag" ], + dayNamesMin: [ "sø","ma","ti","on","to","fr","lø" ], + weekHeader: "Uke", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" +}; +datepicker.setDefaults( datepicker.regional.no ); + +return datepicker.regional.no; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-pl.js b/common/js/plugins/ui/i18n/datepicker-pl.js new file mode 100644 index 000000000..c2fddc132 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-pl.js @@ -0,0 +1,37 @@ +/* Polish initialisation for the jQuery UI date picker plugin. */ +/* Written by Jacek Wysocki (jacek.wysocki@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.pl = { + closeText: "Zamknij", + prevText: "<Poprzedni", + nextText: "Następny>", + currentText: "Dziś", + monthNames: [ "Styczeń","Luty","Marzec","Kwiecień","Maj","Czerwiec", + "Lipiec","Sierpień","Wrzesień","Październik","Listopad","Grudzień" ], + monthNamesShort: [ "Sty","Lu","Mar","Kw","Maj","Cze", + "Lip","Sie","Wrz","Pa","Lis","Gru" ], + dayNames: [ "Niedziela","Poniedziałek","Wtorek","Środa","Czwartek","Piątek","Sobota" ], + dayNamesShort: [ "Nie","Pn","Wt","Śr","Czw","Pt","So" ], + dayNamesMin: [ "N","Pn","Wt","Śr","Cz","Pt","So" ], + weekHeader: "Tydz", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.pl ); + +return datepicker.regional.pl; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-pt-BR.js b/common/js/plugins/ui/i18n/datepicker-pt-BR.js new file mode 100644 index 000000000..c4c0d541c --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-pt-BR.js @@ -0,0 +1,37 @@ +/* Brazilian initialisation for the jQuery UI date picker plugin. */ +/* Written by Leonildo Costa Silva (leocsilva@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional[ "pt-BR" ] = { + closeText: "Fechar", + prevText: "<Anterior", + nextText: "Próximo>", + currentText: "Hoje", + monthNames: [ "Janeiro","Fevereiro","Março","Abril","Maio","Junho", + "Julho","Agosto","Setembro","Outubro","Novembro","Dezembro" ], + monthNamesShort: [ "Jan","Fev","Mar","Abr","Mai","Jun", + "Jul","Ago","Set","Out","Nov","Dez" ], + dayNames: [ "Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado" ], + dayNamesShort: [ "Dom","Seg","Ter","Qua","Qui","Sex","Sáb" ], + dayNamesMin: [ "Dom","Seg","Ter","Qua","Qui","Sex","Sáb" ], + weekHeader: "Sm", + dateFormat: "dd/mm/yy", + firstDay: 0, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional[ "pt-BR" ] ); + +return datepicker.regional[ "pt-BR" ]; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-pt.js b/common/js/plugins/ui/i18n/datepicker-pt.js new file mode 100644 index 000000000..baed7f3de --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-pt.js @@ -0,0 +1,36 @@ +/* Portuguese initialisation for the jQuery UI date picker plugin. */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.pt = { + closeText: "Fechar", + prevText: "Anterior", + nextText: "Seguinte", + currentText: "Hoje", + monthNames: [ "Janeiro","Fevereiro","Março","Abril","Maio","Junho", + "Julho","Agosto","Setembro","Outubro","Novembro","Dezembro" ], + monthNamesShort: [ "Jan","Fev","Mar","Abr","Mai","Jun", + "Jul","Ago","Set","Out","Nov","Dez" ], + dayNames: [ "Domingo","Segunda-feira","Terça-feira","Quarta-feira","Quinta-feira","Sexta-feira","Sábado" ], + dayNamesShort: [ "Dom","Seg","Ter","Qua","Qui","Sex","Sáb" ], + dayNamesMin: [ "Dom","Seg","Ter","Qua","Qui","Sex","Sáb" ], + weekHeader: "Sem", + dateFormat: "dd/mm/yy", + firstDay: 0, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.pt ); + +return datepicker.regional.pt; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-rm.js b/common/js/plugins/ui/i18n/datepicker-rm.js new file mode 100644 index 000000000..856c8c254 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-rm.js @@ -0,0 +1,35 @@ +/* Romansh initialisation for the jQuery UI date picker plugin. */ +/* Written by Yvonne Gienal (yvonne.gienal@educa.ch). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.rm = { + closeText: "Serrar", + prevText: "<Suandant", + nextText: "Precedent>", + currentText: "Actual", + monthNames: [ "Schaner","Favrer","Mars","Avrigl","Matg","Zercladur", "Fanadur","Avust","Settember","October","November","December" ], + monthNamesShort: [ "Scha","Fev","Mar","Avr","Matg","Zer", "Fan","Avu","Sett","Oct","Nov","Dec" ], + dayNames: [ "Dumengia","Glindesdi","Mardi","Mesemna","Gievgia","Venderdi","Sonda" ], + dayNamesShort: [ "Dum","Gli","Mar","Mes","Gie","Ven","Som" ], + dayNamesMin: [ "Du","Gl","Ma","Me","Gi","Ve","So" ], + weekHeader: "emna", + dateFormat: "dd/mm/yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.rm ); + +return datepicker.regional.rm; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-ro.js b/common/js/plugins/ui/i18n/datepicker-ro.js new file mode 100644 index 000000000..b26665c25 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-ro.js @@ -0,0 +1,40 @@ +/* Romanian initialisation for the jQuery UI date picker plugin. + * + * Written by Edmond L. (ll_edmond@walla.com) + * and Ionut G. Stan (ionut.g.stan@gmail.com) + */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.ro = { + closeText: "Închide", + prevText: "« Luna precedentă", + nextText: "Luna următoare »", + currentText: "Azi", + monthNames: [ "Ianuarie","Februarie","Martie","Aprilie","Mai","Iunie", + "Iulie","August","Septembrie","Octombrie","Noiembrie","Decembrie" ], + monthNamesShort: [ "Ian", "Feb", "Mar", "Apr", "Mai", "Iun", + "Iul", "Aug", "Sep", "Oct", "Nov", "Dec" ], + dayNames: [ "Duminică", "Luni", "Marţi", "Miercuri", "Joi", "Vineri", "Sâmbătă" ], + dayNamesShort: [ "Dum", "Lun", "Mar", "Mie", "Joi", "Vin", "Sâm" ], + dayNamesMin: [ "Du","Lu","Ma","Mi","Jo","Vi","Sâ" ], + weekHeader: "Săpt", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.ro ); + +return datepicker.regional.ro; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-ru.js b/common/js/plugins/ui/i18n/datepicker-ru.js new file mode 100644 index 000000000..223e77645 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-ru.js @@ -0,0 +1,37 @@ +/* Russian (UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* Written by Andrew Stromnov (stromnov@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.ru = { + closeText: "Закрыть", + prevText: "<Пред", + nextText: "След>", + currentText: "Сегодня", + monthNames: [ "Январь","Февраль","Март","Апрель","Май","Июнь", + "Июль","Август","Сентябрь","Октябрь","Ноябрь","Декабрь" ], + monthNamesShort: [ "Янв","Фев","Мар","Апр","Май","Июн", + "Июл","Авг","Сен","Окт","Ноя","Дек" ], + dayNames: [ "воскресенье","понедельник","вторник","среда","четверг","пятница","суббота" ], + dayNamesShort: [ "вск","пнд","втр","срд","чтв","птн","сбт" ], + dayNamesMin: [ "Вс","Пн","Вт","Ср","Чт","Пт","Сб" ], + weekHeader: "Нед", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.ru ); + +return datepicker.regional.ru; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-sk.js b/common/js/plugins/ui/i18n/datepicker-sk.js new file mode 100644 index 000000000..16d8bdfe4 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-sk.js @@ -0,0 +1,37 @@ +/* Slovak initialisation for the jQuery UI date picker plugin. */ +/* Written by Vojtech Rinik (vojto@hmm.sk). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.sk = { + closeText: "Zavrieť", + prevText: "<Predchádzajúci", + nextText: "Nasledujúci>", + currentText: "Dnes", + monthNames: [ "január","február","marec","apríl","máj","jún", + "júl","august","september","október","november","december" ], + monthNamesShort: [ "Jan","Feb","Mar","Apr","Máj","Jún", + "Júl","Aug","Sep","Okt","Nov","Dec" ], + dayNames: [ "nedeľa","pondelok","utorok","streda","štvrtok","piatok","sobota" ], + dayNamesShort: [ "Ned","Pon","Uto","Str","Štv","Pia","Sob" ], + dayNamesMin: [ "Ne","Po","Ut","St","Št","Pia","So" ], + weekHeader: "Ty", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.sk ); + +return datepicker.regional.sk; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-sl.js b/common/js/plugins/ui/i18n/datepicker-sl.js new file mode 100644 index 000000000..689162492 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-sl.js @@ -0,0 +1,38 @@ +/* Slovenian initialisation for the jQuery UI date picker plugin. */ +/* Written by Jaka Jancar (jaka@kubje.org). */ +/* c = č, s = š z = ž C = Č S = Š Z = Ž */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.sl = { + closeText: "Zapri", + prevText: "<Prejšnji", + nextText: "Naslednji>", + currentText: "Trenutni", + monthNames: [ "Januar","Februar","Marec","April","Maj","Junij", + "Julij","Avgust","September","Oktober","November","December" ], + monthNamesShort: [ "Jan","Feb","Mar","Apr","Maj","Jun", + "Jul","Avg","Sep","Okt","Nov","Dec" ], + dayNames: [ "Nedelja","Ponedeljek","Torek","Sreda","Četrtek","Petek","Sobota" ], + dayNamesShort: [ "Ned","Pon","Tor","Sre","Čet","Pet","Sob" ], + dayNamesMin: [ "Ne","Po","To","Sr","Če","Pe","So" ], + weekHeader: "Teden", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.sl ); + +return datepicker.regional.sl; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-sq.js b/common/js/plugins/ui/i18n/datepicker-sq.js new file mode 100644 index 000000000..34fe66a59 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-sq.js @@ -0,0 +1,37 @@ +/* Albanian initialisation for the jQuery UI date picker plugin. */ +/* Written by Flakron Bytyqi (flakron@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.sq = { + closeText: "mbylle", + prevText: "<mbrapa", + nextText: "Përpara>", + currentText: "sot", + monthNames: [ "Janar","Shkurt","Mars","Prill","Maj","Qershor", + "Korrik","Gusht","Shtator","Tetor","Nëntor","Dhjetor" ], + monthNamesShort: [ "Jan","Shk","Mar","Pri","Maj","Qer", + "Kor","Gus","Sht","Tet","Nën","Dhj" ], + dayNames: [ "E Diel","E Hënë","E Martë","E Mërkurë","E Enjte","E Premte","E Shtune" ], + dayNamesShort: [ "Di","Hë","Ma","Më","En","Pr","Sh" ], + dayNamesMin: [ "Di","Hë","Ma","Më","En","Pr","Sh" ], + weekHeader: "Ja", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.sq ); + +return datepicker.regional.sq; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-sr-SR.js b/common/js/plugins/ui/i18n/datepicker-sr-SR.js new file mode 100644 index 000000000..e9db26a42 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-sr-SR.js @@ -0,0 +1,37 @@ +/* Serbian i18n for the jQuery UI date picker plugin. */ +/* Written by Dejan Dimić. */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional[ "sr-SR" ] = { + closeText: "Zatvori", + prevText: "<", + nextText: ">", + currentText: "Danas", + monthNames: [ "Januar","Februar","Mart","April","Maj","Jun", + "Jul","Avgust","Septembar","Oktobar","Novembar","Decembar" ], + monthNamesShort: [ "Jan","Feb","Mar","Apr","Maj","Jun", + "Jul","Avg","Sep","Okt","Nov","Dec" ], + dayNames: [ "Nedelja","Ponedeljak","Utorak","Sreda","Četvrtak","Petak","Subota" ], + dayNamesShort: [ "Ned","Pon","Uto","Sre","Čet","Pet","Sub" ], + dayNamesMin: [ "Ne","Po","Ut","Sr","Če","Pe","Su" ], + weekHeader: "Sed", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional[ "sr-SR" ] ); + +return datepicker.regional[ "sr-SR" ]; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-sr.js b/common/js/plugins/ui/i18n/datepicker-sr.js new file mode 100644 index 000000000..fa8827aa1 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-sr.js @@ -0,0 +1,37 @@ +/* Serbian i18n for the jQuery UI date picker plugin. */ +/* Written by Dejan Dimić. */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.sr = { + closeText: "Затвори", + prevText: "<", + nextText: ">", + currentText: "Данас", + monthNames: [ "Јануар","Фебруар","Март","Април","Мај","Јун", + "Јул","Август","Септембар","Октобар","Новембар","Децембар" ], + monthNamesShort: [ "Јан","Феб","Мар","Апр","Мај","Јун", + "Јул","Авг","Сеп","Окт","Нов","Дец" ], + dayNames: [ "Недеља","Понедељак","Уторак","Среда","Четвртак","Петак","Субота" ], + dayNamesShort: [ "Нед","Пон","Уто","Сре","Чет","Пет","Суб" ], + dayNamesMin: [ "Не","По","Ут","Ср","Че","Пе","Су" ], + weekHeader: "Сед", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.sr ); + +return datepicker.regional.sr; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-sv.js b/common/js/plugins/ui/i18n/datepicker-sv.js new file mode 100644 index 000000000..92686efa4 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-sv.js @@ -0,0 +1,37 @@ +/* Swedish initialisation for the jQuery UI date picker plugin. */ +/* Written by Anders Ekdahl ( anders@nomadiz.se). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.sv = { + closeText: "Stäng", + prevText: "«Förra", + nextText: "Nästa»", + currentText: "Idag", + monthNames: [ "Januari","Februari","Mars","April","Maj","Juni", + "Juli","Augusti","September","Oktober","November","December" ], + monthNamesShort: [ "Jan","Feb","Mar","Apr","Maj","Jun", + "Jul","Aug","Sep","Okt","Nov","Dec" ], + dayNamesShort: [ "Sön","Mån","Tis","Ons","Tor","Fre","Lör" ], + dayNames: [ "Söndag","Måndag","Tisdag","Onsdag","Torsdag","Fredag","Lördag" ], + dayNamesMin: [ "Sö","Må","Ti","On","To","Fr","Lö" ], + weekHeader: "Ve", + dateFormat: "yy-mm-dd", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.sv ); + +return datepicker.regional.sv; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-ta.js b/common/js/plugins/ui/i18n/datepicker-ta.js new file mode 100644 index 000000000..e3f09b00c --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-ta.js @@ -0,0 +1,37 @@ +/* Tamil (UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* Written by S A Sureshkumar (saskumar@live.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.ta = { + closeText: "மூடு", + prevText: "முன்னையது", + nextText: "அடுத்தது", + currentText: "இன்று", + monthNames: [ "தை","மாசி","பங்குனி","சித்திரை","வைகாசி","ஆனி", + "ஆடி","ஆவணி","புரட்டாசி","ஐப்பசி","கார்த்திகை","மார்கழி" ], + monthNamesShort: [ "தை","மாசி","பங்","சித்","வைகா","ஆனி", + "ஆடி","ஆவ","புர","ஐப்","கார்","மார்" ], + dayNames: [ "ஞாயிற்றுக்கிழமை","திங்கட்கிழமை","செவ்வாய்க்கிழமை","புதன்கிழமை","வியாழக்கிழமை","வெள்ளிக்கிழமை","சனிக்கிழமை" ], + dayNamesShort: [ "ஞாயிறு","திங்கள்","செவ்வாய்","புதன்","வியாழன்","வெள்ளி","சனி" ], + dayNamesMin: [ "ஞா","தி","செ","பு","வி","வெ","ச" ], + weekHeader: "Не", + dateFormat: "dd/mm/yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.ta ); + +return datepicker.regional.ta; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-th.js b/common/js/plugins/ui/i18n/datepicker-th.js new file mode 100644 index 000000000..6de48cf96 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-th.js @@ -0,0 +1,37 @@ +/* Thai initialisation for the jQuery UI date picker plugin. */ +/* Written by pipo (pipo@sixhead.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.th = { + closeText: "ปิด", + prevText: "« ย้อน", + nextText: "ถัดไป »", + currentText: "วันนี้", + monthNames: [ "มกราคม","กุมภาพันธ์","มีนาคม","เมษายน","พฤษภาคม","มิถุนายน", + "กรกฎาคม","สิงหาคม","กันยายน","ตุลาคม","พฤศจิกายน","ธันวาคม" ], + monthNamesShort: [ "ม.ค.","ก.พ.","มี.ค.","เม.ย.","พ.ค.","มิ.ย.", + "ก.ค.","ส.ค.","ก.ย.","ต.ค.","พ.ย.","ธ.ค." ], + dayNames: [ "อาทิตย์","จันทร์","อังคาร","พุธ","พฤหัสบดี","ศุกร์","เสาร์" ], + dayNamesShort: [ "อา.","จ.","อ.","พ.","พฤ.","ศ.","ส." ], + dayNamesMin: [ "อา.","จ.","อ.","พ.","พฤ.","ศ.","ส." ], + weekHeader: "Wk", + dateFormat: "dd/mm/yy", + firstDay: 0, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.th ); + +return datepicker.regional.th; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-tj.js b/common/js/plugins/ui/i18n/datepicker-tj.js new file mode 100644 index 000000000..8ede4ddcb --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-tj.js @@ -0,0 +1,37 @@ +/* Tajiki (UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* Written by Abdurahmon Saidov (saidovab@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.tj = { + closeText: "Идома", + prevText: "<Қафо", + nextText: "Пеш>", + currentText: "Имрӯз", + monthNames: [ "Январ","Феврал","Март","Апрел","Май","Июн", + "Июл","Август","Сентябр","Октябр","Ноябр","Декабр" ], + monthNamesShort: [ "Янв","Фев","Мар","Апр","Май","Июн", + "Июл","Авг","Сен","Окт","Ноя","Дек" ], + dayNames: [ "якшанбе","душанбе","сешанбе","чоршанбе","панҷшанбе","ҷумъа","шанбе" ], + dayNamesShort: [ "якш","душ","сеш","чор","пан","ҷум","шан" ], + dayNamesMin: [ "Як","Дш","Сш","Чш","Пш","Ҷм","Шн" ], + weekHeader: "Хф", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.tj ); + +return datepicker.regional.tj; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-tr.js b/common/js/plugins/ui/i18n/datepicker-tr.js new file mode 100644 index 000000000..8328e2199 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-tr.js @@ -0,0 +1,37 @@ +/* Turkish initialisation for the jQuery UI date picker plugin. */ +/* Written by Izzet Emre Erkan (kara@karalamalar.net). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.tr = { + closeText: "kapat", + prevText: "<geri", + nextText: "ileri>", + currentText: "bugün", + monthNames: [ "Ocak","Şubat","Mart","Nisan","Mayıs","Haziran", + "Temmuz","Ağustos","Eylül","Ekim","Kasım","Aralık" ], + monthNamesShort: [ "Oca","Şub","Mar","Nis","May","Haz", + "Tem","Ağu","Eyl","Eki","Kas","Ara" ], + dayNames: [ "Pazar","Pazartesi","Salı","Çarşamba","Perşembe","Cuma","Cumartesi" ], + dayNamesShort: [ "Pz","Pt","Sa","Ça","Pe","Cu","Ct" ], + dayNamesMin: [ "Pz","Pt","Sa","Ça","Pe","Cu","Ct" ], + weekHeader: "Hf", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.tr ); + +return datepicker.regional.tr; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-uk.js b/common/js/plugins/ui/i18n/datepicker-uk.js new file mode 100644 index 000000000..c82501ad2 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-uk.js @@ -0,0 +1,38 @@ +/* Ukrainian (UTF-8) initialisation for the jQuery UI date picker plugin. */ +/* Written by Maxim Drogobitskiy (maxdao@gmail.com). */ +/* Corrected by Igor Milla (igor.fsp.milla@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.uk = { + closeText: "Закрити", + prevText: "<", + nextText: ">", + currentText: "Сьогодні", + monthNames: [ "Січень","Лютий","Березень","Квітень","Травень","Червень", + "Липень","Серпень","Вересень","Жовтень","Листопад","Грудень" ], + monthNamesShort: [ "Січ","Лют","Бер","Кві","Тра","Чер", + "Лип","Сер","Вер","Жов","Лис","Гру" ], + dayNames: [ "неділя","понеділок","вівторок","середа","четвер","п’ятниця","субота" ], + dayNamesShort: [ "нед","пнд","вів","срд","чтв","птн","сбт" ], + dayNamesMin: [ "Нд","Пн","Вт","Ср","Чт","Пт","Сб" ], + weekHeader: "Тиж", + dateFormat: "dd.mm.yy", + firstDay: 1, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.uk ); + +return datepicker.regional.uk; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-vi.js b/common/js/plugins/ui/i18n/datepicker-vi.js new file mode 100644 index 000000000..2c208ab71 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-vi.js @@ -0,0 +1,37 @@ +/* Vietnamese initialisation for the jQuery UI date picker plugin. */ +/* Translated by Le Thanh Huy (lthanhhuy@cit.ctu.edu.vn). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional.vi = { + closeText: "Đóng", + prevText: "<Trước", + nextText: "Tiếp>", + currentText: "Hôm nay", + monthNames: [ "Tháng Một", "Tháng Hai", "Tháng Ba", "Tháng Tư", "Tháng Năm", "Tháng Sáu", + "Tháng Bảy", "Tháng Tám", "Tháng Chín", "Tháng Mười", "Tháng Mười Một", "Tháng Mười Hai" ], + monthNamesShort: [ "Tháng 1", "Tháng 2", "Tháng 3", "Tháng 4", "Tháng 5", "Tháng 6", + "Tháng 7", "Tháng 8", "Tháng 9", "Tháng 10", "Tháng 11", "Tháng 12" ], + dayNames: [ "Chủ Nhật", "Thứ Hai", "Thứ Ba", "Thứ Tư", "Thứ Năm", "Thứ Sáu", "Thứ Bảy" ], + dayNamesShort: [ "CN", "T2", "T3", "T4", "T5", "T6", "T7" ], + dayNamesMin: [ "CN", "T2", "T3", "T4", "T5", "T6", "T7" ], + weekHeader: "Tu", + dateFormat: "dd/mm/yy", + firstDay: 0, + isRTL: false, + showMonthAfterYear: false, + yearSuffix: "" }; +datepicker.setDefaults( datepicker.regional.vi ); + +return datepicker.regional.vi; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-zh-CN.js b/common/js/plugins/ui/i18n/datepicker-zh-CN.js new file mode 100644 index 000000000..91f99b4ed --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-zh-CN.js @@ -0,0 +1,37 @@ +/* Chinese initialisation for the jQuery UI date picker plugin. */ +/* Written by Cloudream (cloudream@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional[ "zh-CN" ] = { + closeText: "关闭", + prevText: "<上月", + nextText: "下月>", + currentText: "今天", + monthNames: [ "一月","二月","三月","四月","五月","六月", + "七月","八月","九月","十月","十一月","十二月" ], + monthNamesShort: [ "一月","二月","三月","四月","五月","六月", + "七月","八月","九月","十月","十一月","十二月" ], + dayNames: [ "星期日","星期一","星期二","星期三","星期四","星期五","星期六" ], + dayNamesShort: [ "周日","周一","周二","周三","周四","周五","周六" ], + dayNamesMin: [ "日","一","二","三","四","五","六" ], + weekHeader: "周", + dateFormat: "yy-mm-dd", + firstDay: 1, + isRTL: false, + showMonthAfterYear: true, + yearSuffix: "年" }; +datepicker.setDefaults( datepicker.regional[ "zh-CN" ] ); + +return datepicker.regional[ "zh-CN" ]; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-zh-HK.js b/common/js/plugins/ui/i18n/datepicker-zh-HK.js new file mode 100644 index 000000000..27f02bc65 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-zh-HK.js @@ -0,0 +1,37 @@ +/* Chinese initialisation for the jQuery UI date picker plugin. */ +/* Written by SCCY (samuelcychan@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional[ "zh-HK" ] = { + closeText: "關閉", + prevText: "<上月", + nextText: "下月>", + currentText: "今天", + monthNames: [ "一月","二月","三月","四月","五月","六月", + "七月","八月","九月","十月","十一月","十二月" ], + monthNamesShort: [ "一月","二月","三月","四月","五月","六月", + "七月","八月","九月","十月","十一月","十二月" ], + dayNames: [ "星期日","星期一","星期二","星期三","星期四","星期五","星期六" ], + dayNamesShort: [ "周日","周一","周二","周三","周四","周五","周六" ], + dayNamesMin: [ "日","一","二","三","四","五","六" ], + weekHeader: "周", + dateFormat: "dd-mm-yy", + firstDay: 0, + isRTL: false, + showMonthAfterYear: true, + yearSuffix: "年" }; +datepicker.setDefaults( datepicker.regional[ "zh-HK" ] ); + +return datepicker.regional[ "zh-HK" ]; + +} ) ); diff --git a/common/js/plugins/ui/i18n/datepicker-zh-TW.js b/common/js/plugins/ui/i18n/datepicker-zh-TW.js new file mode 100644 index 000000000..c20754bd5 --- /dev/null +++ b/common/js/plugins/ui/i18n/datepicker-zh-TW.js @@ -0,0 +1,37 @@ +/* Chinese initialisation for the jQuery UI date picker plugin. */ +/* Written by Ressol (ressol@gmail.com). */ +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "../widgets/datepicker" ], factory ); + } else { + + // Browser globals + factory( jQuery.datepicker ); + } +}( function( datepicker ) { + +datepicker.regional[ "zh-TW" ] = { + closeText: "關閉", + prevText: "<上月", + nextText: "下月>", + currentText: "今天", + monthNames: [ "一月","二月","三月","四月","五月","六月", + "七月","八月","九月","十月","十一月","十二月" ], + monthNamesShort: [ "一月","二月","三月","四月","五月","六月", + "七月","八月","九月","十月","十一月","十二月" ], + dayNames: [ "星期日","星期一","星期二","星期三","星期四","星期五","星期六" ], + dayNamesShort: [ "周日","周一","周二","周三","周四","周五","周六" ], + dayNamesMin: [ "日","一","二","三","四","五","六" ], + weekHeader: "周", + dateFormat: "yy/mm/dd", + firstDay: 1, + isRTL: false, + showMonthAfterYear: true, + yearSuffix: "年" }; +datepicker.setDefaults( datepicker.regional[ "zh-TW" ] ); + +return datepicker.regional[ "zh-TW" ]; + +} ) ); diff --git a/common/js/plugins/ui/plugin.load b/common/js/plugins/ui/plugin.load index 49fcfdc53..73e7053a1 100644 --- a/common/js/plugins/ui/plugin.load +++ b/common/js/plugins/ui/plugin.load @@ -1,3 +1,3 @@ jquery-ui.min.js jquery-ui.min.css -jquery.ui.datepicker-ko.js +rx_datepicker.js diff --git a/common/js/plugins/ui/rx_datepicker.js b/common/js/plugins/ui/rx_datepicker.js new file mode 100644 index 000000000..8c9c4d455 --- /dev/null +++ b/common/js/plugins/ui/rx_datepicker.js @@ -0,0 +1,7 @@ +/** + * @brief Load i18n file of datepicker + * @author MinSoo Kim + **/ +if(typeof current_lang !== "undefined" && current_lang !== 'en') { + jQuery.getScript(request_uri + "./common/js/plugins/ui/i18n/datepicker-"+ current_lang.replace("jp", "ja") +".js"); +} \ No newline at end of file From c9ed1f08e97d6fb84aeba20a261b71e9cd5dc6bf Mon Sep 17 00:00:00 2001 From: MinSoo Kim Date: Tue, 23 Feb 2016 04:13:14 +0900 Subject: [PATCH 002/205] =?UTF-8?q?datepicker=20=EA=B0=80=20=EB=82=B4?= =?UTF-8?q?=EC=9E=A5=EB=90=9C=20=EB=B8=8C=EB=9D=BC=EC=9A=B0=EC=A0=80?= =?UTF-8?q?=EC=97=90=EC=84=9C=EB=8A=94=20=EB=82=B4=EC=9E=A5=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/counter/tpl/index.html | 2 +- modules/member/member.admin.view.php | 4 +- modules/member/member.controller.php | 142 +++++++++++++++++- modules/member/skins/default/modify_info.html | 31 +++- modules/member/skins/default/signup_form.html | 35 +++-- modules/member/tpl/insert_member.html | 32 +++- modules/poll/poll.controller.php | 2 +- 7 files changed, 213 insertions(+), 35 deletions(-) diff --git a/modules/counter/tpl/index.html b/modules/counter/tpl/index.html index 04884cbc7..e8c58f4b7 100644 --- a/modules/counter/tpl/index.html +++ b/modules/counter/tpl/index.html @@ -47,7 +47,7 @@ } }; - $.extend(option,$.datepicker.regional['{$lang_type}']); + $.extend($.datepicker.regional['{$lang_type}'],option); $(".inputDate").datepicker(option); }); })(jQuery); diff --git a/modules/member/member.admin.view.php b/modules/member/member.admin.view.php index 21e3d6ba8..04ba47687 100644 --- a/modules/member/member.admin.view.php +++ b/modules/member/member.admin.view.php @@ -486,7 +486,7 @@ class memberAdminView extends member else if($formInfo->name == 'birthday') { $formTag->type = 'date'; - $inputTag = sprintf(' ', + $inputTag = sprintf(' ', $memberInfo['birthday'], zdate($memberInfo['birthday'], 'Y-m-d', false), $lang->cmd_delete); @@ -627,7 +627,7 @@ class memberAdminView extends member else if($extendForm->column_type == 'date') { $extentionReplace = array('date' => zdate($extendForm->value, 'Y-m-d'), 'cmd_delete' => $lang->cmd_delete); - $template = ' '; + $template = ' '; } $replace = array_merge($extentionReplace, $replace); diff --git a/modules/member/member.controller.php b/modules/member/member.controller.php index c99dc0062..c9829021b 100644 --- a/modules/member/member.controller.php +++ b/modules/member/member.controller.php @@ -301,7 +301,16 @@ class memberController extends member $args->{$val} = Context::get($val); if($val == 'birthday') $args->birthday_ui = Context::get('birthday_ui'); } - $args->birthday = intval(strtr($args->birthday, array('-'=>'', '/'=>'', '.'=>'', ' '=>''))); + + // mobile input date format can be different + if($args->birthday !== intval($args->birthday)) + { + $args->birthday = date('Ymd', strtotime($args->birthday)); + } + else + { + $args->birthday = intval($args->birthday); + } if(!$args->birthday && $args->birthday_ui) $args->birthday = intval(strtr($args->birthday_ui, array('-'=>'', '/'=>'', '.'=>'', ' '=>''))); $args->find_account_answer = Context::get('find_account_answer'); @@ -535,8 +544,18 @@ class memberController extends member // Login Information $logged_info = Context::get('logged_info'); $args->member_srl = $logged_info->member_srl; - $args->birthday = intval(strtr($args->birthday, array('-'=>'', '/'=>'', '.'=>'', ' '=>''))); + + // mobile input date format can be different + if($args->birthday !== intval($args->birthday)) + { + $args->birthday = date('Ymd', strtotime($args->birthday)); + } + else + { + $args->birthday = intval($args->birthday); + } if(!$args->birthday && $args->birthday_ui) $args->birthday = intval(strtr($args->birthday_ui, array('-'=>'', '/'=>'', '.'=>'', ' '=>''))); + // Remove some unnecessary variables from all the vars $all_args = Context::getRequestVars(); unset($all_args->module); @@ -1938,10 +1957,23 @@ class memberController extends member $output = ModuleHandler::triggerCall('member.insertMember', 'before', $args); if(!$output->toBool()) return $output; // Terms and Conditions portion of the information set up by members reaffirmed - $oModuleModel = getModel('module'); - $config = $oModuleModel->getModuleConfig('member'); + $oMemberModel = getModel('member'); + $config = $oMemberModel->getMemberConfig(); $logged_info = Context::get('logged_info'); + // limit_date format is YYYYMMDD + if($args->limit_date) + { + // mobile input date format can be different + if($args->limit_date !== intval($args->limit_date)) + { + $args->limit_date = date('Ymd', strtotime($args->limit_date)); + } + else + { + $args->limit_date = intval($args->limit_date); + } + } // If the date of the temporary restrictions limit further information on the date of if($config->limit_day) $args->limit_date = date("YmdHis", $_SERVER['REQUEST_TIME']+$config->limit_day*60*60*24); @@ -1980,6 +2012,49 @@ class memberController extends member if($args->homepage && !preg_match("/^[a-z]+:\/\//i",$args->homepage)) $args->homepage = 'http://'.$args->homepage; if($args->blog && !preg_match("/^[a-z]+:\/\//i",$args->blog)) $args->blog = 'http://'.$args->blog; + + $extend_form_list = $oMemberModel->getCombineJoinForm($memberInfo); + $security = new Security($extend_form_list); + $security->encodeHTML('..column_title', '..description', '..default_value.'); + if($config->signupForm) { + foreach($config->signupForm as $no => $formInfo) + { + if(!$formInfo->isUse) continue; + if($formInfo->isDefaultForm) + { + // birthday format is YYYYMMDD + if($formInfo->name === 'birthday' && $args->{$formInfo->name}) + { + // mobile input date format can be different + if($args->{$formInfo->name} !== intval($args->{$formInfo->name})) + { + $args->{$formInfo->name} = date('Ymd', strtotime($args->{$formInfo->name})); + } + else + { + $args->{$formInfo->name} = intval($args->{$formInfo->name}); + } + } + } + else + { + $extendForm = $extend_form_list[$formInfo->member_join_form_srl]; + // date format is YYYYMMDD + if($extendForm->column_type == 'date' && $args->{$formInfo->name}) + { + if($args->{$formInfo->name} !== intval($args->{$formInfo->name})) + { + $args->{$formInfo->name} = date('Ymd', strtotime($args->{$formInfo->name})); + } + else + { + $args->{$formInfo->name} = intval($args->{$formInfo->name}); + } + } + } + } + } + // Create a model object $oMemberModel = getModel('member'); @@ -2096,7 +2171,6 @@ class memberController extends member } } - $member_config = $oModuleModel->getModuleConfig('member'); // When using email authentication mode (when you subscribed members denied a) certified mail sent if($args->denied == 'Y') { @@ -2146,6 +2220,7 @@ class memberController extends member if(!$output->toBool()) return $output; // Create a model object $oMemberModel = getModel('member'); + $config = $oMemberModel->getMemberConfig(); $logged_info = Context::get('logged_info'); // Get what you want to modify the original information @@ -2180,7 +2255,62 @@ class memberController extends member if($args->blog && !preg_match("/^[a-z]+:\/\//is",$args->blog)) $args->blog = 'http://'.$args->blog; // check member identifier form - $config = $oMemberModel->getMemberConfig(); + + // limit_date format is YYYYMMDD + if($args->limit_date) + { + // mobile input date format can be different + if($args->limit_date !== intval($args->limit_date)) + { + $args->limit_date = date('Ymd', strtotime($args->limit_date)); + } + else + { + $args->limit_date = intval($args->limit_date); + } + } + + $extend_form_list = $oMemberModel->getCombineJoinForm($memberInfo); + $security = new Security($extend_form_list); + $security->encodeHTML('..column_title', '..description', '..default_value.'); + if($config->signupForm){ + foreach($config->signupForm as $no => $formInfo) + { + if(!$formInfo->isUse) continue; + + if($formInfo->isDefaultForm) + { + // birthday format is YYYYMMDD + if($formInfo->name === 'birthday' && $args->{$formInfo->name}) + { + if($args->{$formInfo->name} !== intval($args->{$formInfo->name})) + { + $args->{$formInfo->name} = date('Ymd', strtotime($args->{$formInfo->name})); + } + else + { + $args->{$formInfo->name} = intval($args->{$formInfo->name}); + } + } + } + else + { + $extendForm = $extend_form_list[$formInfo->member_join_form_srl]; + // date format is YYYYMMDD + if($extendForm->column_type == 'date' && $args->{$formInfo->name}) + { + if($args->{$formInfo->name} !== intval($args->{$formInfo->name})) + { + $args->{$formInfo->name} = date('Ymd', strtotime($args->{$formInfo->name})); + } + else + { + $args->{$formInfo->name} = intval($args->{$formInfo->name}); + } + } + } + } + } $output = executeQuery('member.getMemberInfoByMemberSrl', $args); $orgMemberInfo = $output->data; diff --git a/modules/member/skins/default/modify_info.html b/modules/member/skins/default/modify_info.html index f09eefd3a..ced6dae08 100644 --- a/modules/member/skins/default/modify_info.html +++ b/modules/member/skins/default/modify_info.html @@ -66,16 +66,33 @@ jQuery(function($){ }); }); (function($){ - $(function(){ - var option = { changeMonth: true, changeYear: true, gotoCurrent: false,yearRange:'-100:+10', dateFormat:'yy-mm-dd', onSelect:function(){ - $(this).prev('input[type="hidden"]').val(this.value.replace(/-/g,""))} - }; - $.extend(option,$.datepicker.regional['{$lang_type}']); - $(".inputDate").datepicker(option); + $(function(){ + // check if the browser support type date. + if ( $(".inputDate").prop('type') != 'date' ) { + var option = { + changeMonth: true, + changeYear: true, + gotoCurrent: false, + yearRange:'-200:+10', + dateFormat:'yy-mm-dd', + defaultDate: new Date("{date('Y-m-d',time())}"), + minDate: new Date("{date('Y-m-d',strtotime('-200 years'))}"), + + onSelect:function(){ + $(this).prev('input[type="hidden"]').val(this.value.replace(/-/g,"")) + } + }; + $.extend($.datepicker.regional['{$lang_type}'],option); + + //if the browser does not support type date input, start datepicker. If it does, brower's UI will show their datepicker. + $(".inputDate").datepicker(option); + } else { + $(".inputDate").prop('readonly', false); + } $(".dateRemover").click(function() { $(this).prevAll('input').val(''); return false;}); - }); + }); })(jQuery); diff --git a/modules/member/skins/default/signup_form.html b/modules/member/skins/default/signup_form.html index cc6788c4e..b3d464d38 100644 --- a/modules/member/skins/default/signup_form.html +++ b/modules/member/skins/default/signup_form.html @@ -72,7 +72,7 @@ - + \ No newline at end of file diff --git a/modules/member/tpl/insert_member.html b/modules/member/tpl/insert_member.html index e76df5070..552c3d62d 100644 --- a/modules/member/tpl/insert_member.html +++ b/modules/member/tpl/insert_member.html @@ -88,7 +88,7 @@
- + {$lang->about_limit_date}
@@ -124,15 +124,31 @@ \ No newline at end of file diff --git a/modules/member/m.skins/default/signup_form.html b/modules/member/m.skins/default/signup_form.html index 62e0c1669..fffc40e05 100644 --- a/modules/member/m.skins/default/signup_form.html +++ b/modules/member/m.skins/default/signup_form.html @@ -1,5 +1,7 @@ + +

{$lang->cmd_signup}

@@ -58,3 +60,34 @@
+ \ No newline at end of file From f3916c9535475b39ba041bdeeb79b1d4d3dcf6c4 Mon Sep 17 00:00:00 2001 From: conory Date: Thu, 3 Mar 2016 23:03:22 +0900 Subject: [PATCH 004/205] =?UTF-8?q?=EA=B0=9C=EC=9D=B8=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=9D=B4=EB=A9=94=EC=9D=BC=20=EB=B3=B4=ED=98=B8=20(=EB=A9=94?= =?UTF-8?q?=EC=9D=BC=EC=A3=BC=EC=86=8C=20=EA=B0=80=EB=A6=AC=EA=B8=B0,=20?= =?UTF-8?q?=EC=B9=9C=EA=B5=AC=EB=A7=8C=20=EB=A9=94=EC=9D=BC=20=EB=B3=B4?= =?UTF-8?q?=EB=82=B4=EA=B8=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/member/member.model.php | 10 +++++++--- modules/member/member.view.php | 6 +++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/modules/member/member.model.php b/modules/member/member.model.php index fd72ef1ce..bae8da737 100644 --- a/modules/member/member.model.php +++ b/modules/member/member.model.php @@ -155,10 +155,14 @@ class memberModel extends member } // Send an email only if email address is public - if(($logged_info->is_admin == 'Y' || $email_config->isPublic == 'Y') && $member_info->email_address) + if($email_config->isPublic == 'Y' && $member_info->email_address) { - $url = 'mailto:'.htmlspecialchars($member_info->email_address, ENT_COMPAT | ENT_HTML401, 'UTF-8', false); - $oMemberController->addMemberPopupMenu($url,'cmd_send_email',$icon_path); + $oCommunicationModel = getModel('communication'); + if($logged_info->is_admin == 'Y' || $oCommunicationModel->isFriend($member_info->member_srl)) + { + $url = 'mailto:'.htmlspecialchars($member_info->email_address, ENT_COMPAT | ENT_HTML401, 'UTF-8', false); + $oMemberController->addMemberPopupMenu($url,'cmd_send_email',$icon_path); + } } } // View homepage info diff --git a/modules/member/member.view.php b/modules/member/member.view.php index 687dd1cc9..06bd6ac78 100644 --- a/modules/member/member.view.php +++ b/modules/member/member.view.php @@ -84,9 +84,9 @@ class memberView extends member if($logged_info->is_admin != 'Y' && ($member_info->member_srl != $logged_info->member_srl)) { - $start = strpos($member_info->email_address, '@')+1; - $replaceStr = str_repeat('*', (strlen($member_info->email_address) - $start)); - $member_info->email_address = substr_replace($member_info->email_address, $replaceStr, $start); + list($email_id, $email_host) = explode('@', $member_info->email_address); + $protect_id = substr($email_id, 0, 2) . str_repeat('*', strlen($email_id)-2); + $member_info->email_address = sprintf('%s@%s', $protect_id, $email_host); } if(!$member_info->member_srl) return $this->dispMemberSignUpForm(); From a50695f047e3ef3aced7bfa4f413935644a815ea Mon Sep 17 00:00:00 2001 From: conory Date: Fri, 4 Mar 2016 20:35:42 +0900 Subject: [PATCH 005/205] =?UTF-8?q?#331=20=EC=AA=BD=EC=A7=80=EB=B3=B4?= =?UTF-8?q?=EB=82=B4=EA=B8=B0=EC=99=80=20=EC=B9=9C=EA=B5=AC=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=20=EB=A9=94=EB=89=B4=EA=B0=80=20=EB=82=98=EC=98=A4?= =?UTF-8?q?=EC=A7=80=EC=95=8A=EC=95=98=EB=8D=98=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/communication/communication.class.php | 60 +++------ .../communication.controller.php | 125 ++++++++++-------- 2 files changed, 85 insertions(+), 100 deletions(-) diff --git a/modules/communication/communication.class.php b/modules/communication/communication.class.php index 559e2f28b..a4b2518d3 100644 --- a/modules/communication/communication.class.php +++ b/modules/communication/communication.class.php @@ -9,8 +9,10 @@ class communication extends ModuleObject { private $triggers = array( - array('moduleHandler.init', 'communication', 'controller', 'triggerModuleHandlerAfter', 'after') + array('moduleHandler.init', 'communication', 'controller', 'triggerModuleHandlerBefore', 'before'), + array('member.getMemberMenu', 'communication', 'controller', 'triggerMemberMenu', 'before') ); + /** * Implement if additional tasks are necessary when installing * @return Object @@ -18,6 +20,7 @@ class communication extends ModuleObject function moduleInstall() { $oModuleController = getController('module'); + foreach($this->triggers as $trigger) { $oModuleController->insertTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4]); @@ -25,6 +28,7 @@ class communication extends ModuleObject // Create a temporary file storage for one new private message notification FileHandler::makeDir('./files/member_extra_info/new_message_flags'); + return new Object(); } @@ -34,28 +38,8 @@ class communication extends ModuleObject */ function checkUpdate() { - if(!is_dir("./files/member_extra_info/new_message_flags")) - { - return TRUE; - } - - $oModuleModel = getModel('module'); - $config = $oModuleModel->getModuleConfig('message'); - - if($config->skin) - { - $config_parse = explode('.', $config->skin); - if(count($config_parse) > 1) - { - $template_path = sprintf('./themes/%s/modules/communication/', $config_parse[0]); - if(is_dir($template_path)) - { - return TRUE; - } - } - } - $oModuleModel = getModel('module'); + foreach($this->triggers as $trigger) { if(!$oModuleModel->getTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4])) @@ -63,6 +47,12 @@ class communication extends ModuleObject return TRUE; } } + + if(!is_dir("./files/member_extra_info/new_message_flags")) + { + return TRUE; + } + return FALSE; } @@ -72,13 +62,9 @@ class communication extends ModuleObject */ function moduleUpdate() { - if(!is_dir("./files/member_extra_info/new_message_flags")) - { - FileHandler::makeDir('./files/member_extra_info/new_message_flags'); - } - $oModuleModel = getModel('module'); $oModuleController = getController('module'); + foreach($this->triggers as $trigger) { if(!$oModuleModel->getTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4])) @@ -86,24 +72,10 @@ class communication extends ModuleObject $oModuleController->insertTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4]); } } - $config = $oModuleModel->getModuleConfig('message'); - if(!is_object($config)) + + if(!is_dir("./files/member_extra_info/new_message_flags")) { - $config = new stdClass(); - } - - if($config->skin) - { - $config_parse = explode('.', $config->skin); - if(count($config_parse) > 1) - { - $template_path = sprintf('./themes/%s/modules/communication/', $config_parse[0]); - if(is_dir($template_path)) - { - $config->skin = implode('|@|', $config_parse); - $oModuleController->updateModuleConfig('communication', $config); - } - } + FileHandler::makeDir('./files/member_extra_info/new_message_flags'); } return new Object(0, 'success_updated'); diff --git a/modules/communication/communication.controller.php b/modules/communication/communication.controller.php index 452c61afc..248f04632 100644 --- a/modules/communication/communication.controller.php +++ b/modules/communication/communication.controller.php @@ -777,14 +777,9 @@ class communicationController extends communication return executeQuery('communication.setMessageReaded', $args); } - function triggerModuleHandlerAfter($module) + function triggerModuleHandlerBefore($obj) { - if(!Context::get('is_logged') && isCrawler()) - { - return new Object(); - } - - if($module->module == 'admin') + if(!Context::get('is_logged') || $obj->module == 'member') { return new Object(); } @@ -796,60 +791,78 @@ class communicationController extends communication { return new Object(); } - $act = Context::get('act'); - if($module->module != 'member') - { - $oMemberController = getController('member'); - $oMemberController->addMemberMenu('dispCommunicationFriend', 'cmd_view_friend'); - $oMemberController->addMemberMenu('dispCommunicationMessages', 'cmd_view_message_box'); + $logged_info = Context::get('logged_info'); + + // Add menus on the member login information + $oMemberController = getController('member'); + $oMemberController->addMemberMenu('dispCommunicationFriend', 'cmd_view_friend'); + $oMemberController->addMemberMenu('dispCommunicationMessages', 'cmd_view_message_box'); + + $flag_file = './files/member_extra_info/new_message_flags/' . getNumberingPath($logged_info->member_srl) . $logged_info->member_srl; + if(file_exists($flag_file)) + { // Pop-up to display messages if a flag on new message is set - $new_message_count = $oCommunicationModel->getNewMessageCount(); - if($new_message_count > 0) - { - Context::loadFile('./modules/communication/tpl/js/member_communication.js'); - $text = preg_replace('@\r?\n@', '\\n', addslashes(Context::getLang('alert_new_message_arrived'))); - Context::addHtmlHeader(""); - } - + $new_message_count = (int) trim(FileHandler::readFile($flag_file)); + $text = preg_replace('@\r?\n@', '\\n', addslashes(Context::getLang('alert_new_message_arrived'))); + Context::addHtmlFooter(""); + Context::loadFile(array('./modules/communication/tpl/js/member_communication.js'), true); + + FileHandler::removeFile($flag_file); } - elseif($act == 'getMemberMenu') + } + + function triggerMemberMenu() + { + if(!Context::get('is_logged')) { - $member_srl = Context::get('target_srl'); - $oCommunicationModel = getModel('communication'); - $logged_info = Context::get('logged_info'); - // Add a feature to display own message box. - if($logged_info->member_srl == $member_srl) - { - $mid = Context::get('cur_mid'); - $oMemberController = getController('member'); - // Add your own viewing Note Template - $oMemberController->addMemberPopupMenu(getUrl('', 'mid', $mid, 'act', 'dispCommunicationMessages'), 'cmd_view_message_box', '', 'self'); - // Display a list of friends - $oMemberController->addMemberPopupMenu(getUrl('', 'mid', $mid, 'act', 'dispCommunicationFriend'), 'cmd_view_friend', '', 'self'); - // If not, Add menus to send message and to add friends - } - else - { - // Get member information - $oMemberModel = getModel('member'); - $target_member_info = $oMemberModel->getMemberInfoByMemberSrl($member_srl); - if(!$target_member_info->member_srl) - { - return new Object(); - } + return new Object(); + } + + $oCommunicationModel = getModel('communication'); + $config = $oCommunicationModel->getConfig(); - $oMemberController = getController('member'); - // Add a menu for sending message - if($logged_info->is_admin == 'Y' || $target_member_info->allow_message == 'Y' || ($target_member_info->allow_message == 'F' && $oCommunicationModel->isFriend($member_srl))) - { - $oMemberController->addMemberPopupMenu(getUrl('', 'mid', Context::get('cur_mid'), 'act', 'dispCommunicationSendMessage', 'receiver_srl', $member_srl), 'cmd_send_message', '', 'popup'); - } - // Add a menu for listing friends (if a friend is new) - if(!$oCommunicationModel->isAddedFriend($member_srl)) - { - $oMemberController->addMemberPopupMenu(getUrl('', 'mid', Context::get('cur_mid'), 'act', 'dispCommunicationAddFriend', 'target_srl', $member_srl), 'cmd_add_friend', '', 'popup'); - } + if($config->member_menu != 'Y') + { + return new Object(); + } + + $mid = Context::get('cur_mid'); + $member_srl = Context::get('target_srl'); + $logged_info = Context::get('logged_info'); + $oMemberController = getController('member'); + + // Add a feature to display own message box. + if($logged_info->member_srl == $member_srl) + { + // Add your own viewing Note Template + $oMemberController->addMemberPopupMenu(getUrl('', 'mid', $mid, 'act', 'dispCommunicationMessages'), 'cmd_view_message_box', '', 'self'); + + // Display a list of friends + $oMemberController->addMemberPopupMenu(getUrl('', 'mid', $mid, 'act', 'dispCommunicationFriend'), 'cmd_view_friend', '', 'self'); + + } + // If not, Add menus to send message and to add friends + else + { + // Get member information + $oMemberModel = getModel('member'); + $target_member_info = $oMemberModel->getMemberInfoByMemberSrl($member_srl); + if(!$target_member_info->member_srl) + { + return new Object(); + } + + // Add a menu for sending message + if($logged_info->is_admin == 'Y' || $target_member_info->allow_message == 'Y' || ($target_member_info->allow_message == 'F' && $oCommunicationModel->isFriend($member_srl))) + { + $oMemberController->addMemberPopupMenu(getUrl('', 'mid', $mid, 'act', 'dispCommunicationSendMessage', 'receiver_srl', $member_srl), 'cmd_send_message', '', 'popup'); + } + + // Add a menu for listing friends (if a friend is new) + if(!$oCommunicationModel->isAddedFriend($member_srl)) + { + $oMemberController->addMemberPopupMenu(getUrl('', 'mid', $mid, 'act', 'dispCommunicationAddFriend', 'target_srl', $member_srl), 'cmd_add_friend', '', 'popup'); } } } From b8b0e8978a1b08def7ba5b228eb9c6d057aafa7c Mon Sep 17 00:00:00 2001 From: conory Date: Fri, 4 Mar 2016 22:50:31 +0900 Subject: [PATCH 006/205] =?UTF-8?q?=EC=AA=BD=EC=A7=80,=20=EC=B9=9C?= =?UTF-8?q?=EA=B5=AC=EC=97=90=20=EB=8C=80=ED=95=9C=20=ED=99=9C=EC=84=B1?= =?UTF-8?q?=ED=99=94=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../communication.admin.controller.php | 2 +- .../communication.admin.view.php | 12 +-- .../communication.controller.php | 64 ++++++++------ modules/communication/communication.model.php | 86 ++++++++----------- modules/communication/lang/en.php | 4 +- modules/communication/lang/ko.php | 4 +- modules/communication/tpl/index.html | 66 ++++++++------ 7 files changed, 127 insertions(+), 111 deletions(-) diff --git a/modules/communication/communication.admin.controller.php b/modules/communication/communication.admin.controller.php index f0e7f0974..0b52ad2b2 100644 --- a/modules/communication/communication.admin.controller.php +++ b/modules/communication/communication.admin.controller.php @@ -24,7 +24,7 @@ class communicationAdminController extends communication function procCommunicationAdminInsertConfig() { // get the default information - $args = Context::gets('skin', 'colorset', 'editor_skin', 'sel_editor_colorset', 'mskin', 'mcolorset', 'layout_srl', 'mlayout_srl', 'grant_write_default','grant_write_group', 'member_menu'); + $args = Context::gets('enable_message', 'enable_friend', 'skin', 'colorset', 'editor_skin', 'sel_editor_colorset', 'mskin', 'mcolorset', 'layout_srl', 'mlayout_srl', 'grant_write_default','grant_write_group'); $args->editor_colorset = $args->sel_editor_colorset; unset($args->sel_editor_colorset); diff --git a/modules/communication/communication.admin.view.php b/modules/communication/communication.admin.view.php index 9fc91e73d..89dd8f498 100644 --- a/modules/communication/communication.admin.view.php +++ b/modules/communication/communication.admin.view.php @@ -30,7 +30,7 @@ class communicationAdminView extends communication $oCommunicationModel = getModel('communication'); // get the configurations of communication module - Context::set('communication_config', $oCommunicationModel->getConfig()); + Context::set('config', $oCommunicationModel->getConfig()); // get a list of layout Context::set('layout_list', $oLayoutModel->getLayoutList()); @@ -39,10 +39,10 @@ class communicationAdminView extends communication Context::set('editor_skin_list', $oEditorModel->getEditorSkinList()); // get a list of communication skins - Context::set('communication_skin_list', $oModuleModel->getSkins($this->module_path)); + Context::set('skin_list', $oModuleModel->getSkins($this->module_path)); // get a list of communication skins - Context::set('communication_mobile_skin_list', $oModuleModel->getSkins($this->module_path, 'm.skins')); + Context::set('mobile_skin_list', $oModuleModel->getSkins($this->module_path, 'm.skins')); // Get a layout list $layout_list = $oLayoutModel->getLayoutList(); @@ -52,11 +52,11 @@ class communicationAdminView extends communication Context::set('mlayout_list', $mlayout_list); $security = new Security(); - $security->encodeHTML('communication_config..'); + $security->encodeHTML('config..'); $security->encodeHTML('layout_list..'); $security->encodeHTML('editor_skin_list..'); - $security->encodeHTML('communication_skin_list..title'); - $security->encodeHTML('communication_mobile_skin_list..title'); + $security->encodeHTML('skin_list..title'); + $security->encodeHTML('mobile_skin_list..title'); $oMemberModel = getModel('member'); $group_list = $oMemberModel->getGroups($this->site_srl); diff --git a/modules/communication/communication.controller.php b/modules/communication/communication.controller.php index 248f04632..65c4295cb 100644 --- a/modules/communication/communication.controller.php +++ b/modules/communication/communication.controller.php @@ -89,7 +89,7 @@ class communicationController extends communication $oCommunicationModel = getModel('communication'); $config = $oCommunicationModel->getConfig(); - if(!$oCommunicationModel->checkGrant($config->grant_write)) + if(!$oCommunicationModel->checkWriteGrant($config->grant_write)) { return new Object(-1, 'msg_not_permitted'); } @@ -784,31 +784,40 @@ class communicationController extends communication return new Object(); } + $logged_info = Context::get('logged_info'); $oCommunicationModel = getModel('communication'); $config = $oCommunicationModel->getConfig(); - - if($config->member_menu != 'Y') + + if($config->enable_message == 'N' && $config->enable_friend == 'N') { return new Object(); } - - $logged_info = Context::get('logged_info'); // Add menus on the member login information $oMemberController = getController('member'); - $oMemberController->addMemberMenu('dispCommunicationFriend', 'cmd_view_friend'); - $oMemberController->addMemberMenu('dispCommunicationMessages', 'cmd_view_message_box'); - - $flag_file = './files/member_extra_info/new_message_flags/' . getNumberingPath($logged_info->member_srl) . $logged_info->member_srl; - if(file_exists($flag_file)) + if($config->enable_message == 'Y') { - // Pop-up to display messages if a flag on new message is set - $new_message_count = (int) trim(FileHandler::readFile($flag_file)); - $text = preg_replace('@\r?\n@', '\\n', addslashes(Context::getLang('alert_new_message_arrived'))); - Context::addHtmlFooter(""); - Context::loadFile(array('./modules/communication/tpl/js/member_communication.js'), true); - - FileHandler::removeFile($flag_file); + $oMemberController->addMemberMenu('dispCommunicationMessages', 'cmd_view_message_box'); + } + + if($config->enable_friend == 'Y') + { + $oMemberController->addMemberMenu('dispCommunicationFriend', 'cmd_view_friend'); + } + + if($config->enable_message == 'Y') + { + $flag_file = './files/member_extra_info/new_message_flags/' . getNumberingPath($logged_info->member_srl) . $logged_info->member_srl; + if(file_exists($flag_file)) + { + // Pop-up to display messages if a flag on new message is set + $new_message_count = (int) trim(FileHandler::readFile($flag_file)); + $text = preg_replace('@\r?\n@', '\\n', addslashes(Context::getLang('alert_new_message_arrived'))); + Context::addHtmlFooter(""); + Context::loadFile(array('./modules/communication/tpl/js/member_communication.js'), true); + + FileHandler::removeFile($flag_file); + } } } @@ -821,12 +830,12 @@ class communicationController extends communication $oCommunicationModel = getModel('communication'); $config = $oCommunicationModel->getConfig(); - - if($config->member_menu != 'Y') + + if($config->enable_message == 'N' && $config->enable_friend == 'N') { return new Object(); } - + $mid = Context::get('cur_mid'); $member_srl = Context::get('target_srl'); $logged_info = Context::get('logged_info'); @@ -836,11 +845,16 @@ class communicationController extends communication if($logged_info->member_srl == $member_srl) { // Add your own viewing Note Template - $oMemberController->addMemberPopupMenu(getUrl('', 'mid', $mid, 'act', 'dispCommunicationMessages'), 'cmd_view_message_box', '', 'self'); + if($config->enable_message == 'Y') + { + $oMemberController->addMemberPopupMenu(getUrl('', 'mid', $mid, 'act', 'dispCommunicationMessages'), 'cmd_view_message_box', '', 'self'); + } // Display a list of friends - $oMemberController->addMemberPopupMenu(getUrl('', 'mid', $mid, 'act', 'dispCommunicationFriend'), 'cmd_view_friend', '', 'self'); - + if($config->enable_friend == 'Y') + { + $oMemberController->addMemberPopupMenu(getUrl('', 'mid', $mid, 'act', 'dispCommunicationFriend'), 'cmd_view_friend', '', 'self'); + } } // If not, Add menus to send message and to add friends else @@ -854,13 +868,13 @@ class communicationController extends communication } // Add a menu for sending message - if($logged_info->is_admin == 'Y' || $target_member_info->allow_message == 'Y' || ($target_member_info->allow_message == 'F' && $oCommunicationModel->isFriend($member_srl))) + if($config->enable_message == 'Y' && ($logged_info->is_admin == 'Y' || $target_member_info->allow_message == 'Y' || ($target_member_info->allow_message == 'F' && $oCommunicationModel->isFriend($member_srl)))) { $oMemberController->addMemberPopupMenu(getUrl('', 'mid', $mid, 'act', 'dispCommunicationSendMessage', 'receiver_srl', $member_srl), 'cmd_send_message', '', 'popup'); } // Add a menu for listing friends (if a friend is new) - if(!$oCommunicationModel->isAddedFriend($member_srl)) + if($config->enable_friend == 'Y' && !$oCommunicationModel->isAddedFriend($member_srl)) { $oMemberController->addMemberPopupMenu(getUrl('', 'mid', $mid, 'act', 'dispCommunicationAddFriend', 'target_srl', $member_srl), 'cmd_add_friend', '', 'popup'); } diff --git a/modules/communication/communication.model.php b/modules/communication/communication.model.php index 753bb2132..0055b9421 100644 --- a/modules/communication/communication.model.php +++ b/modules/communication/communication.model.php @@ -25,44 +25,44 @@ class communicationModel extends communication function getConfig() { $oModuleModel = getModel('module'); - $communication_config = $oModuleModel->getModuleConfig('communication'); + $config = $oModuleModel->getModuleConfig('communication'); - if(!is_object($communication_config)) + if(!$config->skin) { - $communication_config = new stdClass(); + $config->skin = 'default'; } - if(!$communication_config->skin) + if(!$config->colorset) { - $communication_config->skin = 'default'; + $config->colorset = 'white'; } - if(!$communication_config->colorset) + if(!$config->editor_skin) { - $communication_config->colorset = 'white'; + $config->editor_skin = 'ckeditor'; } - if(!$communication_config->editor_skin) + if(!$config->mskin) { - $communication_config->editor_skin = 'ckeditor'; + $config->mskin = 'default'; } - if(!$communication_config->mskin) + if(!$config->grant_write) { - $communication_config->mskin = 'default'; + $config->grant_write = array('default_grant' => 'member'); } - if(!$communication_config->grant_write) + if(!$config->enable_message) { - $communication_config->grant_write = array('default_grant' => 'member'); + $config->enable_message = 'Y'; + } + + if(!$config->enable_friend) + { + $config->enable_friend = 'Y'; } - if(!$communication_config->member_menu) - { - $communication_config->member_menu = 'Y'; - } - - return $communication_config; + return $config; } /** @@ -74,7 +74,7 @@ class communicationModel extends communication function getGrantArray($default, $group) { $grant = array(); - if($default!="") + if($default) { switch($default) { @@ -91,65 +91,51 @@ class communicationModel extends communication } else if(is_array($group)) { - $oMemberModel = getModel('member'); - $group_list = $oMemberModel->getGroups($this->site_srl); - $group_grant = array(); foreach($group as $group_srl) { - $group_grant[$group_srl] = $group_list[$group_srl]->title; + $group_grant[$group_srl] = true; } - $grant = array('group_grant'=>$group_grant); + + $grant = array('group_grant' => $group_grant); } + return $grant; } /** - * @brief check member's grant - * @param object $member_info + * @brief Check Write Grant * @param array $arrGrant * @return boolean */ - function checkGrant($arrGrant) + function checkWriteGrant($arrGrant) { - if(!$arrGrant) - return false; - - if(!Context::get('is_logged')) - return false; + if(!$arrGrant) return false; $logged_info = Context::get('logged_info'); - if($logged_info->is_admin == "Y") - return true; + if($logged_info->is_admin == "Y") return true; if($arrGrant['default_grant']) { - if($arrGrant['default_grant'] == "member" && $logged_info) - return true; - - if($arrGrant['default_grant'] == "site" && $this->site_srl == $logged_info->site_srl) - return true; - - if($arrGrant['default_grant'] == "manager" && $logged_info->is_admin == "Y") - return true; + if($arrGrant['default_grant'] == "member" && Context::get('is_logged')) return true; + + if($arrGrant['default_grant'] == "site" && $this->site_srl == $logged_info->site_srl) return true; + + if($arrGrant['default_grant'] == "manager" && $logged_info->is_admin == "Y") return true; } if($arrGrant['group_grant']) { $group_grant = $arrGrant['group_grant']; - if(!is_array($group_grant)) - return false; + if(!is_array($group_grant)) return false; - foreach($logged_info->group_list as $group_srl=>$title) + foreach($logged_info->group_list as $group_srl => $title) { - if(isset($group_grant[$group_srl])&&$group_grant[$group_srl]==$title) - return true; + if($group_grant[$group_srl]) return true; } - } return false; - } /** diff --git a/modules/communication/lang/en.php b/modules/communication/lang/en.php index 4289cec4c..2c323f412 100644 --- a/modules/communication/lang/en.php +++ b/modules/communication/lang/en.php @@ -32,6 +32,8 @@ $lang->msg_disallow_message = 'Failed to send a message because the recipient bl $lang->about_allow_message = 'You can set whether to receive messages or not.'; $lang->message_notice = 'Send a message to the author about this. If you don\'t write a message, it is not sent.'; $lang->friends_page_does_not_support = 'Friends in a mobile environment is not supported. Please go to the PC page.'; -$lang->cmd_write_communication = 'Write Grant'; +$lang->communication_send_message_grant = 'Send Message Grant'; $lang->cmd_manage_base = 'Basic infomation'; $lang->alert_new_message_arrived = 'You have %d new message(s). Do you want to check it now?'; +$lang->enable_communication_friend = 'Friend Enable'; +$lang->enable_communication_message = 'Message Enable'; \ No newline at end of file diff --git a/modules/communication/lang/ko.php b/modules/communication/lang/ko.php index fc7b22f74..fa0be5d8d 100644 --- a/modules/communication/lang/ko.php +++ b/modules/communication/lang/ko.php @@ -32,6 +32,8 @@ $lang->msg_disallow_message = '쪽지 수신을 거부한 사용자라서 쪽지 $lang->about_allow_message = '쪽지 수신 여부를 결정할 수 있습니다.'; $lang->message_notice = '저작자에게 쪽지를 발송해서 이 사실을 알립니다. 작성하지 않으면 발송하지 않습니다.'; $lang->friends_page_does_not_support = '모바일 환경에서는 친구 보기 페이지를 지원하지 않습니다. PC 화면으로 이동하세요.'; -$lang->cmd_write_communication = '작성 권한'; +$lang->communication_send_message_grant = '쪽지 발송 권한'; $lang->cmd_manage_base = '기본 정보'; $lang->alert_new_message_arrived = '%d개의 새로운 메시지가 도착하였습니다. 확인하시겠습니까?'; +$lang->enable_communication_friend = '친구기능 사용'; +$lang->enable_communication_message = '쪽지기능 사용'; \ No newline at end of file diff --git a/modules/communication/tpl/index.html b/modules/communication/tpl/index.html index da496c9b0..c89cbd30d 100644 --- a/modules/communication/tpl/index.html +++ b/modules/communication/tpl/index.html @@ -20,18 +20,37 @@ +
+
{$lang->enable_communication_message}
+
+ + +
+
+
+
{$lang->enable_communication_friend}
+
+ + +
+
- +
@@ -40,7 +59,7 @@
@@ -48,7 +67,7 @@
@@ -63,7 +82,7 @@
@@ -71,7 +90,7 @@
@@ -81,39 +100,32 @@
-
- +
-
- -
- -
-
From 5d3389a6ff0fe1c7d902ed79e2fb6427e465faca Mon Sep 17 00:00:00 2001 From: conory Date: Sat, 5 Mar 2016 14:18:50 +0900 Subject: [PATCH 007/205] =?UTF-8?q?=EC=AA=BD=EC=A7=80=20=EB=B0=9C=EC=86=A1?= =?UTF-8?q?=20=EA=B6=8C=ED=95=9C=20=EC=84=A4=EC=A0=95=20=EB=8B=A4=EB=93=AC?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../communication.admin.controller.php | 33 ++-------- .../communication.controller.php | 2 +- modules/communication/communication.model.php | 60 ++++++++----------- modules/communication/tpl/index.html | 29 +++++---- 4 files changed, 50 insertions(+), 74 deletions(-) diff --git a/modules/communication/communication.admin.controller.php b/modules/communication/communication.admin.controller.php index 0b52ad2b2..fd05dcb35 100644 --- a/modules/communication/communication.admin.controller.php +++ b/modules/communication/communication.admin.controller.php @@ -24,39 +24,14 @@ class communicationAdminController extends communication function procCommunicationAdminInsertConfig() { // get the default information - $args = Context::gets('enable_message', 'enable_friend', 'skin', 'colorset', 'editor_skin', 'sel_editor_colorset', 'mskin', 'mcolorset', 'layout_srl', 'mlayout_srl', 'grant_write_default','grant_write_group'); + $args = Context::gets('enable_message', 'enable_friend', 'skin', 'colorset', 'editor_skin', 'sel_editor_colorset', 'mskin', 'mcolorset', 'layout_srl', 'mlayout_srl', 'grant_send_default','grant_send_group'); $args->editor_colorset = $args->sel_editor_colorset; unset($args->sel_editor_colorset); - if(!$args->skin) - { - $args->skin = 'default'; - } - - if(!$args->colorset) - { - $args->colorset = 'white'; - } - - if(!$args->editor_skin) - { - $args->editor_skin = 'default'; - } - - if(!$args->mskin) - { - $args->mskin = 'default'; - } - - if(!$args->layout_srl) - { - $args->layout_srl = NULL; - } - $oCommunicationModel = getModel('communication'); - $args->grant_write = $oCommunicationModel->getGrantArray($args->grant_write_default, $args->grant_write_group); - unset($args->grant_write_default); - unset($args->grant_write_group); + $args->grant_send = $oCommunicationModel->getGrantArray($args->grant_send_default, $args->grant_send_group); + unset($args->grant_send_default); + unset($args->grant_send_group); // create the module module Controller object $oModuleController = getController('module'); diff --git a/modules/communication/communication.controller.php b/modules/communication/communication.controller.php index 65c4295cb..b45dd42e6 100644 --- a/modules/communication/communication.controller.php +++ b/modules/communication/communication.controller.php @@ -89,7 +89,7 @@ class communicationController extends communication $oCommunicationModel = getModel('communication'); $config = $oCommunicationModel->getConfig(); - if(!$oCommunicationModel->checkWriteGrant($config->grant_write)) + if(!$oCommunicationModel->checkGrant($config->grant_send)) { return new Object(-1, 'msg_not_permitted'); } diff --git a/modules/communication/communication.model.php b/modules/communication/communication.model.php index 0055b9421..b4bb6974c 100644 --- a/modules/communication/communication.model.php +++ b/modules/communication/communication.model.php @@ -46,10 +46,10 @@ class communicationModel extends communication { $config->mskin = 'default'; } - - if(!$config->grant_write) + + if(!$config->grant_send) { - $config->grant_write = array('default_grant' => 'member'); + $config->grant_send = array('default' => 'member'); } if(!$config->enable_message) @@ -76,62 +76,54 @@ class communicationModel extends communication $grant = array(); if($default) { - switch($default) - { - case "-2": - $grant = array("default_grant"=>"site"); - break; - case "-3": - $grant = array("default_grant"=>"manager"); - break; - default : - $grant = array("default_grant"=>"member"); - break; - } - } + $grant = array('default' => $default); + } else if(is_array($group)) { - $group_grant = array(); + $grant_group = array(); foreach($group as $group_srl) { - $group_grant[$group_srl] = true; + $grant_group[$group_srl] = true; } - $grant = array('group_grant' => $group_grant); + $grant = array('group' => $grant_group); } return $grant; } /** - * @brief Check Write Grant + * @brief Check Grant * @param array $arrGrant * @return boolean */ - function checkWriteGrant($arrGrant) + function checkGrant($arrGrant) { if(!$arrGrant) return false; $logged_info = Context::get('logged_info'); - if($logged_info->is_admin == "Y") return true; + if($logged_info->is_admin == 'Y') return true; - if($arrGrant['default_grant']) + if($arrGrant['default']) { - if($arrGrant['default_grant'] == "member" && Context::get('is_logged')) return true; - - if($arrGrant['default_grant'] == "site" && $this->site_srl == $logged_info->site_srl) return true; - - if($arrGrant['default_grant'] == "manager" && $logged_info->is_admin == "Y") return true; + if($arrGrant['default'] == 'member') + { + if(Context::get('is_logged')) return true; + } + else if($arrGrant['default'] == 'site') + { + if($this->site_srl == $logged_info->site_srl) return true; + } + else if($arrGrant['default'] == 'manager') + { + if($logged_info->is_admin == 'Y') return true; + } } - - if($arrGrant['group_grant']) + else if(is_array($arrGrant['group'])) { - $group_grant = $arrGrant['group_grant']; - if(!is_array($group_grant)) return false; - foreach($logged_info->group_list as $group_srl => $title) { - if($group_grant[$group_srl]) return true; + if(isset($arrGrant['group'][$group_srl])) return true; } } diff --git a/modules/communication/tpl/index.html b/modules/communication/tpl/index.html index c89cbd30d..4f3909820 100644 --- a/modules/communication/tpl/index.html +++ b/modules/communication/tpl/index.html @@ -101,18 +101,27 @@
- +
- + + + + - From cb206acfc0438f8a4a983e315269a5289849f2a0 Mon Sep 17 00:00:00 2001 From: conory Date: Sat, 5 Mar 2016 17:45:51 +0900 Subject: [PATCH 008/205] =?UTF-8?q?=EC=83=88=20=EC=AA=BD=EC=A7=80=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=20=EB=AC=B8=EC=A0=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../communication.controller.php | 56 ++++++++++++---- modules/communication/communication.view.php | 66 ++++++++++++------- .../tpl/js/member_communication.js | 11 +--- 3 files changed, 87 insertions(+), 46 deletions(-) diff --git a/modules/communication/communication.controller.php b/modules/communication/communication.controller.php index b45dd42e6..289096a3b 100644 --- a/modules/communication/communication.controller.php +++ b/modules/communication/communication.controller.php @@ -254,15 +254,17 @@ class communicationController extends communication $oDB->rollback(); return $trigger_output; } - + + $oDB->commit(); + // create a flag that message is sent (in file format) $flag_path = './files/member_extra_info/new_message_flags/' . getNumberingPath($receiver_srl); FileHandler::makeDir($flag_path); $flag_file = sprintf('%s%s', $flag_path, $receiver_srl); - $flag_count = FileHandler::readFile($flag_file); - FileHandler::writeFile($flag_file, ++$flag_count); - - $oDB->commit(); + + $oCommunicationModel = getModel('communication'); + $new_message_count = $oCommunicationModel->getNewMessageCount($receiver_srl); + FileHandler::writeFile($flag_file, $new_message_count); return new Object(0, 'success_sended'); } @@ -771,10 +773,30 @@ class communicationController extends communication */ function setMessageReaded($message_srl) { - $args = new stdClass(); + $args = new stdClass; $args->message_srl = $message_srl; $args->related_srl = $message_srl; - return executeQuery('communication.setMessageReaded', $args); + $output = executeQuery('communication.setMessageReaded', $args); + + // Renew a flag + $logged_info = Context::get('logged_info'); + $flag_path = './files/member_extra_info/new_message_flags/' . getNumberingPath($logged_info->member_srl); + $flag_file = sprintf('%s%s', $flag_path, $logged_info->member_srl); + if(file_exists($flag_file)) + { + $oCommunicationModel = getModel('communication'); + $new_message_count = $oCommunicationModel->getNewMessageCount(); + if($new_message_count > 0) + { + FileHandler::writeFile($flag_file, $new_message_count); + } + else + { + FileHandler::removeFile($flag_file); + } + } + + return $output; } function triggerModuleHandlerBefore($obj) @@ -805,18 +827,24 @@ class communicationController extends communication $oMemberController->addMemberMenu('dispCommunicationFriend', 'cmd_view_friend'); } - if($config->enable_message == 'Y') + if($config->enable_message == 'Y' && $obj->act != 'dispCommunicationNewMessage') { - $flag_file = './files/member_extra_info/new_message_flags/' . getNumberingPath($logged_info->member_srl) . $logged_info->member_srl; + $flag_path = './files/member_extra_info/new_message_flags/' . getNumberingPath($logged_info->member_srl); + $flag_file = sprintf('%s%s', $flag_path, $logged_info->member_srl); if(file_exists($flag_file)) { // Pop-up to display messages if a flag on new message is set $new_message_count = (int) trim(FileHandler::readFile($flag_file)); - $text = preg_replace('@\r?\n@', '\\n', addslashes(Context::getLang('alert_new_message_arrived'))); - Context::addHtmlFooter(""); - Context::loadFile(array('./modules/communication/tpl/js/member_communication.js'), true); - - FileHandler::removeFile($flag_file); + if($new_message_count > 0) + { + $text = preg_replace('@\r?\n@', '\\n', addslashes(Context::getLang('alert_new_message_arrived'))); + Context::addHtmlFooter(""); + Context::loadFile(array('./modules/communication/tpl/js/member_communication.js'), true); + } + else + { + FileHandler::removeFile($flag_file); + } } } } diff --git a/modules/communication/communication.view.php b/modules/communication/communication.view.php index 23c008524..ff3c10dc2 100644 --- a/modules/communication/communication.view.php +++ b/modules/communication/communication.view.php @@ -17,10 +17,10 @@ class communicationView extends communication { $oCommunicationModel = getModel('communication'); - $this->communication_config = $oCommunicationModel->getConfig(); - $skin = $this->communication_config->skin; + $this->config = $oCommunicationModel->getConfig(); + $skin = $this->config->skin; - Context::set('communication_config', $this->communication_config); + Context::set('communication_config', $this->config); $config_parse = explode('|@|', $skin); @@ -36,10 +36,10 @@ class communicationView extends communication $this->setTemplatePath($tpl_path); $oLayoutModel = getModel('layout'); - $layout_info = $oLayoutModel->getLayout($this->communication_config->layout_srl); + $layout_info = $oLayoutModel->getLayout($this->config->layout_srl); if($layout_info) { - $this->module_info->layout_srl = $this->communication_config->layout_srl; + $this->module_info->layout_srl = $this->config->layout_srl; $this->setLayoutPath($layout_info->path); } } @@ -50,6 +50,11 @@ class communicationView extends communication */ function dispCommunicationMessages() { + if($this->config->enable_message == 'N') + { + return $this->stop('msg_invalid_request'); + } + // Error appears if not logged-in if(!Context::get('is_logged')) { @@ -58,11 +63,6 @@ class communicationView extends communication $logged_info = Context::get('logged_info'); - if(!array_key_exists('dispCommunicationMessages', $logged_info->menu_list)) - { - return $this->stop('msg_invalid_request'); - } - // Set the variables $message_srl = Context::get('message_srl'); $message_type = Context::get('message_type'); @@ -138,6 +138,11 @@ class communicationView extends communication $this->setLayoutPath('./common/tpl/'); $this->setLayoutFile('popup_layout'); + if($this->config->enable_message == 'N') + { + return $this->stop('msg_invalid_request'); + } + // Error appears if not logged-in if(!Context::get('is_logged')) { @@ -157,11 +162,6 @@ class communicationView extends communication Context::set('message', $message); } - // Delete a flag - $flag_path = './files/communication_extra_info/new_message_flags/' . getNumberingPath($logged_info->member_srl); - $flag_file = sprintf('%s%s', $flag_path, $logged_info->member_srl); - FileHandler::removeFile($flag_file); - $this->setTemplateFile('new_message'); } @@ -174,9 +174,11 @@ class communicationView extends communication $this->setLayoutPath('./common/tpl/'); $this->setLayoutFile("popup_layout"); - $oCommunicationModel = getModel('communication'); - $oMemberModel = getModel('member'); - + if($this->config->enable_message == 'N') + { + return $this->stop('msg_invalid_request'); + } + // Error appears if not logged-in if(!Context::get('is_logged')) { @@ -199,6 +201,9 @@ class communicationView extends communication return $this->stop('msg_cannot_send_to_yourself'); } + $oCommunicationModel = getModel('communication'); + $oMemberModel = getModel('member'); + // get message_srl of the original message if it is a reply $message_srl = Context::get('message_srl'); if($message_srl) @@ -235,8 +240,8 @@ class communicationView extends communication $option->resizable = FALSE; $option->disable_html = TRUE; $option->height = 300; - $option->skin = $this->communication_config->editor_skin; - $option->colorset = $this->communication_config->editor_colorset; + $option->skin = $this->config->editor_skin; + $option->colorset = $this->config->editor_colorset; $editor = $oEditorModel->getEditor($logged_info->member_srl, $option); Context::set('editor', $editor); @@ -249,12 +254,17 @@ class communicationView extends communication */ function dispCommunicationFriend() { + if($this->config->enable_friend == 'N') + { + return $this->stop('msg_invalid_request'); + } + // Error appears if not logged-in if(!Context::get('is_logged')) { return $this->stop('msg_not_logged'); } - + $oCommunicationModel = getModel('communication'); // get a group list @@ -307,7 +317,12 @@ class communicationView extends communication { $this->setLayoutPath('./common/tpl/'); $this->setLayoutFile("popup_layout"); - + + if($this->config->enable_friend == 'N') + { + return $this->stop('msg_invalid_request'); + } + // error appears if not logged-in if(!Context::get('is_logged')) { @@ -349,7 +364,12 @@ class communicationView extends communication { $this->setLayoutPath('./common/tpl/'); $this->setLayoutFile("popup_layout"); - + + if($this->config->enable_friend == 'N') + { + return $this->stop('msg_invalid_request'); + } + // error apprears if not logged-in if(!Context::get('is_logged')) { diff --git a/modules/communication/tpl/js/member_communication.js b/modules/communication/tpl/js/member_communication.js index 78f6501e2..e0e71d1a3 100644 --- a/modules/communication/tpl/js/member_communication.js +++ b/modules/communication/tpl/js/member_communication.js @@ -11,16 +11,9 @@ }) .prependTo(document.body); } + text = text.replace('%d', count); - var cur_module = current_url.getQuery('module'); - if( cur_module == "admin" ) - h = $bar.html('

'+text+'

').height(); - else - h = $bar.html('

'+text+'

').height(); + $bar.html('

'+text+'

').height(); $bar.show().animate({top:0}); - // hide after 10 seconds - setTimeout(function(){ - $bar.slideUp(); - }, 5000); }; })(jQuery); From 622a6d932496d6ef3110c6002fe0bacfb68e1ce5 Mon Sep 17 00:00:00 2001 From: conory Date: Sat, 5 Mar 2016 18:03:28 +0900 Subject: [PATCH 009/205] =?UTF-8?q?=EC=95=8C=EB=A6=BC=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=20=ED=81=B4=EB=A6=AD=EC=8B=9C=202=EC=B4=88=20?= =?UTF-8?q?=ED=9B=84=EC=97=90=20=EB=A9=94=EC=84=B8=EC=A7=80=20=EB=8B=AB?= =?UTF-8?q?=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/communication/tpl/js/member_communication.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/communication/tpl/js/member_communication.js b/modules/communication/tpl/js/member_communication.js index e0e71d1a3..ae6f8c013 100644 --- a/modules/communication/tpl/js/member_communication.js +++ b/modules/communication/tpl/js/member_communication.js @@ -1,6 +1,6 @@ (function($){ + var $bar; window.xeNotifyMessage = function(text, count){ - var $bar; $bar = $('div.message.info'); if(!$bar.length) { $bar = $('
') @@ -13,7 +13,13 @@ } text = text.replace('%d', count); - $bar.html('

'+text+'

').height(); + $bar.html('

'+text+'

').height(); $bar.show().animate({top:0}); }; + + window.xeNotifyMessageClose = function(){ + setTimeout(function(){ + $bar.slideUp(); + }, 2000); + }; })(jQuery); From f36724a96f9a94485b66b6a456bfb5a72fe4f4d2 Mon Sep 17 00:00:00 2001 From: YJSoft Date: Sun, 6 Mar 2016 18:14:25 +0900 Subject: [PATCH 010/205] =?UTF-8?q?=EB=B2=84=EC=A0=84=20=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/nfl/jquery-oembed-all 에서 가져옴. 코어에서 가져왔던 버전은 조금 구버전이라 2015년 8월 버전으로 가져옵니다. --- addons/oembed/jquery.oembed.js | 1969 ++++++++++++++++---------------- 1 file changed, 957 insertions(+), 1012 deletions(-) diff --git a/addons/oembed/jquery.oembed.js b/addons/oembed/jquery.oembed.js index dd17f4841..07ed993a5 100755 --- a/addons/oembed/jquery.oembed.js +++ b/addons/oembed/jquery.oembed.js @@ -5,1054 +5,999 @@ * Licensed under the MIT license * * Orignal Author: Richard Chamorro - * Forked by Andrew Mee to Provide a slightly diffent kind of embedding - * experience - * - * Modified : NAVER Corp. + * Forked by Andrew Mee to Provide a slightly diffent kind of embedding experience */ (function ($) { - $.fn.oembed = function (url, options, embedAction) { - settings = $.extend(true, $.fn.oembed.defaults, options); - var shortURLList = ["0rz.tw", "1link.in", "1url.com", "2.gp", "2big.at", "2tu.us", "3.ly", "307.to", "4ms.me", "4sq.com", "4url.cc", "6url.com", "7.ly", "a.gg", "a.nf", "aa.cx", "abcurl.net", - "ad.vu", "adf.ly", "adjix.com", "afx.cc", "all.fuseurl.com", "alturl.com", "amzn.to", "ar.gy", "arst.ch", "atu.ca", "azc.cc", "b23.ru", "b2l.me", "bacn.me", "bcool.bz", "binged.it", - "bit.ly", "bizj.us", "bloat.me", "bravo.ly", "bsa.ly", "budurl.com", "canurl.com", "chilp.it", "chzb.gr", "cl.lk", "cl.ly", "clck.ru", "cli.gs", "cliccami.info", - "clickthru.ca", "clop.in", "conta.cc", "cort.as", "cot.ag", "crks.me", "ctvr.us", "cutt.us", "dai.ly", "decenturl.com", "dfl8.me", "digbig.com", - "http:\/\/digg\.com\/[^\/]+$", "disq.us", "dld.bz", "dlvr.it", "do.my", "doiop.com", "dopen.us", "easyuri.com", "easyurl.net", "eepurl.com", "eweri.com", - "fa.by", "fav.me", "fb.me", "fbshare.me", "ff.im", "fff.to", "fire.to", "firsturl.de", "firsturl.net", "flic.kr", "flq.us", "fly2.ws", "fon.gs", "freak.to", - "fuseurl.com", "fuzzy.to", "fwd4.me", "fwib.net", "g.ro.lt", "gizmo.do", "gl.am", "go.9nl.com", "go.ign.com", "go.usa.gov", "goo.gl", "goshrink.com", "gurl.es", - "hex.io", "hiderefer.com", "hmm.ph", "href.in", "hsblinks.com", "htxt.it", "huff.to", "hulu.com", "hurl.me", "hurl.ws", "icanhaz.com", "idek.net", "ilix.in", "is.gd", - "its.my", "ix.lt", "j.mp", "jijr.com", "kl.am", "klck.me", "korta.nu", "krunchd.com", "l9k.net", "lat.ms", "liip.to", "liltext.com", "linkbee.com", "linkbun.ch", - "liurl.cn", "ln-s.net", "ln-s.ru", "lnk.gd", "lnk.ms", "lnkd.in", "lnkurl.com", "lru.jp", "lt.tl", "lurl.no", "macte.ch", "mash.to", "merky.de", "migre.me", "miniurl.com", - "minurl.fr", "mke.me", "moby.to", "moourl.com", "mrte.ch", "myloc.me", "myurl.in", "n.pr", "nbc.co", "nblo.gs", "nn.nf", "not.my", "notlong.com", "nsfw.in", - "nutshellurl.com", "nxy.in", "nyti.ms", "o-x.fr", "oc1.us", "om.ly", "omf.gd", "omoikane.net", "on.cnn.com", "on.mktw.net", "onforb.es", "orz.se", "ow.ly", "ping.fm", - "pli.gs", "pnt.me", "politi.co", "post.ly", "pp.gg", "profile.to", "ptiturl.com", "pub.vitrue.com", "qlnk.net", "qte.me", "qu.tc", "qy.fi", "r.ebay.com", "r.im", "rb6.me", "read.bi", - "readthis.ca", "reallytinyurl.com", "redir.ec", "redirects.ca", "redirx.com", "retwt.me", "ri.ms", "rickroll.it", "riz.gd", "rt.nu", "ru.ly", "rubyurl.com", "rurl.org", - "rww.tw", "s4c.in", "s7y.us", "safe.mn", "sameurl.com", "sdut.us", "shar.es", "shink.de", "shorl.com", "short.ie", "short.to", "shortlinks.co.uk", "shorturl.com", - "shout.to", "show.my", "shrinkify.com", "shrinkr.com", "shrt.fr", "shrt.st", "shrten.com", "shrunkin.com", "simurl.com", "slate.me", "smallr.com", "smsh.me", "smurl.name", - "sn.im", "snipr.com", "snipurl.com", "snurl.com", "sp2.ro", "spedr.com", "srnk.net", "srs.li", "starturl.com", "stks.co", "su.pr", "surl.co.uk", "surl.hu", "t.cn", "t.co", "t.lh.com", - "ta.gd", "tbd.ly", "tcrn.ch", "tgr.me", "tgr.ph", "tighturl.com", "tiniuri.com", "tiny.cc", "tiny.ly", "tiny.pl", "tinylink.in", "tinyuri.ca", "tinyurl.com", "tk.", "tl.gd", - "tmi.me", "tnij.org", "tnw.to", "tny.com", "to.ly", "togoto.us", "totc.us", "toysr.us", "tpm.ly", "tr.im", "tra.kz", "trunc.it", "twhub.com", "twirl.at", - "twitclicks.com", "twitterurl.net", "twitterurl.org", "twiturl.de", "twurl.cc", "twurl.nl", "u.mavrev.com", "u.nu", "u76.org", "ub0.cc", "ulu.lu", "updating.me", "ur1.ca", - "url.az", "url.co.uk", "url.ie", "url360.me", "url4.eu", "urlborg.com", "urlbrief.com", "urlcover.com", "urlcut.com", "urlenco.de", "urli.nl", "urls.im", - "urlshorteningservicefortwitter.com", "urlx.ie", "urlzen.com", "usat.ly", "use.my", "vb.ly", "vevo.ly", "vgn.am", "vl.am", "vm.lc", "w55.de", "wapo.st", "wapurl.co.uk", "wipi.es", - "wp.me", "x.vu", "xr.com", "xrl.in", "xrl.us", "xurl.es", "xurl.jp", "y.ahoo.it", "yatuc.com", "ye.pe", "yep.it", "yfrog.com", "yhoo.it", "yiyd.com", "youtu.be", "yuarel.com", - "z0p.de", "zi.ma", "zi.mu", "zipmyurl.com", "zud.me", "zurl.ws", "zz.gd", "zzang.kr", "›.ws", "✩.ws", "✿.ws", "❥.ws", "➔.ws", "➞.ws", "➡.ws", "➨.ws", "➯.ws", "➹.ws", "➽.ws" - ]; + $.fn.oembed = function (url, options, embedAction) { - if($('#jqoembeddata').length === 0) $('').appendTo('body'); + settings = $.extend(true, $.fn.oembed.defaults, options); + var shortURLList = ["0rz.tw", "1link.in", "1url.com", "2.gp", "2big.at", "2tu.us", "3.ly", "307.to", "4ms.me", "4sq.com", "4url.cc", "6url.com", "7.ly", "a.gg", "a.nf", "aa.cx", "abcurl.net", + "ad.vu", "adf.ly", "adjix.com", "afx.cc", "all.fuseurl.com", "alturl.com", "amzn.to", "ar.gy", "arst.ch", "atu.ca", "azc.cc", "b23.ru", "b2l.me", "bacn.me", "bcool.bz", "binged.it", + "bit.ly", "bizj.us", "bloat.me", "bravo.ly", "bsa.ly", "budurl.com", "canurl.com", "chilp.it", "chzb.gr", "cl.lk", "cl.ly", "clck.ru", "cli.gs", "cliccami.info", + "clickthru.ca", "clop.in", "conta.cc", "cort.as", "cot.ag", "crks.me", "ctvr.us", "cutt.us", "dai.ly", "decenturl.com", "dfl8.me", "digbig.com", + "http:\/\/digg\.com\/[^\/]+$", "disq.us", "dld.bz", "dlvr.it", "do.my", "doiop.com", "dopen.us", "easyuri.com", "easyurl.net", "eepurl.com", "eweri.com", + "fa.by", "fav.me", "fb.me", "fbshare.me", "ff.im", "fff.to", "fire.to", "firsturl.de", "firsturl.net", "flic.kr", "flq.us", "fly2.ws", "fon.gs", "freak.to", + "fuseurl.com", "fuzzy.to", "fwd4.me", "fwib.net", "g.ro.lt", "gizmo.do", "gl.am", "go.9nl.com", "go.ign.com", "go.usa.gov", "goo.gl", "goshrink.com", "gurl.es", + "hex.io", "hiderefer.com", "hmm.ph", "href.in", "hsblinks.com", "htxt.it", "huff.to", "hulu.com", "hurl.me", "hurl.ws", "icanhaz.com", "idek.net", "ilix.in", "is.gd", + "its.my", "ix.lt", "j.mp", "jijr.com", "kl.am", "klck.me", "korta.nu", "krunchd.com", "l9k.net", "lat.ms", "liip.to", "liltext.com", "linkbee.com", "linkbun.ch", + "liurl.cn", "ln-s.net", "ln-s.ru", "lnk.gd", "lnk.ms", "lnkd.in", "lnkurl.com", "lru.jp", "lt.tl", "lurl.no", "macte.ch", "mash.to", "merky.de", "migre.me", "miniurl.com", + "minurl.fr", "mke.me", "moby.to", "moourl.com", "mrte.ch", "myloc.me", "myurl.in", "n.pr", "nbc.co", "nblo.gs", "nn.nf", "not.my", "notlong.com", "nsfw.in", + "nutshellurl.com", "nxy.in", "nyti.ms", "o-x.fr", "oc1.us", "om.ly", "omf.gd", "omoikane.net", "on.cnn.com", "on.mktw.net", "onforb.es", "orz.se", "ow.ly", "ping.fm", + "pli.gs", "pnt.me", "politi.co", "post.ly", "pp.gg", "profile.to", "ptiturl.com", "pub.vitrue.com", "qlnk.net", "qte.me", "qu.tc", "qy.fi", "r.ebay.com", "r.im", "rb6.me", "read.bi", + "readthis.ca", "reallytinyurl.com", "redir.ec", "redirects.ca", "redirx.com", "retwt.me", "ri.ms", "rickroll.it", "riz.gd", "rt.nu", "ru.ly", "rubyurl.com", "rurl.org", + "rww.tw", "s4c.in", "s7y.us", "safe.mn", "sameurl.com", "sdut.us", "shar.es", "shink.de", "shorl.com", "short.ie", "short.to", "shortlinks.co.uk", "shorturl.com", + "shout.to", "show.my", "shrinkify.com", "shrinkr.com", "shrt.fr", "shrt.st", "shrten.com", "shrunkin.com", "simurl.com", "slate.me", "smallr.com", "smsh.me", "smurl.name", + "sn.im", "snipr.com", "snipurl.com", "snurl.com", "sp2.ro", "spedr.com", "srnk.net", "srs.li", "starturl.com", "stks.co", "su.pr", "surl.co.uk", "surl.hu", "t.cn", "t.co", "t.lh.com", + "ta.gd", "tbd.ly", "tcrn.ch", "tgr.me", "tgr.ph", "tighturl.com", "tiniuri.com", "tiny.cc", "tiny.ly", "tiny.pl", "tinylink.in", "tinyuri.ca", "tinyurl.com", "tk.", "tl.gd", + "tmi.me", "tnij.org", "tnw.to", "tny.com", "to.ly", "togoto.us", "totc.us", "toysr.us", "tpm.ly", "tr.im", "tra.kz", "trunc.it", "twhub.com", "twirl.at", + "twitclicks.com", "twitterurl.net", "twitterurl.org", "twiturl.de", "twurl.cc", "twurl.nl", "u.mavrev.com", "u.nu", "u76.org", "ub0.cc", "ulu.lu", "updating.me", "ur1.ca", + "url.az", "url.co.uk", "url.ie", "url360.me", "url4.eu", "urlborg.com", "urlbrief.com", "urlcover.com", "urlcut.com", "urlenco.de", "urli.nl", "urls.im", + "urlshorteningservicefortwitter.com", "urlx.ie", "urlzen.com", "usat.ly", "use.my", "vb.ly", "vevo.ly", "vgn.am", "vl.am", "vm.lc", "w55.de", "wapo.st", "wapurl.co.uk", "wipi.es", + "wp.me", "x.vu", "xr.com", "xrl.in", "xrl.us", "xurl.es", "xurl.jp", "y.ahoo.it", "yatuc.com", "ye.pe", "yep.it", "yfrog.com", "yhoo.it", "yiyd.com", "youtu.be", "yuarel.com", + "z0p.de", "zi.ma", "zi.mu", "zipmyurl.com", "zud.me", "zurl.ws", "zz.gd", "zzang.kr", "›.ws", "✩.ws", "✿.ws", "❥.ws", "➔.ws", "➞.ws", "➡.ws", "➨.ws", "➯.ws", "➹.ws", "➽.ws"]; - return this.each(function () { - var container = $(this), - resourceURL = (url && (!url.indexOf('http://') || !url.indexOf('https://'))) ? url : container.attr("href"), - provider; + if ($('#jqoembeddata').length === 0) $('').appendTo('body'); - if(embedAction) { - settings.onEmbed = embedAction; - } else if(!settings.onEmbed) { - settings.onEmbed = function (oembedData) { - $.fn.oembed.insertCode(this, settings.embedMethod, oembedData); - }; - } + return this.each(function () { + var container = $(this), + resourceURL = (url && (!url.indexOf('http://') || !url.indexOf('https://'))) ? url : container.attr("href"), + provider; - if(resourceURL !== null && resourceURL !== undefined) { - //Check if shorten URL - for(var j = 0, l = shortURLList.length; j < l; j++) { - var regExp = new RegExp('://' + shortURLList[j] + '/', "i"); - if(resourceURL.match(regExp) !== null) { - //AJAX to http://api.longurl.org/v2/expand?url=http://bit.ly/JATvIs&format=json&callback=hhh - var ajaxopts = $.extend({ - url: "http://api.longurl.org/v2/expand", - dataType: 'jsonp', - data: { - url: resourceURL, - format: "json" - //callback: "?" - }, - success: function (data) { - //this = $.fn.oembed; - resourceURL = data['long-url']; - provider = $.fn.oembed.getOEmbedProvider(data['long-url']); + if (embedAction) { + settings.onEmbed = embedAction; + } + else if (!settings.onEmbed) { + settings.onEmbed = function (oembedData) { + $.fn.oembed.insertCode(this, settings.embedMethod, oembedData); + }; + } - if(provider !== null) { - provider.params = getNormalizedParams(settings[provider.name]) || {}; - provider.maxWidth = settings.maxWidth; - provider.maxHeight = settings.maxHeight; - embedCode(container, resourceURL, provider); - } else { - settings.onProviderNotFound.call(container, resourceURL); - } - } - }, settings.ajaxOptions || {}); + if (resourceURL !== null && resourceURL !== undefined) { + //Check if shorten URL + for (var j = 0, l = shortURLList.length; j < l; j++) { + var regExp = new RegExp('://' + shortURLList[j] + '/', "i"); - $.ajax(ajaxopts); + if (resourceURL.match(regExp) !== null) { + //AJAX to http://api.longurl.org/v2/expand?url=http://bit.ly/JATvIs&format=json&callback=hhh + var ajaxopts = $.extend({ + url: "http://api.longurl.org/v2/expand", + dataType: 'jsonp', + data: { + url: resourceURL, + format: "json" + //callback: "?" + }, + success: function (data) { + //this = $.fn.oembed; + resourceURL = data['long-url']; + provider = $.fn.oembed.getOEmbedProvider(data['long-url']); - return container; - } - } - provider = $.fn.oembed.getOEmbedProvider(resourceURL); + //remove fallback + if (!!settings.fallback === false) { + provider = provider.name.toLowerCase() === 'opengraph' ? null : provider; + } - if(provider !== null) { - provider.params = getNormalizedParams(settings[provider.name]) || {}; - provider.maxWidth = settings.maxWidth; - provider.maxHeight = settings.maxHeight; - embedCode(container, resourceURL, provider); - } else { - settings.onProviderNotFound.call(container, resourceURL); - } - } + if (provider !== null) { + provider.params = getNormalizedParams(settings[provider.name]) || {}; + provider.maxWidth = settings.maxWidth; + provider.maxHeight = settings.maxHeight; + embedCode(container, resourceURL, provider); + } else { + settings.onProviderNotFound.call(container, resourceURL); + } + }, + error: function () { + settings.onError.call(container, resourceURL) + } + }, settings.longUrlAjaxOptions || settings.ajaxOptions || {}); - return container; - }); + $.ajax(ajaxopts); + + return container; + } + } + provider = $.fn.oembed.getOEmbedProvider(resourceURL); + + //remove fallback + if (!!settings.fallback === false) { + provider = provider.name.toLowerCase() === 'opengraph' ? null : provider; + } + if (provider !== null) { + provider.params = getNormalizedParams(settings[provider.name]) || {}; + provider.maxWidth = settings.maxWidth; + provider.maxHeight = settings.maxHeight; + embedCode(container, resourceURL, provider); + } else { + settings.onProviderNotFound.call(container, resourceURL); + } + } + return container; + }); + }; + + var settings; + + // Plugin defaults + $.fn.oembed.defaults = { + fallback: true, + maxWidth: null, + maxHeight: null, + includeHandle: true, + embedMethod: 'auto', + // "auto", "append", "fill" + onProviderNotFound: function () { + }, + beforeEmbed: function () { + }, + afterEmbed: function () { + }, + onEmbed: false, + onError: function (a, b, c, d) { + console.log('err:', a, b, c, d) + }, + ajaxOptions: {}, + longUrlAjaxOptions: {} + }; + + /* Private functions */ + function rand(length, current) { //Found on http://stackoverflow.com/questions/1349404/generate-a-string-of-5-random-characters-in-javascript + current = current ? current : ''; + return length ? rand(--length, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz".charAt(Math.floor(Math.random() * 60)) + current) : current; + } + + function getRequestUrl(provider, externalUrl) { + var url = provider.apiendpoint, + qs = "", + i; + url += (url.indexOf("?") <= 0) ? "?" : "&"; + url = url.replace('#', '%23'); + + if (provider.maxWidth !== null && (typeof provider.params.maxwidth === 'undefined' || provider.params.maxwidth === null)) { + provider.params.maxwidth = provider.maxWidth; + } + + if (provider.maxHeight !== null && (typeof provider.params.maxheight === 'undefined' || provider.params.maxheight === null)) { + provider.params.maxheight = provider.maxHeight; + } + + for (i in provider.params) { + // We don't want them to jack everything up by changing the callback parameter + if (i == provider.callbackparameter) + continue; + + // allows the options to be set to null, don't send null values to the server as parameters + if (provider.params[i] !== null) + qs += "&" + escape(i) + "=" + provider.params[i]; + } + + url += "format=" + provider.format + "&url=" + escape(externalUrl) + qs; + if (provider.dataType != 'json') + url += "&" + provider.callbackparameter + "=?"; + + return url; + } + + function success(oembedData, externalUrl, container) { + $('#jqoembeddata').data(externalUrl, oembedData.code); + settings.beforeEmbed.call(container, oembedData); + settings.onEmbed.call(container, oembedData); + settings.afterEmbed.call(container, oembedData); + } + + function embedCode(container, externalUrl, embedProvider) { + if ($('#jqoembeddata').data(externalUrl) != undefined && embedProvider.embedtag.tag != 'iframe') { + var oembedData = {code: $('#jqoembeddata').data(externalUrl)}; + success(oembedData, externalUrl, container); + } else if (embedProvider.yql) { + var from = embedProvider.yql.from || 'htmlstring'; + var url = embedProvider.yql.url ? embedProvider.yql.url(externalUrl) : externalUrl; + var query = 'SELECT * FROM ' + from + + ' WHERE url="' + (url) + '"' + + " and " + (/html/.test(from) ? 'xpath' : 'itemPath') + "='" + (embedProvider.yql.xpath || '/') + "'"; + if (from == 'html') + query += " and compat='html5'"; + var ajaxopts = $.extend({ + url: "//query.yahooapis.com/v1/public/yql", + dataType: 'jsonp', + data: { + q: query, + format: "json", + env: 'store://datatables.org/alltableswithkeys', + callback: "?" + }, + success: function (data) { + var result; + + if (embedProvider.yql.xpath && embedProvider.yql.xpath == '//meta|//title|//link') { + var meta = {}; + + if (data.query == null) { + data.query = {}; + } + if (data.query.results == null) { + data.query.results = {"meta": []}; + } + for (var i = 0, l = data.query.results.meta.length; i < l; i++) { + var name = data.query.results.meta[i].name || data.query.results.meta[i].property || null; + if (name == null)continue; + meta[name.toLowerCase()] = data.query.results.meta[i].content; + } + if (!meta.hasOwnProperty("title") || !meta.hasOwnProperty("og:title")) { + if (data.query.results.title != null) { + meta.title = data.query.results.title; + } + } + if (!meta.hasOwnProperty("og:image") && data.query.results.hasOwnProperty("link")) { + for (var i = 0, l = data.query.results.link.length; i < l; i++) { + if (data.query.results.link[i].hasOwnProperty("rel")) { + if (data.query.results.link[i].rel == "apple-touch-icon") { + if (data.query.results.link[i].href.charAt(0) == "/") { + meta["og:image"] = url.match(/^(([a-z]+:)?(\/\/)?[^\/]+\/).*$/)[1] + data.query.results.link[i].href; + } else { + meta["og:image"] = data.query.results.link[i].href; + } + } + } + } + } + result = embedProvider.yql.datareturn(meta); + } else { + result = embedProvider.yql.datareturn ? embedProvider.yql.datareturn(data.query.results) : data.query.results.result; + } + if (result === false)return; + var oembedData = $.extend({}, result); + oembedData.code = result; + success(oembedData, externalUrl, container); + }, + error: settings.onError.call(container, externalUrl, embedProvider) + }, settings.ajaxOptions || {}); + $.ajax(ajaxopts); + } else if (embedProvider.templateRegex) { + if (embedProvider.embedtag.tag !== '') { + var flashvars = embedProvider.embedtag.flashvars || ''; + var tag = embedProvider.embedtag.tag || 'embed'; + var width = embedProvider.embedtag.width || 'auto'; + var height = embedProvider.embedtag.height || 'auto'; + var src = externalUrl.replace(embedProvider.templateRegex, embedProvider.apiendpoint); + + if (!embedProvider.nocache) { + src += '&jqoemcache=' + rand(5); + } + + if (embedProvider.apikey) { + src = src.replace('_APIKEY_', settings.apikeys[embedProvider.name]); + } + + var code = $('<' + tag + '/>').attr('src', src).attr('width', width) + .attr('height', height) + .attr('allowfullscreen', embedProvider.embedtag.allowfullscreen || 'true') + .attr('allowscriptaccess', embedProvider.embedtag.allowfullscreen || 'always') + .css('max-height', settings.maxHeight || 'auto') + .css('max-width', settings.maxWidth || 'auto'); + + if (tag == 'embed') { + code.attr('type', embedProvider.embedtag.type || "application/x-shockwave-flash") + .attr('flashvars', externalUrl.replace(embedProvider.templateRegex, flashvars)); + } + + if (tag == 'iframe') { + code.attr('scrolling', embedProvider.embedtag.scrolling || "no") + .attr('frameborder', embedProvider.embedtag.frameborder || "0"); + + } + + success({code: code}, externalUrl, container); + } else if (embedProvider.apiendpoint) { + //Add APIkey if true + if (embedProvider.apikey) + embedProvider.apiendpoint = embedProvider.apiendpoint.replace('_APIKEY_', settings.apikeys[embedProvider.name]); + + ajaxopts = $.extend({ + url: externalUrl.replace(embedProvider.templateRegex, embedProvider.apiendpoint), + dataType: 'jsonp', + success: function (data) { + var oembedData = $.extend({}, data); + oembedData.code = embedProvider.templateData(data); + success(oembedData, externalUrl, container); + }, + error: settings.onError.call(container, externalUrl, embedProvider) + }, settings.ajaxOptions || {}); + $.ajax(ajaxopts); + } else { + success({code: externalUrl.replace(embedProvider.templateRegex, embedProvider.template)}, externalUrl, container); + } + } else { + + var requestUrl = getRequestUrl(embedProvider, externalUrl); + ajaxopts = $.extend({ + url: requestUrl, + dataType: embedProvider.dataType || 'jsonp', + success: function (data) { + var oembedData = $.extend({}, data); + switch (oembedData.type) { + case "file": //Deviant Art has this + case "photo": + oembedData.code = $.fn.oembed.getPhotoCode(externalUrl, oembedData); + break; + case "video": + case "rich": + oembedData.code = $.fn.oembed.getRichCode(externalUrl, oembedData); + break; + default: + oembedData.code = $.fn.oembed.getGenericCode(externalUrl, oembedData); + break; + } + success(oembedData, externalUrl, container); + }, + error: settings.onError.call(container, externalUrl, embedProvider) + }, settings.ajaxOptions || {}); + $.ajax(ajaxopts); + } + } + + function getNormalizedParams(params) { + if (params === null) return null; + var key, normalizedParams = {}; + for (key in params) { + if (key !== null) normalizedParams[key.toLowerCase()] = params[key]; + } + return normalizedParams; + } + + /* Public functions */ + $.fn.oembed.insertCode = function (container, embedMethod, oembedData) { + if (oembedData === null) + return; + + if (embedMethod === 'auto' && container.attr('href') !== null) { + embedMethod = 'append'; + } else if (embedMethod == 'auto') { + embedMethod = 'replace'; + } + + switch (embedMethod) { + case "replace": + container.replaceWith(oembedData.code); + break; + case "fill": + container.html(oembedData.code); + break; + case "append": + container.wrap('
'); + var oembedContainer = container.parent(); + if (settings.includeHandle) { + $('').insertBefore(container).click(function () { + var encodedString = encodeURIComponent($(this).text()); + $(this).html((encodedString == '%E2%86%91') ? '↓' : '↑'); + $(this).parent().children().last().toggle(); + }); + } + oembedContainer.append('
'); + try { + oembedData.code.clone().appendTo(oembedContainer); + } catch (e) { + oembedContainer.append(oembedData.code); + } + /* Make videos semi-responsive + * If parent div width less than embeded iframe video then iframe gets shrunk to fit smaller width + * If parent div width greater thans embed iframe use the max widht + * - works on youtubes and vimeo + */ + if (settings.maxWidth) { + var post_width = oembedContainer.parent().width(); + if (post_width < settings.maxWidth) { + var iframe_width_orig = $('iframe', oembedContainer).width(); + var iframe_height_orig = $('iframe', oembedContainer).height(); + var ratio = iframe_width_orig / post_width; + $('iframe', oembedContainer).width(iframe_width_orig / ratio); + $('iframe', oembedContainer).height(iframe_height_orig / ratio); + } else { + if (settings.maxWidth) { + $('iframe', oembedContainer).width(settings.maxWidth); + } + if (settings.maxHeight) { + $('iframe', oembedContainer).height(settings.maxHeight); + } + } + } + break; + } + }; + + $.fn.oembed.getPhotoCode = function (url, oembedData) { + var code; + var alt = oembedData.title ? oembedData.title : ''; + alt += oembedData.author_name ? ' - ' + oembedData.author_name : ''; + alt += oembedData.provider_name ? ' - ' + oembedData.provider_name : ''; + + if (oembedData.url) { + code = '
' + alt + '
'; + } else if (oembedData.thumbnail_url) { + var newURL = oembedData.thumbnail_url.replace('_s', '_b'); + code = '
' + alt + '
'; + } else { + code = '
Error loading this picture
'; + } + + if (oembedData.html) { + code += "
" + oembedData.html + "
"; + } + + return code; + }; + + $.fn.oembed.getRichCode = function (url, oembedData) { + return oembedData.html; + }; + + $.fn.oembed.getGenericCode = function (url, oembedData) { + var title = ((oembedData.title) && (oembedData.title !== null)) ? oembedData.title : url; + var code = '' + title + ''; + + if (oembedData.html) { + code += "
" + oembedData.html + "
"; + } + + return code; + }; + + $.fn.oembed.getOEmbedProvider = function (url) { + for (var i = 0; i < $.fn.oembed.providers.length; i++) { + for (var j = 0, l = $.fn.oembed.providers[i].urlschemes.length; j < l; j++) { + var regExp = new RegExp($.fn.oembed.providers[i].urlschemes[j], "i"); + + if (url.match(regExp) !== null) + return $.fn.oembed.providers[i]; + } + } + return null; + }; + + // Constructor Function for OEmbedProvider Class. + $.fn.oembed.OEmbedProvider = function (name, type, urlschemesarray, apiendpoint, extraSettings) { + this.name = name; + this.type = type; // "photo", "video", "link", "rich", null + this.urlschemes = urlschemesarray; + this.apiendpoint = apiendpoint; + this.maxWidth = 500; + this.maxHeight = 400; + extraSettings = extraSettings || {}; + + if (extraSettings.useYQL) { + + if (extraSettings.useYQL == 'xml') { + extraSettings.yql = { + xpath: "//oembed/html", + from: 'xml', + apiendpoint: this.apiendpoint, + url: function (externalurl) { + return this.apiendpoint + '?format=xml&url=' + externalurl + }, + datareturn: function (results) { + return results.html.replace(/.*\[CDATA\[(.*)\]\]>$/, '$1') || '' + } + }; + } else { + extraSettings.yql = { + from: 'json', + apiendpoint: this.apiendpoint, + url: function (externalurl) { + return this.apiendpoint + '?format=json&url=' + externalurl + }, + datareturn: function (results) { + if (results.json.type != 'video' && (results.json.url || results.json.thumbnail_url)) { + return ''; + } + return results.json.html || '' + } + }; + } + this.apiendpoint = null; + } - }; + for (var property in extraSettings) { + this[property] = extraSettings[property]; + } - var settings; + this.format = this.format || 'json'; + this.callbackparameter = this.callbackparameter || "callback"; + this.embedtag = this.embedtag || {tag: ""}; - // Plugin defaults - $.fn.oembed.defaults = { - maxWidth: null, - maxHeight: null, - includeHandle: true, - embedMethod: 'auto', - // "auto", "append", "fill" - onProviderNotFound: function () {}, - beforeEmbed: function () {}, - afterEmbed: function () {}, - onEmbed: false, - onError: function () {}, - ajaxOptions: {} - }; - /* Private functions */ - function rand(length, current) { //Found on http://stackoverflow.com/questions/1349404/generate-a-string-of-5-random-characters-in-javascript - current = current ? current : ''; - return length ? rand(--length, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz".charAt(Math.floor(Math.random() * 60)) + current) : current; - } + }; - function getRequestUrl(provider, externalUrl) { - var url = provider.apiendpoint, - qs = "", - i; - url += (url.indexOf("?") <= 0) ? "?" : "&"; - url = url.replace('#', '%23'); + /* + * Function to update existing providers + * + * @param {String} name The name of the provider + * @param {String} type The type of the provider can be "file", "photo", "video", "rich" + * @param {String} urlshemesarray Array of url of the provider + * @param {String} apiendpoint The endpoint of the provider + * @param {String} extraSettings Extra settings of the provider + */ + $.fn.updateOEmbedProvider = function (name, type, urlschemesarray, apiendpoint, extraSettings) { + for (var i = 0; i < $.fn.oembed.providers.length; i++) { + if ($.fn.oembed.providers[i].name === name) { + if (type !== null) { + $.fn.oembed.providers[i].type = type; + } + if (urlschemesarray !== null) { + $.fn.oembed.providers[i].urlschemes = urlschemesarray; + } + if (apiendpoint !== null) { + $.fn.oembed.providers[i].apiendpoint = apiendpoint; + } + if (extraSettings !== null) { + $.fn.oembed.providers[i].extraSettings = extraSettings; + for (var property in extraSettings) { + if (property !== null && extraSettings[property] !== null) { + $.fn.oembed.providers[i][property] = extraSettings[property]; + } + } + } + } + } + }; - if(provider.maxWidth !== null && (typeof provider.params.maxwidth === 'undefined' || provider.params.maxwidth === null)) { - provider.params.maxwidth = provider.maxWidth; - } + /* Native & common providers */ + $.fn.oembed.providers = [ - if(provider.maxHeight !== null && (typeof provider.params.maxheight === 'undefined' || provider.params.maxheight === null)) { - provider.params.maxheight = provider.maxHeight; - } + //Video + new $.fn.oembed.OEmbedProvider("youtube", "video", ["youtube\\.com/watch.+v=[\\w-]+&?", "youtu\\.be/[\\w-]+", "youtube.com/embed"], '//www.youtube.com/embed/$1?wmode=transparent', { + templateRegex: /.*(?:v\=|be\/|embed\/)([\w\-]+)&?.*/, embedtag: {tag: 'iframe', width: '425', height: '349'} + }), - for(i in provider.params) { - // We don't want them to jack everything up by changing the callback parameter - if(i == provider.callbackparameter) continue; + //new $.fn.oembed.OEmbedProvider("youtube", "video", ["youtube\\.com/watch.+v=[\\w-]+&?", "youtu\\.be/[\\w-]+"], 'http://www.youtube.com/oembed', {useYQL:'json'}), + //new $.fn.oembed.OEmbedProvider("youtubeiframe", "video", ["youtube.com/embed"], "$1?wmode=transparent", + // {templateRegex:/(.*)/,embedtag : {tag: 'iframe', width:'425',height: '349'}}), + new $.fn.oembed.OEmbedProvider("wistia", "video", ["wistia.com/medias/.+", "wistia.com/m/.+", "wistia.com/embed/.+", "wi.st/m/.+", "wi.st/embed/.+"], 'http://fast.wistia.com/oembed', {useYQL: 'json'}), + new $.fn.oembed.OEmbedProvider("xtranormal", "video", ["xtranormal\\.com/watch/.+"], "http://www.xtranormal.com/xtraplayr/$1/$2", { + templateRegex: /.*com\/watch\/([\w\-]+)\/([\w\-]+).*/, embedtag: {tag: 'iframe', width: '320', height: '269'}}), + new $.fn.oembed.OEmbedProvider("scivee", "video", ["scivee.tv/node/.+"], "http://www.scivee.tv/flash/embedCast.swf?", { + templateRegex: /.*tv\/node\/(.+)/, embedtag: {width: '480', height: '400', flashvars: "id=$1&type=3"}}), + new $.fn.oembed.OEmbedProvider("veoh", "video", ["veoh.com/watch/.+"], "http://www.veoh.com/swf/webplayer/WebPlayer.swf?version=AFrontend.5.7.0.1337&permalinkId=$1&player=videodetailsembedded&videoAutoPlay=0&id=anonymous", { + templateRegex: /.*watch\/([^\?]+).*/, embedtag: {width: '410', height: '341'}}), + new $.fn.oembed.OEmbedProvider("gametrailers", "video", ["gametrailers\\.com/video/.+"], "http://media.mtvnservices.com/mgid:moses:video:gametrailers.com:$2", { + templateRegex: /.*com\/video\/([\w\-]+)\/([\w\-]+).*/, embedtag: {width: '512', height: '288' }}), + new $.fn.oembed.OEmbedProvider("funnyordie", "video", ["funnyordie\\.com/videos/.+"], "http://player.ordienetworks.com/flash/fodplayer.swf?", { + templateRegex: /.*videos\/([^\/]+)\/([^\/]+)?/, embedtag: {width: 512, height: 328, flashvars: "key=$1"}}), + new $.fn.oembed.OEmbedProvider("colledgehumour", "video", ["collegehumor\\.com/video/.+"], "http://www.collegehumor.com/moogaloop/moogaloop.swf?clip_id=$1&use_node_id=true&fullscreen=1", + {templateRegex: /.*video\/([^\/]+).*/, embedtag: {width: 600, height: 338}}), + new $.fn.oembed.OEmbedProvider("metacafe", "video", ["metacafe\\.com/watch/.+"], "http://www.metacafe.com/fplayer/$1/$2.swf", + {templateRegex: /.*watch\/(\d+)\/(\w+)\/.*/, embedtag: {width: 400, height: 345}}), + new $.fn.oembed.OEmbedProvider("bambuser", "video", ["bambuser\\.com\/channel\/.*\/broadcast\/.*"], "http://static.bambuser.com/r/player.swf?vid=$1", + {templateRegex: /.*bambuser\.com\/channel\/.*\/broadcast\/(\w+).*/, embedtag: {width: 512, height: 339 }}), + new $.fn.oembed.OEmbedProvider("twitvid", "video", ["twitvid\\.com/.+"], "http://www.twitvid.com/embed.php?guid=$1&autoplay=0", + {templateRegex: /.*twitvid\.com\/(\w+).*/, embedtag: {tag: 'iframe', width: 480, height: 360 }}), + new $.fn.oembed.OEmbedProvider("aniboom", "video", ["aniboom\\.com/animation-video/.+"], "http://api.aniboom.com/e/$1", + {templateRegex: /.*animation-video\/(\d+).*/, embedtag: {width: 594, height: 334}}), + new $.fn.oembed.OEmbedProvider("vzaar", "video", ["vzaar\\.com/videos/.+", "vzaar.tv/.+"], "http://view.vzaar.com/$1/player?", + {templateRegex: /.*\/(\d+).*/, embedtag: {tag: 'iframe', width: 576, height: 324 }}), + new $.fn.oembed.OEmbedProvider("snotr", "video", ["snotr\\.com/video/.+"], "http://www.snotr.com/embed/$1", + {templateRegex: /.*\/(\d+).*/, embedtag: {tag: 'iframe', width: 400, height: 330}, nocache: 1 }), + new $.fn.oembed.OEmbedProvider("youku", "video", ["v.youku.com/v_show/id_.+"], "http://player.youku.com/player.php/sid/$1/v.swf", + {templateRegex: /.*id_(.+)\.html.*/, embedtag: {width: 480, height: 400}, nocache: 1 }), + new $.fn.oembed.OEmbedProvider("tudou", "video", ["tudou.com/programs/view/.+\/"], "http://www.tudou.com/v/$1/v.swf", + {templateRegex: /.*view\/(.+)\//, embedtag: {width: 480, height: 400}, nocache: 1 }), + new $.fn.oembed.OEmbedProvider("embedr", "video", ["embedr\\.com/playlist/.+"], "http://embedr.com/swf/slider/$1/425/520/default/false/std?", + {templateRegex: /.*playlist\/([^\/]+).*/, embedtag: {width: 425, height: 520}}), + new $.fn.oembed.OEmbedProvider("blip", "video", ["blip\\.tv/.+"], "//blip.tv/oembed/"), + new $.fn.oembed.OEmbedProvider("minoto-video", "video", ["http://api.minoto-video.com/publishers/.+/videos/.+", "http://dashboard.minoto-video.com/main/video/details/.+", "http://embed.minoto-video.com/.+"], "http://api.minoto-video.com/services/oembed.json", {useYQL: 'json'}), + new $.fn.oembed.OEmbedProvider("animoto", "video", ["animoto.com/play/.+"], "http://animoto.com/services/oembed"), + new $.fn.oembed.OEmbedProvider("hulu", "video", ["hulu\\.com/watch/.*"], "//www.hulu.com/api/oembed.json"), + new $.fn.oembed.OEmbedProvider("ustream", "video", ["ustream\\.tv/recorded/.*"], "http://www.ustream.tv/oembed", {useYQL: 'json'}), + new $.fn.oembed.OEmbedProvider("videojug", "video", ["videojug\\.com/(film|payer|interview).*"], "http://www.videojug.com/oembed.json", {useYQL: 'json'}), + new $.fn.oembed.OEmbedProvider("sapo", "video", ["videos\\.sapo\\.pt/.*"], "http://videos.sapo.pt/oembed", {useYQL: 'json'}), + new $.fn.oembed.OEmbedProvider("vodpod", "video", ["vodpod.com/watch/.*"], "http://vodpod.com/oembed.js", {useYQL: 'json'}), + new $.fn.oembed.OEmbedProvider("vimeo", "video", ["www\.vimeo\.com\/groups\/.*\/videos\/.*", "www\.vimeo\.com\/.*", "vimeo\.com\/groups\/.*\/videos\/.*", "vimeo\.com\/.*"], "//vimeo.com/api/oembed.json"), + new $.fn.oembed.OEmbedProvider("dailymotion", "video", ["dailymotion\\.com/.+"], '//www.dailymotion.com/services/oembed'), + new $.fn.oembed.OEmbedProvider("5min", "video", ["www\\.5min\\.com/.+"], 'http://api.5min.com/oembed.xml', {useYQL: 'xml'}), + new $.fn.oembed.OEmbedProvider("National Film Board of Canada", "video", ["nfb\\.ca/film/.+"], 'http://www.nfb.ca/remote/services/oembed/', {useYQL: 'json'}), + new $.fn.oembed.OEmbedProvider("qik", "video", ["qik\\.com/\\w+"], 'http://qik.com/api/oembed.json', {useYQL: 'json'}), + new $.fn.oembed.OEmbedProvider("revision3", "video", ["revision3\\.com"], "http://revision3.com/api/oembed/"), + new $.fn.oembed.OEmbedProvider("dotsub", "video", ["dotsub\\.com/view/.+"], "http://dotsub.com/services/oembed", {useYQL: 'json'}), + new $.fn.oembed.OEmbedProvider("clikthrough", "video", ["clikthrough\\.com/theater/video/\\d+"], "http://clikthrough.com/services/oembed"), + new $.fn.oembed.OEmbedProvider("Kinomap", "video", ["kinomap\\.com/.+"], "http://www.kinomap.com/oembed"), + new $.fn.oembed.OEmbedProvider("VHX", "video", ["vhx.tv/.+"], "http://vhx.tv/services/oembed.json"), + new $.fn.oembed.OEmbedProvider("bambuser", "video", ["bambuser.com/.+"], "http://api.bambuser.com/oembed/iframe.json"), + new $.fn.oembed.OEmbedProvider("justin.tv", "video", ["justin.tv/.+"], 'http://api.justin.tv/api/embed/from_url.json', {useYQL: 'json'}), + new $.fn.oembed.OEmbedProvider("vine", "video", ["vine.co/v/.*"], null, + { + templateRegex: /https?:\/\/w?w?w?.?vine\.co\/v\/([a-zA-Z0-9]*).*/, + template: '' + + '', + nocache: 1 + }), + new $.fn.oembed.OEmbedProvider("boxofficebuz", "video", ["boxofficebuz\\.com\\/embed/.+"], "http://boxofficebuz.com/embed/$1/$2", {templateRegex: [/.*boxofficebuz\.com\/embed\/(\w+)\/([\w*\-*]+)/], embedtag: {tag: 'iframe', width: 480, height: 360 }}), + new $.fn.oembed.OEmbedProvider("clipsyndicate", "video", ["clipsyndicate\\.com/video/play/.+", "clipsyndicate\\.com/embed/iframe\?.+"], "http://eplayer.clipsyndicate.com/embed/iframe?pf_id=1&show_title=0&va_id=$1&windows=1", {templateRegex: [/.*www\.clipsyndicate\.com\/video\/play\/(\w+)\/.*/, /.*eplayer\.clipsyndicate\.com\/embed\/iframe\?.*va_id=(\w+).*.*/], embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), + new $.fn.oembed.OEmbedProvider("coub", "video", ["coub\\.com/.+"], "http://www.coub.com/embed/$1?muted=false&autostart=false&originalSize=false&hideTopBar=false&noSiteButtons=false&startWithHD=false", {templateRegex: [/.*coub\.com\/embed\/(\w+)\?*.*/, /.*coub\.com\/view\/(\w+).*/], embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), + new $.fn.oembed.OEmbedProvider("discoverychannel", "video", ["snagplayer\\.video\\.dp\\.discovery\\.com/.+"], "http://snagplayer.video.dp.discovery.com/$1/snag-it-player.htm?auto=no", {templateRegex: [/.*snagplayer\.video\.dp\.discovery\/(\w+).*/], embedtag: {tag: 'iframe', width: 480, height: 360 }}), + new $.fn.oembed.OEmbedProvider("telly", "video", ["telly\\.com/.+"], "http://www.telly.com/embed.php?guid=$1&autoplay=0", {templateRegex: [/.*telly\.com\/embed\.php\?guid=(\w+).*/, /.*telly\.com\/(\w+).*/], embedtag: {tag: 'iframe', width: 480, height: 360 }}), + new $.fn.oembed.OEmbedProvider("minilogs", "video", ["minilogs\\.com/.+"], "http://www.minilogs.com/e/$1", {templateRegex: [/.*minilogs\.com\/e\/(\w+).*/, /.*minilogs\.com\/(\w+).*/], embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), + new $.fn.oembed.OEmbedProvider("viddy", "video", ["viddy\\.com/.+"], "http://www.viddy.com/embed/video/$1", {templateRegex: [/.*viddy\.com\/embed\/video\/(\.*)/, /.*viddy\.com\/video\/(\.*)/], embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), + new $.fn.oembed.OEmbedProvider("worldstarhiphop", "video", ["worldstarhiphop\\.com\/embed/.+"], "http://www.worldstarhiphop.com/embed/$1", {templateRegex: /.*worldstarhiphop\.com\/embed\/(\w+).*/, embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), + new $.fn.oembed.OEmbedProvider("zapiks", "video", ["zapiks\\.fr\/.+"], "http://www.zapiks.fr/index.php?action=playerIframe&media_id=$1&autoStart=fals", {templateRegex: /.*zapiks\.fr\/index.php\?[\w\=\&]*media_id=(\w+).*/, embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), - // allows the options to be set to null, don't send null values to the server as parameters - if(provider.params[i] !== null) qs += "&" + escape(i) + "=" + provider.params[i]; - } + //Audio + new $.fn.oembed.OEmbedProvider("official.fm", "rich", ["official.fm/.+"], 'http://official.fm/services/oembed', {useYQL: 'json'}), + new $.fn.oembed.OEmbedProvider("chirbit", "rich", ["chirb.it/.+"], 'http://chirb.it/oembed.json', {useYQL: 'json'}), + new $.fn.oembed.OEmbedProvider("chirbit", "audio", ["chirb\\.it/.+"], "http://chirb.it/wp/$1", {templateRegex: [/.*chirb\.it\/wp\/(\w+).*/, /.*chirb\.it\/(\w+).*/], embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), + new $.fn.oembed.OEmbedProvider("Huffduffer", "rich", ["huffduffer.com/[-.\\w@]+/\\d+"], "http://huffduffer.com/oembed"), + new $.fn.oembed.OEmbedProvider("Spotify", "rich", ["open.spotify.com/(track|album|user)/"], "https://embed.spotify.com/oembed/"), + new $.fn.oembed.OEmbedProvider("shoudio", "rich", ["shoudio.com/.+", "shoud.io/.+"], "http://shoudio.com/api/oembed"), + new $.fn.oembed.OEmbedProvider("mixcloud", "rich", ["mixcloud.com/.+"], 'http://www.mixcloud.com/oembed/', {useYQL: 'json'}), + new $.fn.oembed.OEmbedProvider("rdio.com", "rich", ["rd.io/.+", "rdio.com"], "http://www.rdio.com/api/oembed/"), + new $.fn.oembed.OEmbedProvider("Soundcloud", "rich", ["soundcloud.com/.+", "snd.sc/.+"], "//soundcloud.com/oembed", {format: 'js'}), + new $.fn.oembed.OEmbedProvider("bandcamp", "rich", ["bandcamp\\.com/album/.+"], null, + { + yql: { + xpath: "//meta[contains(@content, \\'EmbeddedPlayer\\')]", + from: 'html', + datareturn: function (results) { + return results.meta ? '' : false; + } + } + }), - url += "format=" + provider.format + "&url=" + escape(externalUrl) + qs; - if(provider.dataType != 'json') url += "&" + provider.callbackparameter + "=?"; + //Photo + new $.fn.oembed.OEmbedProvider("deviantart", "photo", ["deviantart.com/.+", "fav.me/.+", "deviantart.com/.+"], "//backend.deviantart.com/oembed", {format: 'jsonp'}), + new $.fn.oembed.OEmbedProvider("skitch", "photo", ["skitch.com/.+"], null, + { + yql: { + xpath: "json", + from: 'json', + url: function (externalurl) { + return 'http://skitch.com/oembed/?format=json&url=' + externalurl + }, + datareturn: function (data) { + return $.fn.oembed.getPhotoCode(data.json.url, data.json); + } + } + }), + new $.fn.oembed.OEmbedProvider("mobypicture", "photo", ["mobypicture.com/user/.+/view/.+", "moby.to/.+"], "http://api.mobypicture.com/oEmbed"), + new $.fn.oembed.OEmbedProvider("flickr", "photo", ["flickr\\.com/photos/.+"], "//flickr.com/services/oembed", {callbackparameter: 'jsoncallback'}), + new $.fn.oembed.OEmbedProvider("photobucket", "photo", ["photobucket\\.com/(albums|groups)/.+"], "http://photobucket.com/oembed/"), + new $.fn.oembed.OEmbedProvider("instagram", "photo", ["instagr\\.?am(\\.com)?/.+"], "//api.instagram.com/oembed"), + //new $.fn.oembed.OEmbedProvider("yfrog", "photo", ["yfrog\\.(com|ru|com\\.tr|it|fr|co\\.il|co\\.uk|com\\.pl|pl|eu|us)/.+"], "http://www.yfrog.com/api/oembed",{useYQL:"json"}), + new $.fn.oembed.OEmbedProvider("SmugMug", "photo", ["smugmug.com/[-.\\w@]+/.+"], "http://api.smugmug.com/services/oembed/"), + new $.fn.oembed.OEmbedProvider("dribbble", "photo", ["dribbble.com/shots/.+"], "http://api.dribbble.com/shots/$1?callback=?", + { + templateRegex: /.*shots\/([\d]+).*/, + templateData: function (data) { + if (!data.image_teaser_url) { + return false; + } + return ''; + } + }), + new $.fn.oembed.OEmbedProvider("chart.ly", "photo", ["chart\\.ly/[a-z0-9]{6,8}"], "http://chart.ly/uploads/large_$1.png", + {templateRegex: /.*ly\/([^\/]+).*/, embedtag: {tag: 'img'}, nocache: 1}), + //new $.fn.oembed.OEmbedProvider("stocktwits.com", "photo", ["stocktwits\\.com/message/.+"], "http://charts.stocktwits.com/production/original_$1.png?", + // { templateRegex: /.*message\/([^\/]+).*/, embedtag: { tag: 'img'},nocache:1 }), + new $.fn.oembed.OEmbedProvider("circuitlab", "photo", ["circuitlab.com/circuit/.+"], "https://www.circuitlab.com/circuit/$1/screenshot/540x405/", + {templateRegex: /.*circuit\/([^\/]+).*/, embedtag: {tag: 'img'}, nocache: 1}), + new $.fn.oembed.OEmbedProvider("23hq", "photo", ["23hq.com/[-.\\w@]+/photo/.+"], "http://www.23hq.com/23/oembed", {useYQL: "json"}), + new $.fn.oembed.OEmbedProvider("img.ly", "photo", ["img\\.ly/.+"], "//img.ly/show/thumb/$1", + {templateRegex: /.*ly\/([^\/]+).*/, embedtag: {tag: 'img'}, nocache: 1}), + new $.fn.oembed.OEmbedProvider("twitgoo.com", "photo", ["twitgoo\\.com/.+"], "http://twitgoo.com/show/thumb/$1", + {templateRegex: /.*com\/([^\/]+).*/, embedtag: {tag: 'img'}, nocache: 1}), + new $.fn.oembed.OEmbedProvider("imgur.com", "photo", ["imgur\\.com/gallery/.+"], "http://imgur.com/$1l.jpg", + {templateRegex: /.*gallery\/([^\/]+).*/, embedtag: {tag: 'img'}, nocache: 1}), + new $.fn.oembed.OEmbedProvider("visual.ly", "rich", ["visual\\.ly/.+"], null, + { + yql: { + xpath: "//a[@id=\\'gc_article_graphic_image\\']/img", + from: 'htmlstring' + } + }), + new $.fn.oembed.OEmbedProvider("achewood", "photo", ["achewood\\.com\\/index.php\\?date=.+"], "http://www.achewood.com/comic.php?date=$1", {templateRegex: /.*achewood\.com\/index.php\?date=(\w+).*/, embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), + new $.fn.oembed.OEmbedProvider("fotokritik", "photo", ["fotokritik\\.com/.+"], "http://www.fotokritik.com/embed/$1", {templateRegex: [/.*fotokritik\.com\/embed\/(\w+).*/, /.*fotokritik\.com\/(\w+).*/], embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), + new $.fn.oembed.OEmbedProvider("giflike", "photo", ["giflike\\.com/.+"], "http://www.giflike.com/embed/$1", {templateRegex: [/.*giflike\.com\/embed\/(\w+).*/, /.*giflike\.com\/a\/(\w+).*/], embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), - return url; - } + //Rich + new $.fn.oembed.OEmbedProvider("twitter", "rich", ["twitter.com/.+"], "https://api.twitter.com/1/statuses/oembed.json"), + new $.fn.oembed.OEmbedProvider("gmep", "rich", ["gmep.imeducate.com/.*", "gmep.org/.*"], "http://gmep.org/oembed.json"), + new $.fn.oembed.OEmbedProvider("urtak", "rich", ["urtak.com/(u|clr)/.+"], "http://oembed.urtak.com/1/oembed"), + new $.fn.oembed.OEmbedProvider("cacoo", "rich", ["cacoo.com/.+"], "http://cacoo.com/oembed.json"), + new $.fn.oembed.OEmbedProvider("dailymile", "rich", ["dailymile.com/people/.*/entries/.*"], "http://api.dailymile.com/oembed"), + new $.fn.oembed.OEmbedProvider("documentcloud", "rich", ["documentcloud.org/documents/.+"], "https://www.documentcloud.org/api/oembed.json"), + new $.fn.oembed.OEmbedProvider("dipity", "rich", ["dipity.com/timeline/.+"], 'http://www.dipity.com/oembed/timeline/', {useYQL: 'json'}), + new $.fn.oembed.OEmbedProvider("sketchfab", "rich", ["sketchfab.com/show/.+"], 'http://sketchfab.com/oembed', {useYQL: 'json'}), + new $.fn.oembed.OEmbedProvider("speakerdeck", "rich", ["speakerdeck.com/.+"], 'http://speakerdeck.com/oembed.json', {useYQL: 'json'}), + new $.fn.oembed.OEmbedProvider("popplet", "rich", ["popplet.com/app/.*"], "http://popplet.com/app/Popplet_Alpha.swf?page_id=$1&em=1", + { + templateRegex: /.*#\/([^\/]+).*/, + embedtag: { + width: 460, + height: 460 + } + }), - function success(oembedData, externalUrl, container) { - $('#jqoembeddata').data(externalUrl, oembedData.code); - settings.beforeEmbed.call(container, oembedData); - settings.onEmbed.call(container, oembedData); - settings.afterEmbed.call(container, oembedData); - } + new $.fn.oembed.OEmbedProvider("pearltrees", "rich", ["pearltrees.com/.*"], "http://cdn.pearltrees.com/s/embed/getApp?", + { + templateRegex: /.*N-f=1_(\d+).*N-p=(\d+).*/, + embedtag: { + width: 460, + height: 460, + flashvars: "lang=en_US&embedId=pt-embed-$1-693&treeId=$1&pearlId=$2&treeTitle=Diagrams%2FVisualization&site=www.pearltrees.com%2FF" + } + }), - function embedCode(container, externalUrl, embedProvider) { - if($('#jqoembeddata').data(externalUrl) != undefined && embedProvider.embedtag.tag != 'iframe') { - var oembedData = { - code: $('#jqoembeddata').data(externalUrl) - }; - success(oembedData, externalUrl, container); - } else if(embedProvider.yql) { - var from = embedProvider.yql.from || 'htmlstring'; - var url = embedProvider.yql.url ? embedProvider.yql.url(externalUrl) : externalUrl; - var query = 'SELECT * FROM ' + from + ' WHERE url="' + (url) + '"' + " and " + (/html/.test(from) ? 'xpath' : 'itemPath') + "='" + (embedProvider.yql.xpath || '/') + "'"; - if(from == 'html') query += " and compat='html5'"; - var ajaxopts = $.extend({ - url: "http://query.yahooapis.com/v1/public/yql", - dataType: 'jsonp', - data: { - q: query, - format: "json", - env: 'store://datatables.org/alltableswithkeys', - callback: "?" - }, - success: function (data) { - var result; - if(embedProvider.yql.xpath && embedProvider.yql.xpath == '//meta|//title|//link') { - var meta = {}; - if(data.query.results == null) { - data.query.results = { - "meta": [] - }; - } - for(var i = 0, l = data.query.results.meta.length; i < l; i++) { - var name = data.query.results.meta[i].name || data.query.results.meta[i].property || null; - if(name == null) continue; - meta[name.toLowerCase()] = data.query.results.meta[i].content; - } - if(!meta.hasOwnProperty("title") || !meta.hasOwnProperty("og:title")) { - if(data.query.results.title != null) { - meta.title = data.query.results.title; - } - } - result = embedProvider.yql.datareturn(meta); - } else { - result = embedProvider.yql.datareturn ? embedProvider.yql.datareturn(data.query.results) : data.query.results.result; - } - if(result === false) return; - var oembedData = $.extend({}, result); - oembedData.code = result; - success(oembedData, externalUrl, container); - }, - error: settings.onError.call(container, externalUrl, embedProvider) - }, settings.ajaxOptions || {}); + new $.fn.oembed.OEmbedProvider("prezi", "rich", ["prezi.com/.*"], "//prezi.com/bin/preziloader.swf?", + { + templateRegex: /.*com\/([^\/]+)\/.*/, + embedtag: { + width: 550, + height: 400, + flashvars: "prezi_id=$1&lock_to_path=0&color=ffffff&autoplay=no&autohide_ctrls=0" + } + }), - $.ajax(ajaxopts); - } else if(embedProvider.templateRegex) { - if(embedProvider.embedtag.tag !== '') { - var flashvars = embedProvider.embedtag.flashvars || ''; - var tag = embedProvider.embedtag.tag || 'embed'; - var width = embedProvider.embedtag.width || 'auto'; - var nocache = embedProvider.embedtag.nocache || 0; - var height = embedProvider.embedtag.height || 'auto'; - var src = externalUrl.replace(embedProvider.templateRegex, embedProvider.apiendpoint); - if(!embedProvider.nocache) src += '&jqoemcache=' + rand(5); - if(embedProvider.apikey) src = src.replace('_APIKEY_', settings.apikeys[embedProvider.name]); + new $.fn.oembed.OEmbedProvider("tourwrist", "rich", ["tourwrist.com/tours/.+"], null, + { + templateRegex: /.*tours.([\d]+).*/, + template: function (wm, tourid) { + setTimeout(function () { + if (loadEmbeds)loadEmbeds(); + }, 2000); + return "
"; + } + }), - var code = $('<' + tag + '/>') - .attr('src', src) - .attr('width', width) - .attr('height', height) - .attr('allowfullscreen', embedProvider.embedtag.allowfullscreen || 'true') - .attr('allowscriptaccess', embedProvider.embedtag.allowfullscreen || 'always') - .css('max-height', settings.maxHeight || 'auto') - .css('max-width', settings.maxWidth || 'auto'); - if(tag == 'embed') { - code - .attr('type', embedProvider.embedtag.type || "application/x-shockwave-flash") - .attr('flashvars', externalUrl.replace(embedProvider.templateRegex, flashvars)); - } - if(tag == 'iframe'){ - code - .attr('scrolling', embedProvider.embedtag.scrolling || "no") - .attr('frameborder', embedProvider.embedtag.frameborder || "0"); - } + new $.fn.oembed.OEmbedProvider("meetup", "rich", ["meetup\\.(com|ps)/.+"], "http://api.meetup.com/oembed"), + new $.fn.oembed.OEmbedProvider("ebay", "rich", ["ebay\\.*"], "http://togo.ebay.com/togo/togo.swf?2008013100", + { + templateRegex: /.*\/([^\/]+)\/(\d{10,13}).*/, + embedtag: { + width: 355, + height: 300, + flashvars: "base=http://togo.ebay.com/togo/&lang=en-us&mode=normal&itemid=$2&query=$1" + } + }), + new $.fn.oembed.OEmbedProvider("wikipedia", "rich", ["wikipedia.org/wiki/.+"], "http://$1.wikipedia.org/w/api.php?action=parse&page=$2&format=json§ion=0&callback=?", { + templateRegex: /.*\/\/([\w]+).*\/wiki\/([^\/]+).*/, + templateData: function (data) { + if (!data.parse) + return false; + var text = data.parse['text']['*'].replace(/href="\/wiki/g, 'href="http://en.wikipedia.org/wiki'); + return ''; + } + }), + new $.fn.oembed.OEmbedProvider("imdb", "rich", ["imdb.com/title/.+"], "http://www.imdbapi.com/?i=$1&callback=?", + { + templateRegex: /.*\/title\/([^\/]+).*/, + templateData: function (data) { + if (!data.Title) + return false; + return '

' + data.Title + ' (' + data.Year + ')

Rating: ' + data.imdbRating + '
Genre: ' + data.Genre + '
Starring: ' + data.Actors + '

' + data.Plot + '
'; + } + }), + new $.fn.oembed.OEmbedProvider("livejournal", "rich", ["livejournal.com/"], "http://ljpic.seacrow.com/json/$2$4?jsonp=?" + , { + templateRegex: /(http:\/\/(((?!users).)+)\.livejournal\.com|.*users\.livejournal\.com\/([^\/]+)).*/, + templateData: function (data) { + if (!data.username) + return false; + return '
[info]' + data.username + '
' + data.name + '
'; + } + }), + new $.fn.oembed.OEmbedProvider("circuitbee", "rich", ["circuitbee\\.com/circuit/view/.+"], "http://c.circuitbee.com/build/r/schematic-embed.html?id=$1", + { + templateRegex: /.*circuit\/view\/(\d+).*/, + embedtag: { + tag: 'iframe', + width: '500', + height: '350' + } + }), - var oembedData = { - code: code - }; - success(oembedData, externalUrl, container); - } else if(embedProvider.apiendpoint) { - //Add APIkey if true - if(embedProvider.apikey) embedProvider.apiendpoint = embedProvider.apiendpoint.replace('_APIKEY_', settings.apikeys[embedProvider.name]); - ajaxopts = $.extend({ - url: externalUrl.replace(embedProvider.templateRegex, embedProvider.apiendpoint), - dataType: 'jsonp', - success: function (data) { - var oembedData = $.extend({}, data); - oembedData.code = embedProvider.templateData(data); - if(oembedData.code) success(oembedData, externalUrl, container); - }, - error: settings.onError.call(container, externalUrl, embedProvider) - }, settings.ajaxOptions || {}); + new $.fn.oembed.OEmbedProvider("googlecalendar", "rich", ["www.google.com/calendar/embed?.+"], "$1", + {templateRegex: /(.*)/, embedtag: {tag: 'iframe', width: '800', height: '600' }}), + new $.fn.oembed.OEmbedProvider("jsfiddle", "rich", ["jsfiddle.net/[^/]+/?"], "http://jsfiddle.net/$1/embedded/result,js,resources,html,css/?", + {templateRegex: /.*net\/([^\/]+).*/, embedtag: {tag: 'iframe', width: '100%', height: '300' }}), + new $.fn.oembed.OEmbedProvider("jsbin", "rich", ["jsbin.com/.+"], "http://jsbin.com/$1/?", + {templateRegex: /.*com\/([^\/]+).*/, embedtag: {tag: 'iframe', width: '100%', height: '300' }}), + new $.fn.oembed.OEmbedProvider("jotform", "rich", ["form.jotform.co/form/.+"], "$1?", + {templateRegex: /(.*)/, embedtag: {tag: 'iframe', width: '100%', height: '507' }}), + new $.fn.oembed.OEmbedProvider("reelapp", "rich", ["reelapp\\.com/.+"], "http://www.reelapp.com/$1/embed", + {templateRegex: /.*com\/(\S{6}).*/, embedtag: {tag: 'iframe', width: '400', height: '338'}}), + new $.fn.oembed.OEmbedProvider("linkedin", "rich", ["linkedin.com/pub/.+"], "https://www.linkedin.com/cws/member/public_profile?public_profile_url=$1&format=inline&isFramed=true", + {templateRegex: /(.*)/, embedtag: {tag: 'iframe', width: '368px', height: 'auto'}}), + new $.fn.oembed.OEmbedProvider("timetoast", "rich", ["timetoast.com/timelines/[0-9]+"], "http://www.timetoast.com/flash/TimelineViewer.swf?passedTimelines=$1", + {templateRegex: /.*timelines\/([0-9]*)/, embedtag: { width: 550, height: 400}, nocache: 1}), + new $.fn.oembed.OEmbedProvider("pastebin", "rich", ["pastebin\\.com/[\\S]{8}"], "http://pastebin.com/embed_iframe.php?i=$1", + {templateRegex: /.*\/(\S{8}).*/, embedtag: {tag: 'iframe', width: '100%', height: 'auto'}}), + new $.fn.oembed.OEmbedProvider("mixlr", "rich", ["mixlr.com/.+"], "http://mixlr.com/embed/$1?autoplay=ae", + {templateRegex: /.*com\/([^\/]+).*/, embedtag: {tag: 'iframe', width: '100%', height: 'auto' }}), + new $.fn.oembed.OEmbedProvider("pastie", "rich", ["pastie\\.org/pastes/.+"], null, {yql: {xpath: '//pre[@class="textmate-source"]'}}), + new $.fn.oembed.OEmbedProvider("github", "rich", ["gist.github.com/.+"], "https://github.com/api/oembed"), + new $.fn.oembed.OEmbedProvider("github", "rich", ["github.com/[-.\\w@]+/[-.\\w@]+"], "https://api.github.com/repos/$1/$2?callback=?" + , {templateRegex: /.*\/([^\/]+)\/([^\/]+).*/, + templateData: function (data) { + if (!data.data.html_url)return false; + return '

' + data.data.name + '

' + data.data.description + '

' + + '

Last updated: ' + data.data.pushed_at + '

'; + } + }), + new $.fn.oembed.OEmbedProvider("facebook", "rich", ["facebook.com"], null + , {templateRegex: /.*\/([^\/]+)\/([^\/]+).*/, + template: function (url) { + // adding script directly to DOM to make sure that it is loaded correctly. + if (!$.fn.oembed.facebokScriptHasBeenAdded) { + $('
').appendTo('body'); + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.text = '(function(d, s, id) {var js, fjs = d.getElementsByTagName(s)[0];if (d.getElementById(id)) return;js = d.createElement(s); js.id = id;js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.0";fjs.parentNode.insertBefore(js, fjs);}(document, "script", "facebook-jssdk"));'; + document.body.appendChild(script); + $.fn.oembed.facebokScriptHasBeenAdded = true; + } - $.ajax(ajaxopts); - } else { - var oembedData = { - code: externalUrl.replace(embedProvider.templateRegex, embedProvider.template) - }; - success(oembedData, externalUrl, container); - } - } else { + // returning template with url of facebook post. + return '
'; - var requestUrl = getRequestUrl(embedProvider, externalUrl), - ajaxopts = $.extend({ - url: requestUrl, - dataType: embedProvider.dataType || 'jsonp', - success: function (data) { - var oembedData = $.extend({}, data); - switch(oembedData.type) { - case "file": //Deviant Art has this - case "photo": - oembedData.code = $.fn.oembed.getPhotoCode(externalUrl, oembedData); - break; - case "video": - case "rich": - oembedData.code = $.fn.oembed.getRichCode(externalUrl, oembedData); - break; - default: - oembedData.code = $.fn.oembed.getGenericCode(externalUrl, oembedData); - break; - } - success(oembedData, externalUrl, container); - }, - error: settings.onError.call(container, externalUrl, embedProvider) - }, settings.ajaxOptions || {}); + } + }), + /* + // Saving old implementation of Facebook in case we will need it as example in the future. + new $.fn.oembed.OEmbedProvider("facebook", "rich", ["facebook.com/(people/[^\\/]+/\\d+|[^\\/]+$)"], "https://graph.facebook.com/$2$3/?callback=?" + ,{templateRegex:/.*facebook.com\/(people\/[^\/]+\/(\d+).*|([^\/]+$))/, + templateData : function(data){ if(!data.id)return false; + var out = '
facebook '; + if(data.from) out += ''+data.from.name+''; + else if(data.link) out += ''+data.name+''; + else if(data.username) out += ''+data.name+''; + else out += ''+data.name+''; + out += '
'; + if(data.picture) out += ''; + else out += ''; + if(data.from) out += ''+data.name+''; + if(data.founded) out += 'Founded: '+data.founded+'
'; + if(data.category) out += 'Category: '+data.category+'
'; + if(data.website) out += 'Website: '+data.website+'
'; + if(data.gender) out += 'Gender: '+data.gender+'
'; + if(data.description) out += data.description + '
'; + out += '
'; + return out; + } + }), + */ + new $.fn.oembed.OEmbedProvider("stackoverflow", "rich", ["stackoverflow.com/questions/[\\d]+"], "http://api.stackoverflow.com/1.1/questions/$1?body=true&jsonp=?" + , {templateRegex: /.*questions\/([\d]+).*/, + templateData: function (data) { + if (!data.questions) + return false; + var q = data.questions[0]; + var body = $(q.body).text(); + var out = '
' + + '' + (q.up_vote_count - q.down_vote_count) + '
vote(s)
' + + '
' + q.answer_count + 'answer
' + q.view_count + ' view(s)
' + + '

' + q.title + '

' + + '
' + body.substring(0, 100) + '...
'; + for (i in q.tags) { + out += ''; + } - $.ajax(ajaxopts); - } - }; + out += '
'; + return out; + } + }), + new $.fn.oembed.OEmbedProvider("wordpress", "rich", ["wordpress\\.com/.+", "blogs\\.cnn\\.com/.+", "techcrunch\\.com/.+", "wp\\.me/.+"], "http://public-api.wordpress.com/oembed/1.0/?for=jquery-oembed-all"), + new $.fn.oembed.OEmbedProvider("screenr", "rich", ["screenr\.com"], "http://www.screenr.com/embed/$1", + {templateRegex: /.*\/([^\/]+).*/, embedtag: {tag: 'iframe', width: '650', height: 396}}) , + new $.fn.oembed.OEmbedProvider("gigpans", "rich", ["gigapan\\.org/[-.\\w@]+/\\d+"], "http://gigapan.org/gigapans/$1/options/nosnapshots/iframe/flash.html", + {templateRegex: /.*\/(\d+)\/?.*/, embedtag: {tag: 'iframe', width: '100%', height: 400 }}), + new $.fn.oembed.OEmbedProvider("scribd", "rich", ["scribd\\.com/.+"], "http://www.scribd.com/embeds/$1/content?start_page=1&view_mode=list", + {templateRegex: /.*doc\/([^\/]+).*/, embedtag: {tag: 'iframe', width: '100%', height: 600}}), + new $.fn.oembed.OEmbedProvider("kickstarter", "rich", ["kickstarter\\.com/projects/.+"], "$1/widget/card.html", + {templateRegex: /([^\?]+).*/, embedtag: {tag: 'iframe', width: '220', height: 380}}), + new $.fn.oembed.OEmbedProvider("amazon", "rich", ["amzn.com/B+", "amazon.com.*/(B\\S+)($|\\/.*)"], "http://rcm.amazon.com/e/cm?t=_APIKEY_&o=1&p=8&l=as1&asins=$1&ref=qf_br_asin_til&fc1=000000&IS2=1<1=_blank&m=amazon&lc1=0000FF&bc1=000000&bg1=FFFFFF&f=ifr", + { + apikey: true, + templateRegex: /.*\/(B[0-9A-Z]+)($|\/.*)/, + embedtag: { + tag: 'iframe', + width: '120px', + height: '240px'} + }), + new $.fn.oembed.OEmbedProvider("slideshare", "rich", ["slideshare\.net"], "//www.slideshare.net/api/oembed/2", {format: 'jsonp'}), + new $.fn.oembed.OEmbedProvider("roomsharejp", "rich", ["roomshare\\.jp/(en/)?post/.*"], "http://roomshare.jp/oembed.json"), + new $.fn.oembed.OEmbedProvider("lanyard", "rich", ["lanyrd.com/\\d+/.+"], null, + { + yql: { + xpath: '(//div[@class="primary"])[1]', + from: 'htmlstring', + datareturn: function (results) { + if (!results.result) + return false; + return '
' + results.result + '
'; + } + } + }), + new $.fn.oembed.OEmbedProvider("asciiartfarts", "rich", ["asciiartfarts.com/\\d+.html"], null, + { + yql: { + xpath: '//pre/font', + from: 'htmlstring', + datareturn: function (results) { + if (!results.result) + return false; + return '
' + results.result + '
'; + } + } + }), + new $.fn.oembed.OEmbedProvider("coveritlive", "rich", ["coveritlive.com/"], null, { + templateRegex: /(.*)/, + template: ''}), + new $.fn.oembed.OEmbedProvider("polldaddy", "rich", ["polldaddy.com/"], null, { + templateRegex: /(?:https?:\/\/w?w?w?.?polldaddy.com\/poll\/)([0-9]*)\//, + template: '', + nocache: 1 + }), + new $.fn.oembed.OEmbedProvider("360io", "rich", ["360\\.io/.+"], "http://360.io/$1", {templateRegex: /.*360\.io\/(\w+).*/, embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), + new $.fn.oembed.OEmbedProvider("bubbli", "rich", ["on\\.bubb\\.li/.+"], "http://on.bubb.li/$1", {templateRegex: /.*on\.bubb\.li\/(\w+).*/, embedtag: {tag: 'iframe', width: 480, height: 360}, nocache: 1 }), + new $.fn.oembed.OEmbedProvider("cloudup", "rich", ["cloudup\\.com/.+"], "http://cloudup.com/$1?chromeless", {templateRegex: [/.*cloudup\.com\/(\w+).*/], embedtag: {tag: 'iframe', width: 480, height: 360 }}), + new $.fn.oembed.OEmbedProvider("codepen", "rich", ["codepen.io/.+"], "http://codepen.io/$1/embed/$2", {templateRegex: [/.*io\/(\w+)\/pen\/(\w+).*/, /.*io\/(\w+)\/full\/(\w+).*/], embedtag: {tag: 'iframe', width: '100%', height: '300'}, nocache: 1 }), + new $.fn.oembed.OEmbedProvider("googleviews", "rich", ["(.*maps\\.google\\.com\\/maps\\?).+(output=svembed).+(cbp=(.*)).*"], "https://maps.google.com/maps?layer=c&panoid=$3&ie=UTF8&source=embed&output=svembed&cbp=$5", {templateRegex: /(.*maps\.google\.com\/maps\?).+(panoid=(\w+)&).*(cbp=(.*)).*/, embedtag: {tag: 'iframe', width: 480, height: 360}, nocache: 1 }), + new $.fn.oembed.OEmbedProvider("googlemaps", "rich", ["google\\.com\/maps\/place/.+"], "http://maps.google.com/maps?t=m&q=$1&output=embed", {templateRegex: /.*google\.com\/maps\/place\/([\w\+]*)\/.*/, embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), + new $.fn.oembed.OEmbedProvider("imajize", "rich", ["embed\\.imajize\\.com/.+"], "http://embed.imajize.com/$1", {templateRegex: /.*embed\.imajize\.com\/(.*)/, embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), + new $.fn.oembed.OEmbedProvider("mapjam", "rich", ["mapjam\\.com/.+"], "http://www.mapjam.com/$1", {templateRegex: /.*mapjam\.com\/(.*)/, embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), + new $.fn.oembed.OEmbedProvider("polar", "rich", ["polarb\\.com/.+"], "http://assets-polarb-com.a.ssl.fastly.net/api/v4/publishers/unknown/embedded_polls/iframe?poll_id=$1", {templateRegex: /.*polarb\.com\/polls\/(\w+).*/, embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), + new $.fn.oembed.OEmbedProvider("ponga", "rich", ["ponga\\.com/.+"], "https://www.ponga.com/embedded?id=$1", {templateRegex: [/.*ponga\.com\/embedded\?id=(\w+).*/, /.*ponga\.com\/(\w+).*/], embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), - function getNormalizedParams(params) { - if(params === null) return null; - var key, normalizedParams = {}; - for(key in params) { - if(key !== null) normalizedParams[key.toLowerCase()] = params[key]; - } - return normalizedParams; - } + //Use Open Graph Where applicable + new $.fn.oembed.OEmbedProvider("opengraph", "rich", [".*"], null, + { + yql: { + xpath: "//meta|//title|//link", + from: 'html', + datareturn: function (results) { + if (!results['og:title'] && results['title'] && results['description']) + results['og:title'] = results['title']; - /* Public functions */ - $.fn.oembed.insertCode = function (container, embedMethod, oembedData) { - if(oembedData === null) return; - if(embedMethod == 'auto' && container.attr("href") !== null) embedMethod = 'append'; - else if(embedMethod == 'auto') embedMethod = 'replace'; - switch(embedMethod) { - case "replace": - container.replaceWith(oembedData.code); - break; - case "fill": - container.html(oembedData.code); - break; - case "append": - container.wrap('
'); - var oembedContainer = container.parent(); - if(settings.includeHandle) { - $('').insertBefore(container).click(function () { - var encodedString = encodeURIComponent($(this).text()); - $(this).html((encodedString == '%E2%86%91') ? '↓' : '↑'); - $(this).parent().children().last().toggle(); - }); - } - oembedContainer.append('
'); - try { - oembedData.code.clone().appendTo(oembedContainer); - } catch(e) { - oembedContainer.append(oembedData.code); - } - /* Make videos semi-responsive - * If parent div width less than embeded iframe video then iframe gets shrunk to fit smaller width - * If parent div width greater thans embed iframe use the max widht - * - works on youtubes and vimeo - */ - if(settings.maxWidth) { - var post_width = oembedContainer.parent().width(); - if(post_width < settings.maxWidth) { - var iframe_width_orig = $('iframe', oembedContainer).width(); - var iframe_height_orig = $('iframe', oembedContainer).height(); - var ratio = iframe_width_orig / post_width; - $('iframe', oembedContainer).width(iframe_width_orig / ratio); - $('iframe', oembedContainer).height(iframe_height_orig / ratio); - } else { - if(settings.maxWidth) { - $('iframe', oembedContainer).width(settings.maxWidth); - } - if(settings.maxHeight) { - $('iframe', oembedContainer).height(settings.maxHeight); - } - } - } - break; - } - }; + if (!results['og:title'] && !results['title']) + return false; - $.fn.oembed.getPhotoCode = function (url, oembedData) { - var code, alt = oembedData.title ? oembedData.title : ''; - alt += oembedData.author_name ? ' - ' + oembedData.author_name : ''; - alt += oembedData.provider_name ? ' - ' + oembedData.provider_name : ''; - if(oembedData.url) { - code = '
' + alt + '
'; - } else if(oembedData.thumbnail_url) { - var newURL = oembedData.thumbnail_url.replace('_s', '_b'); - code = '
' + alt + '
'; - } else { - code = '
Error loading this picture
'; - } - if(oembedData.html) code += "
" + oembedData.html + "
"; - return code; - }; + var code = $('

'); + if (results['og:video']) { + var embed = $(''); + embed.attr('type', results['og:video:type'] || "application/x-shockwave-flash") + .css('max-height', settings.maxHeight || 'auto') + .css('max-width', settings.maxWidth || 'auto'); + if (results['og:video:width']) + embed.attr('width', results['og:video:width']); + if (results['og:video:height']) + embed.attr('height', results['og:video:height']); + code.append(embed); + } else if (results['og:image']) { + var img = $(''); + img.css('max-height', settings.maxHeight || 'auto').css('max-width', settings.maxWidth || 'auto'); + if (results['og:image:width']) + img.attr('width', results['og:image:width']); + if (results['og:image:height']) + img.attr('height', results['og:image:height']); + code.append(img); + } - $.fn.oembed.getRichCode = function (url, oembedData) { - var code = oembedData.html; - return code; - }; + if (results['og:title']) + code.append('' + results['og:title'] + '
'); - $.fn.oembed.getGenericCode = function (url, oembedData) { - var title = (oembedData.title !== null) ? oembedData.title : url, - code = ''; - if(oembedData.html) code += '

' + title + '
' + jQuery(oembedData.html).text().substring(0,200) + '... more
'; - return code; - }; + if (results['og:description']) + code.append(results['og:description'] + '
'); + else if (results['description']) + code.append(results['description'] + '
'); - $.fn.oembed.getOEmbedProvider = function (url) { - for(var i = 0; i < $.fn.oembed.providers.length; i++) { - for(var j = 0, l = $.fn.oembed.providers[i].urlschemes.length; j < l; j++) { - var regExp = new RegExp($.fn.oembed.providers[i].urlschemes[j], "i"); - if(url.match(regExp) !== null) return $.fn.oembed.providers[i]; - } - } - return null; - }; + return code; + } + } + } + ) - $.fn.oembed.OEmbedProvider = function (name, type, urlschemesarray, apiendpoint, extraSettings) { - this.name = name; - this.type = type; // "photo", "video", "link", "rich", null - this.urlschemes = urlschemesarray; - this.apiendpoint = apiendpoint; - this.maxWidth = 500; - this.maxHeight = 400; - extraSettings = extraSettings || {}; - - if(extraSettings.useYQL) { - if(extraSettings.useYQL == 'xml') { - extraSettings.yql = { - xpath: "//oembed/html", - from: 'xml', - apiendpoint: this.apiendpoint, - url: function (externalurl) { - return this.apiendpoint + '?format=xml&url=' + externalurl - }, - datareturn: function (results) { - return results.html.replace(/.*\[CDATA\[(.*)\]\]>$/, '$1') || '' - } - }; - } else { - extraSettings.yql = { - from: 'json', - apiendpoint: this.apiendpoint, - url: function (externalurl) { - return this.apiendpoint + '?format=json&url=' + externalurl - }, - datareturn: function (results) { - if(results.json.type != 'video' && (results.json.url || results.json.thumbnail_url)) { - return ''; - } - return results.json.html || '' - } - }; - } - this.apiendpoint = null; - } - - for(var property in extraSettings) { - this[property] = extraSettings[property]; - } - - this.format = this.format || 'json'; - this.callbackparameter = this.callbackparameter || "callback"; - this.embedtag = this.embedtag || { - tag: "" - }; - }; - - /* - * Function to update existing providers - * - * @param {String} name The name of the provider - * @param {String} type The type of the provider can be "file", "photo", "video", "rich" - * @param {String} urlshemesarray Array of url of the provider - * @param {String} apiendpoint The endpoint of the provider - * @param {String} extraSettings Extra settings of the provider - */ - $.fn.updateOEmbedProvider = function (name, type, urlschemesarray, apiendpoint, extraSettings) { - for(var i = 0; i < $.fn.oembed.providers.length; i++) { - if($.fn.oembed.providers[i].name === name) { - if(type !== null) { - $.fn.oembed.providers[i].type = type; - } - if(urlschemesarray !== null) { - $.fn.oembed.providers[i].urlschemes = urlschemesarray; - } - if(apiendpoint !== null) { - $.fn.oembed.providers[i].apiendpoint = apiendpoint; - } - if(extraSettings !== null) { - $.fn.oembed.providers[i].extraSettings = extraSettings; - for(var property in extraSettings) { - if(property !== null && extraSettings[property] !== null) { - $.fn.oembed.providers[i][property] = extraSettings[property]; - } - } - } - } - } - }; - - /* Native & common providers */ - $.fn.oembed.providers = [ - //Video - new $.fn.oembed.OEmbedProvider("youtube", "video", ["youtube\\.com/watch.+v=[\\w-]+&?", "youtu\\.be/[\\w-]+", "youtube.com/embed"], 'http://www.youtube.com/embed/$1?wmode=transparent', { - templateRegex: /.*(?:v\=|be\/|embed\/)([\w\-]+)&?.*/, - embedtag: { - tag: 'iframe', - width: '425', - height: '349' - } - }), - - // new $.fn.oembed.OEmbedProvider("youtube", "video", ["youtube\\.com/watch.+v=[\\w-]+&?", "youtu\\.be/[\\w-]+"], 'http://www.youtube.com/oembed', { - // useYQL: 'json' - // }), - // new $.fn.oembed.OEmbedProvider("youtubeiframe", "video", ["youtube.com/embed"], "$1?wmode=transparent", { - // templateRegex: /(.*)/, - // embedtag: { - // tag: 'iframe', - // width: '425', - // height: '349' - // } - // }), - new $.fn.oembed.OEmbedProvider("wistia", "video", ["wistia.com/m/.+", "wistia.com/embed/.+", "wi.st/m/.+", "wi.st/embed/.+"], 'http://fast.wistia.com/oembed', { - useYQL: 'json' - }), - new $.fn.oembed.OEmbedProvider("xtranormal", "video", ["xtranormal\\.com/watch/.+"], "http://www.xtranormal.com/xtraplayr/$1/$2", { - templateRegex: /.*com\/watch\/([\w\-]+)\/([\w\-]+).*/, - embedtag: { - tag: 'iframe', - width: '320', - height: '269' - } - }), - new $.fn.oembed.OEmbedProvider("scivee", "video", ["scivee.tv/node/.+"], "http://www.scivee.tv/flash/embedCast.swf?", { - templateRegex: /.*tv\/node\/(.+)/, - embedtag: { - width: '480', - height: '400', - flashvars: "id=$1&type=3" - } - }), - new $.fn.oembed.OEmbedProvider("veoh", "video", ["veoh.com/watch/.+"], "http://www.veoh.com/swf/webplayer/WebPlayer.swf?version=AFrontend.5.7.0.1337&permalinkId=$1&player=videodetailsembedded&videoAutoPlay=0&id=anonymous", { - templateRegex: /.*watch\/([^\?]+).*/, - embedtag: { - width: '410', - height: '341' - } - }), - new $.fn.oembed.OEmbedProvider("gametrailers", "video", ["gametrailers\\.com/video/.+"], "http://media.mtvnservices.com/mgid:moses:video:gametrailers.com:$2", { - templateRegex: /.*com\/video\/([\w\-]+)\/([\w\-]+).*/, - embedtag: { - width: '512', - height: '288' - } - }), - new $.fn.oembed.OEmbedProvider("funnyordie", "video", ["funnyordie\\.com/videos/.+"], "http://player.ordienetworks.com/flash/fodplayer.swf?", { - templateRegex: /.*videos\/([^\/]+)\/([^\/]+)?/, - embedtag: { - width: 512, - height: 328, - flashvars: "key=$1" - } - }), - new $.fn.oembed.OEmbedProvider("colledgehumour", "video", ["collegehumor\\.com/video/.+"], "http://www.collegehumor.com/moogaloop/moogaloop.swf?clip_id=$1&use_node_id=true&fullscreen=1", { - templateRegex: /.*video\/([^\/]+).*/, - embedtag: { - width: 600, - height: 338 - } - }), - new $.fn.oembed.OEmbedProvider("metacafe", "video", ["metacafe\\.com/watch/.+"], "http://www.metacafe.com/fplayer/$1/$2.swf", { - templateRegex: /.*watch\/(\d+)\/(\w+)\/.*/, - embedtag: { - width: 400, - height: 345 - } - }), - new $.fn.oembed.OEmbedProvider("bambuser", "video", ["bambuser\\.com\/channel\/.*\/broadcast\/.*"], "http://static.bambuser.com/r/player.swf?vid=$1", { - templateRegex: /.*bambuser\.com\/channel\/.*\/broadcast\/(\w+).*/, - embedtag: { - width: 512, - height: 339 - } - }), - new $.fn.oembed.OEmbedProvider("twitvid", "video", ["twitvid\\.com/.+"], "http://www.twitvid.com/embed.php?guid=$1&autoplay=0", { - templateRegex: /.*twitvid\.com\/(\w+).*/, - embedtag: { - tag: 'iframe', - width: 480, - height: 360 - } - }), - new $.fn.oembed.OEmbedProvider("aniboom", "video", ["aniboom\\.com/animation-video/.+"], "http://api.aniboom.com/e/$1", { - templateRegex: /.*animation-video\/(\d+).*/, - embedtag: { - width: 594, - height: 334 - } - }), - new $.fn.oembed.OEmbedProvider("vzaar", "video", ["vzaar\\.com/videos/.+", "vzaar.tv/.+"], "http://view.vzaar.com/$1/player?", { - templateRegex: /.*\/(\d+).*/, - embedtag: { - tag: 'iframe', - width: 576, - height: 324 - } - }), - new $.fn.oembed.OEmbedProvider("snotr", "video", ["snotr\\.com/video/.+"], "http://www.snotr.com/embed/$1", { - templateRegex: /.*\/(\d+).*/, - embedtag: { - tag: 'iframe', - width: 400, - height: 330, - nocache: 1 - } - }), - new $.fn.oembed.OEmbedProvider("youku", "video", ["v.youku.com/v_show/id_.+"], "http://player.youku.com/player.php/sid/$1/v.swf", { - templateRegex: /.*id_(.+)\.html.*/, - embedtag: { - width: 480, - height: 400, - nocache: 1 - } - }), - new $.fn.oembed.OEmbedProvider("tudou", "video", ["tudou.com/programs/view/.+\/"], "http://www.tudou.com/v/$1/v.swf", { - templateRegex: /.*view\/(.+)\//, - embedtag: { - width: 480, - height: 400, - nocache: 1 - } - }), - new $.fn.oembed.OEmbedProvider("embedr", "video", ["embedr\\.com/playlist/.+"], "http://embedr.com/swf/slider/$1/425/520/default/false/std?", { - templateRegex: /.*playlist\/([^\/]+).*/, - embedtag: { - width: 425, - height: 520 - } - }), - new $.fn.oembed.OEmbedProvider("blip", "video", ["blip\\.tv/.+"], "http://blip.tv/oembed/"), - new $.fn.oembed.OEmbedProvider("minoto-video", "video", ["http://api.minoto-video.com/publishers/.+/videos/.+", "http://dashboard.minoto-video.com/main/video/details/.+", "http://embed.minoto-video.com/.+"], "http://api.minoto-video.com/services/oembed.json", { - useYQL: 'json' - }), - new $.fn.oembed.OEmbedProvider("animoto", "video", ["animoto.com/play/.+"], "http://animoto.com/services/oembed"), - new $.fn.oembed.OEmbedProvider("hulu", "video", ["hulu\\.com/watch/.*"], "http://www.hulu.com/api/oembed.json"), - new $.fn.oembed.OEmbedProvider("ustream", "video", ["ustream\\.tv/recorded/.*"], "http://www.ustream.tv/oembed", { - useYQL: 'json' - }), - new $.fn.oembed.OEmbedProvider("videojug", "video", ["videojug\\.com/(film|payer|interview).*"], "http://www.videojug.com/oembed.json", { - useYQL: 'json' - }), - new $.fn.oembed.OEmbedProvider("sapo", "video", ["videos\\.sapo\\.pt/.*"], "http://videos.sapo.pt/oembed", { - useYQL: 'json' - }), - new $.fn.oembed.OEmbedProvider("vodpod", "video", ["vodpod.com/watch/.*"], "http://vodpod.com/oembed.js", { - useYQL: 'json' - }), - new $.fn.oembed.OEmbedProvider("vimeo", "video", ["www\.vimeo\.com\/groups\/.*\/videos\/.*", "www\.vimeo\.com\/.*", "vimeo\.com\/groups\/.*\/videos\/.*", "vimeo\.com\/.*"], "//vimeo.com/api/oembed.json"), - new $.fn.oembed.OEmbedProvider("dailymotion", "video", ["dailymotion\\.com/.+"], 'http://www.dailymotion.com/services/oembed'), - new $.fn.oembed.OEmbedProvider("5min", "video", ["www\\.5min\\.com/.+"], 'http://api.5min.com/oembed.xml', { - useYQL: 'xml' - }), - new $.fn.oembed.OEmbedProvider("National Film Board of Canada", "video", ["nfb\\.ca/film/.+"], 'http://www.nfb.ca/remote/services/oembed/', { - useYQL: 'json' - }), - new $.fn.oembed.OEmbedProvider("qik", "video", ["qik\\.com/\\w+"], 'http://qik.com/api/oembed.json', { - useYQL: 'json' - }), - new $.fn.oembed.OEmbedProvider("revision3", "video", ["revision3\\.com"], "http://revision3.com/api/oembed/"), - new $.fn.oembed.OEmbedProvider("dotsub", "video", ["dotsub\\.com/view/.+"], "http://dotsub.com/services/oembed", { - useYQL: 'json' - }), - new $.fn.oembed.OEmbedProvider("clikthrough", "video", ["clikthrough\\.com/theater/video/\\d+"], "http://clikthrough.com/services/oembed"), - new $.fn.oembed.OEmbedProvider("Kinomap", "video", ["kinomap\\.com/.+"], "http://www.kinomap.com/oembed"), - new $.fn.oembed.OEmbedProvider("VHX", "video", ["vhx.tv/.+"], "http://vhx.tv/services/oembed.json"), - new $.fn.oembed.OEmbedProvider("bambuser", "video", ["bambuser.com/.+"], "http://api.bambuser.com/oembed/iframe.json"), - new $.fn.oembed.OEmbedProvider("justin.tv", "video", ["justin.tv/.+"], 'http://api.justin.tv/api/embed/from_url.json', { - useYQL: 'json' - }), - - //Audio - new $.fn.oembed.OEmbedProvider("official.fm", "rich", ["official.fm/.+"], 'http://official.fm/services/oembed', { - useYQL: 'json' - }), - new $.fn.oembed.OEmbedProvider("chirbit", "rich", ["chirb.it/.+"], 'http://chirb.it/oembed.json', { - useYQL: 'json' - }), - new $.fn.oembed.OEmbedProvider("Huffduffer", "rich", ["huffduffer.com/[-.\\w@]+/\\d+"], "http://huffduffer.com/oembed"), - new $.fn.oembed.OEmbedProvider("Spotify", "rich", ["open.spotify.com/(track|album|user)/"], "https://embed.spotify.com/oembed/"), - new $.fn.oembed.OEmbedProvider("shoudio", "rich", ["shoudio.com/.+", "shoud.io/.+"], "http://shoudio.com/api/oembed"), - new $.fn.oembed.OEmbedProvider("mixcloud", "rich", ["mixcloud.com/.+"], 'http://www.mixcloud.com/oembed/', { - useYQL: 'json' - }), - new $.fn.oembed.OEmbedProvider("rdio.com", "rich", ["rd.io/.+", "rdio.com"], "http://www.rdio.com/api/oembed/"), - new $.fn.oembed.OEmbedProvider("Soundcloud", "rich", ["soundcloud.com/.+", "snd.sc/.+"], "http://soundcloud.com/oembed", { - format: 'js' - }), - new $.fn.oembed.OEmbedProvider("bandcamp", "rich", ["bandcamp\\.com/album/.+"], null, { - yql: { - xpath: "//meta[contains(@content, \\'EmbeddedPlayer\\')]", - from: 'html', - datareturn: function (results) { - return results.meta ? '' : false; - } - } - }), - - //Photo - new $.fn.oembed.OEmbedProvider("deviantart", "photo", ["deviantart.com/.+", "fav.me/.+", "deviantart.com/.+"], "http://backend.deviantart.com/oembed", { - format: 'jsonp' - }), - new $.fn.oembed.OEmbedProvider("skitch", "photo", ["skitch.com/.+"], null, { - yql: { - xpath: "json", - from: 'json', - url: function (externalurl) { - return 'http://skitch.com/oembed/?format=json&url=' + externalurl - }, - datareturn: function (data) { - return $.fn.oembed.getPhotoCode(data.json.url, data.json); - } - } - }), - new $.fn.oembed.OEmbedProvider("mobypicture", "photo", ["mobypicture.com/user/.+/view/.+", "moby.to/.+"], "http://api.mobypicture.com/oEmbed"), - new $.fn.oembed.OEmbedProvider("flickr", "photo", ["flickr\\.com/photos/.+"], "http://flickr.com/services/oembed", { - callbackparameter: 'jsoncallback' - }), - new $.fn.oembed.OEmbedProvider("photobucket", "photo", ["photobucket\\.com/(albums|groups)/.+"], "http://photobucket.com/oembed/"), - new $.fn.oembed.OEmbedProvider("instagram", "photo", ["instagr\\.?am(\\.com)?/.+"], "http://api.instagram.com/oembed"), - // new $.fn.oembed.OEmbedProvider("yfrog", "photo", ["yfrog\\.(com|ru|com\\.tr|it|fr|co\\.il|co\\.uk|com\\.pl|pl|eu|us)/.+"], "http://www.yfrog.com/api/oembed", { - // useYQL: "json" - // }), - new $.fn.oembed.OEmbedProvider("SmugMug", "photo", ["smugmug.com/[-.\\w@]+/.+"], "http://api.smugmug.com/services/oembed/"), - - new $.fn.oembed.OEmbedProvider("dribbble", "photo", ["dribbble.com/shots/.+"], "http://api.dribbble.com/shots/$1?callback=?", { - templateRegex: /.*shots\/([\d]+).*/, - templateData: function (data) { - if(!data.image_teaser_url) return false; - return ''; - } - }), - new $.fn.oembed.OEmbedProvider("chart.ly", "photo", ["chart\\.ly/[a-z0-9]{6,8}"], "http://chart.ly/uploads/large_$1.png", { - templateRegex: /.*ly\/([^\/]+).*/, - embedtag: { - tag: 'img' - }, - nocache: 1 - }), - // new $.fn.oembed.OEmbedProvider("stocktwits.com", "photo", ["stocktwits\\.com/message/.+"], "http://charts.stocktwits.com/production/original_$1.png?", { - // templateRegex: /.*message\/([^\/]+).*/, - // embedtag: { - // tag: 'img' - // }, - // nocache: 1 - // }), - new $.fn.oembed.OEmbedProvider("circuitlab", "photo", ["circuitlab.com/circuit/.+"], "https://www.circuitlab.com/circuit/$1/screenshot/540x405/", { - templateRegex: /.*circuit\/([^\/]+).*/, - embedtag: { - tag: 'img' - }, - nocache: 1 - }), - new $.fn.oembed.OEmbedProvider("23hq", "photo", ["23hq.com/[-.\\w@]+/photo/.+"], "http://www.23hq.com/23/oembed", { - useYQL: "json" - }), - new $.fn.oembed.OEmbedProvider("img.ly", "photo", ["img\\.ly/.+"], "http://img.ly/show/thumb/$1", { - templateRegex: /.*ly\/([^\/]+).*/, - embedtag: { - tag: 'img' - }, - nocache: 1 - }), - new $.fn.oembed.OEmbedProvider("twitgoo.com", "photo", ["twitgoo\\.com/.+"], "http://twitgoo.com/show/thumb/$1", { - templateRegex: /.*com\/([^\/]+).*/, - embedtag: { - tag: 'img' - }, - nocache: 1 - }), - new $.fn.oembed.OEmbedProvider("imgur.com", "photo", ["imgur\\.com/gallery/.+"], "http://imgur.com/$1l.jpg", { - templateRegex: /.*gallery\/([^\/]+).*/, - embedtag: { - tag: 'img' - }, - nocache: 1 - }), - new $.fn.oembed.OEmbedProvider("visual.ly", "rich", ["visual\\.ly/.+"], null, { - yql: { - xpath: "//a[@id=\\'gc_article_graphic_image\\']/img", - from: 'htmlstring' - } - }), - - //Rich - new $.fn.oembed.OEmbedProvider("twitter", "rich", ["twitter.com/.+"], "https://api.twitter.com/1/statuses/oembed.json"), - new $.fn.oembed.OEmbedProvider("gmep", "rich", ["gmep.imeducate.com/.*", "gmep.org/.*"], "http://gmep.org/oembed.json"), - new $.fn.oembed.OEmbedProvider("urtak", "rich", ["urtak.com/(u|clr)/.+"], "http://oembed.urtak.com/1/oembed"), - new $.fn.oembed.OEmbedProvider("cacoo", "rich", ["cacoo.com/.+"], "http://cacoo.com/oembed.json"), - new $.fn.oembed.OEmbedProvider("dailymile", "rich", ["dailymile.com/people/.*/entries/.*"], "http://api.dailymile.com/oembed"), - new $.fn.oembed.OEmbedProvider("dipity", "rich", ["dipity.com/timeline/.+"], 'http://www.dipity.com/oembed/timeline/', { - useYQL: 'json' - }), - new $.fn.oembed.OEmbedProvider("sketchfab", "rich", ["sketchfab.com/show/.+"], 'http://sketchfab.com/oembed', { - useYQL: 'json' - }), - new $.fn.oembed.OEmbedProvider("speakerdeck", "rich", ["speakerdeck.com/.+"], 'http://speakerdeck.com/oembed.json', { - useYQL: 'json' - }), - new $.fn.oembed.OEmbedProvider("popplet", "rich", ["popplet.com/app/.*"], "http://popplet.com/app/Popplet_Alpha.swf?page_id=$1&em=1", { - templateRegex: /.*#\/([^\/]+).*/, - embedtag: { - width: 460, - height: 460 - } - }), - new $.fn.oembed.OEmbedProvider("pearltrees", "rich", ["pearltrees.com/.*"], "http://cdn.pearltrees.com/s/embed/getApp?", { - templateRegex: /.*N-f=1_(\d+).*N-p=(\d+).*/, - embedtag: { - width: 460, - height: 460, - flashvars: "lang=en_US&embedId=pt-embed-$1-693&treeId=$1&pearlId=$2&treeTitle=Diagrams%2FVisualization&site=www.pearltrees.com%2FF" - } - }), - new $.fn.oembed.OEmbedProvider("prezi", "rich", ["prezi.com/.*"], "http://prezi.com/bin/preziloader.swf?", { - templateRegex: /.*com\/([^\/]+)\/.*/, - embedtag: { - width: 550, - height: 400, - flashvars: "prezi_id=$1&lock_to_path=0&color=ffffff&autoplay=no&autohide_ctrls=0" - } - }), - new $.fn.oembed.OEmbedProvider("tourwrist", "rich", ["tourwrist.com/tours/.+"], null, { - templateRegex: /.*tours.([\d]+).*/, - template: function (wm, tourid) { - setTimeout(function () { - if(loadEmbeds) loadEmbeds(); - }, 2000); - return "
"; - } - }), - new $.fn.oembed.OEmbedProvider("meetup", "rich", ["meetup\\.(com|ps)/.+"], "http://api.meetup.com/oembed"), - new $.fn.oembed.OEmbedProvider("ebay", "rich", ["ebay\\.*"], "http://togo.ebay.com/togo/togo.swf?2008013100", { - templateRegex: /.*\/([^\/]+)\/(\d{10,13}).*/, - embedtag: { - width: 355, - height: 300, - flashvars: "base=http://togo.ebay.com/togo/&lang=en-us&mode=normal&itemid=$2&query=$1" - } - }), - new $.fn.oembed.OEmbedProvider("wikipedia", "rich", ["wikipedia.org/wiki/.+"], "http://$1.wikipedia.org/w/api.php?action=parse&page=$2&format=json§ion=0&callback=?", { - templateRegex: /.*\/\/([\w]+).*\/wiki\/([^\/]+).*/, - templateData: function (data) { - if(!data.parse) return false; - var text = data.parse['text']['*'].replace(/href="\/wiki/g, 'href="http://en.wikipedia.org/wiki'); - return '
' + data.parse['displaytitle'] + '
' +jQuery(text).text().substring(0,200) + '...
'; - } - }), - new $.fn.oembed.OEmbedProvider("imdb", "rich", ["imdb.com/title/.+"], "http://www.imdbapi.com/?i=$1&callback=?", { - templateRegex: /.*\/title\/([^\/]+).*/, - templateData: function (data) { - if(!data.Title) return false; - return '

' + data.Title + ' (' + data.Year + ')

Rating: ' + data.imdbRating + '
Genre: ' + data.Genre + '
Starring: ' + data.Actors + '

' + data.Plot + '
'; - } - }), - new $.fn.oembed.OEmbedProvider("livejournal", "rich", ["livejournal.com/"], "http://ljpic.seacrow.com/json/$2$4?jsonp=?", { - templateRegex: /(http:\/\/(((?!users).)+)\.livejournal\.com|.*users\.livejournal\.com\/([^\/]+)).*/, - templateData: function (data) { - if(!data.username) return false; - return '
[info]' + data.username + '
' + data.name + '
'; - } - }), - new $.fn.oembed.OEmbedProvider("circuitbee", "rich", ["circuitbee\\.com/circuit/view/.+"], "http://c.circuitbee.com/build/r/schematic-embed.html?id=$1", { - templateRegex: /.*circuit\/view\/(\d+).*/, - embedtag: { - tag: 'iframe', - width: '500', - height: '350' - } - }), - new $.fn.oembed.OEmbedProvider("googlecalendar", "rich", ["www.google.com/calendar/embed?.+"], "$1", { - templateRegex: /(.*)/, - embedtag: { - tag: 'iframe', - width: '800', - height: '600' - } - }), - new $.fn.oembed.OEmbedProvider("jsfiddle", "rich", ["jsfiddle.net/[^/]+/?"], "http://jsfiddle.net/$1/embedded/result,js,resources,html,css/?", { - templateRegex: /.*net\/([^\/]+).*/, - embedtag: { - tag: 'iframe', - width: '100%', - height: '300' - } - }), - new $.fn.oembed.OEmbedProvider("jsbin", "rich", ["jsbin.com/.+"], "http://jsbin.com/$1/?", { - templateRegex: /.*com\/([^\/]+).*/, - embedtag: { - tag: 'iframe', - width: '100%', - height: '300' - } - }), - new $.fn.oembed.OEmbedProvider("jotform", "rich", ["form.jotform.co/form/.+"], "$1?", { - templateRegex: /(.*)/, - embedtag: { - tag: 'iframe', - width: '100%', - height: '507' - } - }), - new $.fn.oembed.OEmbedProvider("reelapp", "rich", ["reelapp\\.com/.+"], "http://www.reelapp.com/$1/embed", { - templateRegex: /.*com\/(\S{6}).*/, - embedtag: { - tag: 'iframe', - width: '400', - height: '338' - } - }), - new $.fn.oembed.OEmbedProvider("linkedin", "rich", ["linkedin.com/pub/.+"], "https://www.linkedin.com/cws/member/public_profile?public_profile_url=$1&format=inline&isFramed=true", { - templateRegex: /(.*)/, - embedtag: { - tag: 'iframe', - width: '368px', - height: 'auto' - } - }), - new $.fn.oembed.OEmbedProvider("timetoast", "rich", ["timetoast.com/timelines/[0-9]+"], "http://www.timetoast.com/flash/TimelineViewer.swf?passedTimelines=$1", { - templateRegex: /.*timelines\/([0-9]*)/, - embedtag: { - width: 550, - height: 400, - nocache: 1 - } - }), - new $.fn.oembed.OEmbedProvider("pastebin", "rich", ["pastebin\\.com/[\\S]{8}"], "http://pastebin.com/embed_iframe.php?i=$1", { - templateRegex: /.*\/(\S{8}).*/, - embedtag: { - tag: 'iframe', - width: '100%', - height: 'auto' - } - }), - new $.fn.oembed.OEmbedProvider("mixlr", "rich", ["mixlr.com/.+"], "http://mixlr.com/embed/$1?autoplay=ae", { - templateRegex: /.*com\/([^\/]+).*/, - embedtag: { - tag: 'iframe', - width: '100%', - height: 'auto' - } - }), - new $.fn.oembed.OEmbedProvider("pastie", "rich", ["pastie\\.org/pastes/.+"], null, { - yql: { - xpath: '//pre[@class="textmate-source"]' - } - }), - new $.fn.oembed.OEmbedProvider("github", "rich", ["gist.github.com/.+"], "https://github.com/api/oembed"), - new $.fn.oembed.OEmbedProvider("github", "rich", ["github.com/[-.\\w@]+/[-.\\w@]+"], "https://api.github.com/repos/$1/$2?callback=?", { - templateRegex: /.*\/([^\/]+)\/([^\/]+).*/, - templateData: function (data) { - if(!data.data.html_url) return false; - return '

' + data.data.name + '

' + data.data.description + '

' + '

Last updated: ' + data.data.pushed_at + '

'; - } - }), - new $.fn.oembed.OEmbedProvider("facebook", "rich", ["facebook.com/(people/[^\\/]+/\\d+|[^\\/]+$)"], "https://graph.facebook.com/$2$3/?callback=?", { - templateRegex: /.*facebook.com\/(people\/[^\/]+\/(\d+).*|([^\/]+$))/, - templateData: function (data) { - if(!data.id) return false; - var out = '
facebook '; - if(data.from) out += '' + data.from.name + ''; - else if(data.link) out += '' + data.name + ''; - else if(data.username) out += '' + data.name + ''; - else out += '' + data.name + ''; - out += '
'; - if(data.picture) out += ''; - else out += ''; - if(data.from) out += '' + data.name + ''; - if(data.founded) out += 'Founded: ' + data.founded + '
' - if(data.category) out += 'Category: ' + data.category + '
'; - if(data.website) out += 'Website: ' + data.website + '
'; - if(data.gender) out += 'Gender: ' + data.gender + '
'; - if(data.description) out += data.description + '
'; - out += '
'; - return out; - } - }), - new $.fn.oembed.OEmbedProvider("stackoverflow", "rich", ["stackoverflow.com/questions/[\\d]+"], "http://api.stackoverflow.com/1.1/questions/$1?body=true&jsonp=?", { - templateRegex: /.*questions\/([\d]+).*/, - templateData: function (data) { - if(!data.questions) return false; - var q = data.questions[0]; - var body = $(q.body).text(); - var out = '
' + '' + (q.up_vote_count - q.down_vote_count) + '
vote(s)
' + '
' + q.answer_count + 'answer
' + q.view_count + ' view(s)
' + '

' + q.title + '

' + '
' + body.substring(0, 100) + '...
'; - for(i in q.tags) - out += ''; - out += '
'; - return out; - } - }), - new $.fn.oembed.OEmbedProvider("wordpress", "rich", ["wordpress\\.com/.+", "blogs\\.cnn\\.com/.+", "techcrunch\\.com/.+", "wp\\.me/.+"], "http://public-api.wordpress.com/oembed/1.0/?for=jquery-oembed-all"), - new $.fn.oembed.OEmbedProvider("screenr", "rich", ["screenr\.com"], "http://www.screenr.com/embed/$1", { - templateRegex: /.*\/([^\/]+).*/, - embedtag: { - tag: 'iframe', - width: '650', - height: 396 - } - }), - new $.fn.oembed.OEmbedProvider("gigpans", "rich", ["gigapan\\.org/[-.\\w@]+/\\d+"], "http://gigapan.org/gigapans/$1/options/nosnapshots/iframe/flash.html", { - templateRegex: /.*\/(\d+)\/?.*/, - embedtag: { - tag: 'iframe', - width: '100%', - height: 400 - } - }), - new $.fn.oembed.OEmbedProvider("scribd", "rich", ["scribd\\.com/.+"], "http://www.scribd.com/embeds/$1/content?start_page=1&view_mode=list", { - templateRegex: /.*doc\/([^\/]+).*/, - embedtag: { - tag: 'iframe', - width: '100%', - height: 600 - } - }), - new $.fn.oembed.OEmbedProvider("kickstarter", "rich", ["kickstarter\\.com/projects/.+"], "$1/widget/card.html", { - templateRegex: /([^\?]+).*/, - embedtag: { - tag: 'iframe', - width: '220', - height: 380 - } - }), - new $.fn.oembed.OEmbedProvider("amazon", "rich", ["amzn.com/B+", "amazon.com.*/(B\\S+)($|\\/.*)"], "http://rcm.amazon.com/e/cm?t=_APIKEY_&o=1&p=8&l=as1&asins=$1&ref=qf_br_asin_til&fc1=000000&IS2=1<1=_blank&m=amazon&lc1=0000FF&bc1=000000&bg1=FFFFFF&f=ifr", { - apikey: true, - templateRegex: /.*\/(B[0-9A-Z]+)($|\/.*)/, - embedtag: { - tag: 'iframe', - width: '120px', - height: '240px' - } - }), - new $.fn.oembed.OEmbedProvider("slideshare", "rich", ["slideshare\.net"], "http://www.slideshare.net/api/oembed/2", { - format: 'jsonp' - }), - new $.fn.oembed.OEmbedProvider("roomsharejp", "rich", ["roomshare\\.jp/(en/)?post/.*"], "http://roomshare.jp/oembed.json"), - new $.fn.oembed.OEmbedProvider("lanyard", "rich", ["lanyrd.com/\\d+/.+"], null, { - yql: { - xpath: '(//div[@class="primary"])[1]', - from: 'htmlstring', - datareturn: function (results) { - if(!results.result) return false; - return '
' + results.result + '
'; - } - } - }), - new $.fn.oembed.OEmbedProvider("asciiartfarts", "rich", ["asciiartfarts.com/\\d+.html"], null, { - yql: { - xpath: '//pre/font', - from: 'htmlstring', - datareturn: function (results) { - if(!results.result) return false; - return '
' + results.result + '
'; - } - } - }) - ]; + ]; })(jQuery); +//This is needed for gravatar :( +String.prototype.md5=function(){var a=function(a,b){var c=(a&65535)+(b&65535);var d=(a>>16)+(b>>16)+(c>>16);return d<<16|c&65535};var b=function(a,b){return a<>>32-b};var c=function(c,d,e,f,g,h){return a(b(a(a(d,c),a(f,h)),g),e)};var d=function(a,b,d,e,f,g,h){return c(b&d|~b&e,a,b,f,g,h)};var e=function(a,b,d,e,f,g,h){return c(b&e|d&~e,a,b,f,g,h)};var f=function(a,b,d,e,f,g,h){return c(b^d^e,a,b,f,g,h)};var g=function(a,b,d,e,f,g,h){return c(d^(b|~e),a,b,f,g,h)};var h=function(b){var c,h,i,j,k,l=b.length;var m=1732584193;var n=-271733879;var o=-1732584194;var p=271733878;for(k=0;k>2]>>d%4*8+4&15)+b.charAt(a[d>>2]>>d%4*8&15)}return c};var j=function(a){var b=(a.length+8>>6)+1;var c=[],d,e=b*16,f,g=a.length;for(d=0;d>2]|=(a.charCodeAt(f)&255)<>2]|=128< Date: Sun, 6 Mar 2016 20:22:50 +0900 Subject: [PATCH 011/205] =?UTF-8?q?opengraph=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/oembed/jquery.oembed.js | 48 ---------------------------------- 1 file changed, 48 deletions(-) diff --git a/addons/oembed/jquery.oembed.js b/addons/oembed/jquery.oembed.js index 07ed993a5..496d689af 100755 --- a/addons/oembed/jquery.oembed.js +++ b/addons/oembed/jquery.oembed.js @@ -949,54 +949,6 @@ new $.fn.oembed.OEmbedProvider("polar", "rich", ["polarb\\.com/.+"], "http://assets-polarb-com.a.ssl.fastly.net/api/v4/publishers/unknown/embedded_polls/iframe?poll_id=$1", {templateRegex: /.*polarb\.com\/polls\/(\w+).*/, embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), new $.fn.oembed.OEmbedProvider("ponga", "rich", ["ponga\\.com/.+"], "https://www.ponga.com/embedded?id=$1", {templateRegex: [/.*ponga\.com\/embedded\?id=(\w+).*/, /.*ponga\.com\/(\w+).*/], embedtag: {tag: 'iframe', width: 480, height: 360 }, nocache: 1}), - //Use Open Graph Where applicable - new $.fn.oembed.OEmbedProvider("opengraph", "rich", [".*"], null, - { - yql: { - xpath: "//meta|//title|//link", - from: 'html', - datareturn: function (results) { - if (!results['og:title'] && results['title'] && results['description']) - results['og:title'] = results['title']; - - if (!results['og:title'] && !results['title']) - return false; - - var code = $('

'); - if (results['og:video']) { - var embed = $(''); - embed.attr('type', results['og:video:type'] || "application/x-shockwave-flash") - .css('max-height', settings.maxHeight || 'auto') - .css('max-width', settings.maxWidth || 'auto'); - if (results['og:video:width']) - embed.attr('width', results['og:video:width']); - if (results['og:video:height']) - embed.attr('height', results['og:video:height']); - code.append(embed); - } else if (results['og:image']) { - var img = $(''); - img.css('max-height', settings.maxHeight || 'auto').css('max-width', settings.maxWidth || 'auto'); - if (results['og:image:width']) - img.attr('width', results['og:image:width']); - if (results['og:image:height']) - img.attr('height', results['og:image:height']); - code.append(img); - } - - if (results['og:title']) - code.append('' + results['og:title'] + '
'); - - if (results['og:description']) - code.append(results['og:description'] + '
'); - else if (results['description']) - code.append(results['description'] + '
'); - - return code; - } - } - } - ) - ]; })(jQuery); //This is needed for gravatar :( From dff2578df315785d5490ef9b5c015ca669f78e5c Mon Sep 17 00:00:00 2001 From: YJSoft Date: Sun, 6 Mar 2016 21:08:16 +0900 Subject: [PATCH 012/205] =?UTF-8?q?HTTPS=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EC=97=90=EC=84=9C=EB=8A=94=20=EA=B8=B4=20=EC=A3=BC=EC=86=8C?= =?UTF-8?q?=EB=A5=BC=20=ED=92=80=EC=A7=80=20=EC=95=8A=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/oembed/jquery.oembed.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/oembed/jquery.oembed.js b/addons/oembed/jquery.oembed.js index 496d689af..1bafb620e 100755 --- a/addons/oembed/jquery.oembed.js +++ b/addons/oembed/jquery.oembed.js @@ -56,8 +56,10 @@ //Check if shorten URL for (var j = 0, l = shortURLList.length; j < l; j++) { var regExp = new RegExp('://' + shortURLList[j] + '/', "i"); + + provider = $.fn.oembed.getOEmbedProvider(resourceURL); - if (resourceURL.match(regExp) !== null) { + if (!provider && window.location.protocol !== "https:" && resourceURL.match(regExp) !== null) { //AJAX to http://api.longurl.org/v2/expand?url=http://bit.ly/JATvIs&format=json&callback=hhh var ajaxopts = $.extend({ url: "http://api.longurl.org/v2/expand", @@ -96,7 +98,6 @@ return container; } } - provider = $.fn.oembed.getOEmbedProvider(resourceURL); //remove fallback if (!!settings.fallback === false) { From 145bdab6c62c22747b13fdabbff3a05db0e05b7a Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 7 Mar 2016 10:27:27 +0900 Subject: [PATCH 013/205] Allow more units in widget cache settings --- .../tpl/widget_generate_code.include.html | 9 ++++++++- modules/widget/widget.controller.php | 20 ++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/modules/widget/tpl/widget_generate_code.include.html b/modules/widget/tpl/widget_generate_code.include.html index 8d931af62..07e1daec0 100644 --- a/modules/widget/tpl/widget_generate_code.include.html +++ b/modules/widget/tpl/widget_generate_code.include.html @@ -25,7 +25,14 @@

- {$lang->unit_min} + + +

{$lang->about_widget_cache}

diff --git a/modules/widget/widget.controller.php b/modules/widget/widget.controller.php index bc0154136..623c534ad 100644 --- a/modules/widget/widget.controller.php +++ b/modules/widget/widget.controller.php @@ -359,6 +359,16 @@ class widgetController extends widget // widget, the cache number and cache values are set $widget_sequence = $args->widget_sequence; $widget_cache = $args->widget_cache; + if (preg_match('/^([0-9\.]+)([smhd])$/i', $widget_cache, $matches)) + { + $multipliers = array('s' => 1, 'm' => 60, 'h' => 3600, 'd' => 86400); + $widget_cache = intval(floatval($matches[1]) * $multipliers[strtolower($matches[2])]); + } + else + { + $widget_cache = intval(floatval($widget_cache) * 60); + } + debugPrint($widget_cache); /** * Even if the cache number and value of the cache and return it to extract data @@ -400,7 +410,7 @@ class widgetController extends widget { $filemtime = filemtime($cache_file); // Should be modified compared to the time of the cache or in the future if creating more than widget.controller.php file a return value of the cache - if($filemtime + $widget_cache * 60 > $_SERVER['REQUEST_TIME'] && $filemtime > filemtime(_XE_PATH_.'modules/widget/widget.controller.php')) + if($filemtime + $widget_cache > $_SERVER['REQUEST_TIME'] && $filemtime > filemtime(_XE_PATH_.'modules/widget/widget.controller.php')) { $cache_body = FileHandler::readFile($cache_file); $cache_body = preg_replace('@<\!--#Meta:@', '
+ +
+ + + + +
diff --git a/modules/board/tpl/filter/update.xml b/modules/board/tpl/filter/update.xml new file mode 100644 index 000000000..a4ae8bb65 --- /dev/null +++ b/modules/board/tpl/filter/update.xml @@ -0,0 +1,7 @@ + +
+ + + + +
diff --git a/modules/document/queries/getDocumentUpdateLog.xml b/modules/document/queries/getDocumentUpdateLog.xml index c7ceeedc0..db61979cb 100644 --- a/modules/document/queries/getDocumentUpdateLog.xml +++ b/modules/document/queries/getDocumentUpdateLog.xml @@ -9,6 +9,7 @@ + From 8b9bad458b0953fc99ec1b16bf0d1b4a1cf2364d Mon Sep 17 00:00:00 2001 From: BJRambo Date: Thu, 10 Mar 2016 18:05:20 +0900 Subject: [PATCH 036/205] delete debug code --- modules/board/board.controller.php | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/board/board.controller.php b/modules/board/board.controller.php index c936f3297..e2d38bd98 100644 --- a/modules/board/board.controller.php +++ b/modules/board/board.controller.php @@ -232,7 +232,6 @@ class boardController extends board { $oMemberModel = getModel('member'); $member_info = $oMemberModel->getMemberInfoByMemberSrl($val->update_member_srl); - debugPrint($val); if($member_info->is_admin === 'Y') { $isadminDocument = true; From b89818e13d724b62da83015d059b5f6f69327ae8 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 11 Mar 2016 15:29:42 +0900 Subject: [PATCH 037/205] Update HTMLPurifier configuration and allow HTML5 --- classes/security/Purifier.class.php | 155 +------------ common/framework/security/htmlfilter.php | 275 +++++++++++++++++++++++ common/legacy.php | 5 +- 3 files changed, 281 insertions(+), 154 deletions(-) create mode 100644 common/framework/security/htmlfilter.php diff --git a/classes/security/Purifier.class.php b/classes/security/Purifier.class.php index 6923e8cde..1664d87e0 100644 --- a/classes/security/Purifier.class.php +++ b/classes/security/Purifier.class.php @@ -3,161 +3,14 @@ class Purifier { - - private $_cacheDir; - private $_htmlPurifier; - private $_config; - private $_def; - - public function __construct() + public static function getInstance() { - $this->_checkCacheDir(); - $this->_setConfig(); + return new self(); } - - public function getInstance() - { - if(!isset($GLOBALS['__PURIFIER_INSTANCE__'])) - { - $GLOBALS['__PURIFIER_INSTANCE__'] = new Purifier(); - } - return $GLOBALS['__PURIFIER_INSTANCE__']; - } - - private function _setConfig() - { - $this->_config = HTMLPurifier_Config::createDefault(); - $this->_config->set('HTML.TidyLevel', 'light'); - $this->_config->set('Output.FlashCompat', TRUE); - $this->_config->set('HTML.SafeObject', TRUE); - $this->_config->set('HTML.SafeEmbed', TRUE); - $this->_config->set('HTML.SafeIframe', TRUE); - $this->_config->set('URI.SafeIframeRegexp', $this->_getWhiteDomainRegexp()); - $this->_config->set('Cache.SerializerPath', $this->_cacheDir); - $this->_config->set('Attr.AllowedFrameTargets', array('_blank')); - //$allowdClasses = array('emoticon'); - //$this->_config->set('Attr.AllowedClasses', $allowdClasses); - $this->_def = $this->_config->getHTMLDefinition(TRUE); - } - - private function _setDefinition(&$content) - { - // add attribute for edit component - $editComponentAttrs = $this->_searchEditComponent($content); - if(is_array($editComponentAttrs)) - { - foreach($editComponentAttrs AS $k => $v) - { - $this->_def->addAttribute('img', $v, 'CDATA'); - $this->_def->addAttribute('div', $v, 'CDATA'); - } - } - - // add attribute for widget component - $widgetAttrs = $this->_searchWidget($content); - if(is_array($widgetAttrs)) - { - foreach($widgetAttrs AS $k => $v) - { - $this->_def->addAttribute('img', $v, 'CDATA'); - } - } - } - - /** - * Search attribute of edit component tag - * @param string $content - * @return array - */ - private function _searchEditComponent($content) - { - preg_match_all('!<(?:(div)|img)([^>]*)editor_component=([^>]*)>(?(1)(.*?))!is', $content, $m); - - $attributeList = array(); - if(is_array($m[2])) - { - foreach($m[2] as $key => $value) - { - unset($script, $m2); - $script = " {$m[2][$key]} editor_component={$m[3][$key]}"; - - if(preg_match_all('/([a-z0-9_-]+)="([^"]+)"/is', $script, $m2)) - { - foreach($m2[1] as $value2) - { - //SECISSUE check style attr - if($value2 == 'style') - { - continue; - } - $attributeList[] = $value2; - } - } - } - } - - return array_unique($attributeList); - } - - /** - * Search edit component tag - * @param string $content - * @return array - */ - private function _searchWidget(&$content) - { - preg_match_all('!<(?:(div)|img)([^>]*)class="zbxe_widget_output"([^>]*)>(?(1)(.*?))!is', $content, $m); - - $attributeList = array(); - if(is_array($m[3])) - { - $content = str_replace(' $value) - { - if (preg_match_all('/([a-z0-9_-]+)="([^"]+)"/is', $m[3][$key], $m2)) - { - foreach($m2[1] as $value2) - { - //SECISSUE check style attr - if($value2 == 'style') - { - continue; - } - $attributeList[] = $value2; - } - } - } - } - return array_unique($attributeList); - } - - private function _getWhiteDomainRegexp() - { - $oEmbedFilter = EmbedFilter::getInstance(); - $whiteIframeUrlList = $oEmbedFilter->getWhiteIframeUrlList(); - - $whiteDomains = array(); - foreach($whiteIframeUrlList as $domain) - { - $whiteDomains[] = preg_quote($domain, '%'); - } - return '%^https?://(' . implode('|', $whiteDomains) . ')%'; - } - - private function _checkCacheDir() - { - // check htmlpurifier cache directory - $this->_cacheDir = _XE_PATH_ . 'files/cache/htmlpurifier'; - FileHandler::makeDir($this->_cacheDir); - } - + public function purify(&$content) { - $this->_setDefinition($content); - $this->_htmlPurifier = new HTMLPurifier($this->_config); - - $content = $this->_htmlPurifier->purify($content); + $content = Rhymix\Framework\Security\HTMLFilter::clean($content); } } diff --git a/common/framework/security/htmlfilter.php b/common/framework/security/htmlfilter.php new file mode 100644 index 000000000..3c66e2d0a --- /dev/null +++ b/common/framework/security/htmlfilter.php @@ -0,0 +1,275 @@ +purify($input); + $output = self::_decodeWidgetsAndEditorComponents($output); + + foreach (self::$_postproc as $callback) + { + $output = $callback($output); + } + + return $output; + } + + /** + * Get an instance of HTMLPurifier. + * + * @return object + */ + public static function getHTMLPurifier() + { + // Create an instance with reasonable defaults. + if (self::$_htmlpurifier === null) + { + // Get the default configuration. + $config = \HTMLPurifier_Config::createDefault(); + + // Customize the default configuration. + $config->set('Attr.AllowedFrameTargets', array('_blank')); + $config->set('Attr.DefaultImageAlt', ''); + $config->set('Attr.EnableID', false); + $config->set('AutoFormat.AutoParagraph', false); + $config->set('AutoFormat.DisplayLinkURI', false); + $config->set('AutoFormat.Linkify', false); + $config->set('Core.Encoding', 'UTF-8'); + $config->set('HTML.Doctype', 'XHTML 1.0 Transitional'); + $config->set('HTML.FlashAllowFullScreen', true); + $config->set('HTML.MaxImgLength', null); + $config->set('CSS.MaxImgLength', null); + $config->set('CSS.Proprietary', true); + $config->set('Output.FlashCompat', true); + $config->set('Output.Newline', "\n"); + $config->set('URI.MakeAbsolute', false); + + // Allow embedding of external multimedia content. + $config->set('HTML.SafeEmbed', true); + $config->set('HTML.SafeIframe', true); + $config->set('HTML.SafeObject', true); + $config->set('URI.SafeIframeRegexp', self::_getIframeWhitelist()); + + // Set the serializer path. + $config->set('Cache.SerializerPath', RX_BASEDIR . 'files/cache/htmlpurifier'); + \FileHandler::makeDir(RX_BASEDIR . 'files/cache/htmlpurifier'); + + // Modify the HTML definition to support editor components and widgets. + $def = $config->getHTMLDefinition(true); + $def->addAttribute('img', 'editor_component', 'Text'); + $def->addAttribute('img', 'rx_encoded_properties', 'Text'); + $def->addAttribute('div', 'rx_encoded_properties', 'Text'); + + // Support HTML5: Based on https://github.com/xemlock/htmlpurifier-html5 + $def->addAttribute('img', 'srcset', 'Text'); + $def->addAttribute('iframe', 'allowfullscreen', 'Bool'); + $def->addElement('header', 'Block', 'Flow', 'Common'); + $def->addElement('footer', 'Block', 'Flow', 'Common'); + $def->addElement('nav', 'Block', 'Flow', 'Common'); + $def->addElement('main', 'Block', 'Flow', 'Common'); + $def->addElement('section', 'Block', 'Flow', 'Common'); + $def->addElement('article', 'Block', 'Flow', 'Common'); + $def->addElement('aside', 'Block', 'Flow', 'Common'); + $def->addElement('address', 'Block', 'Flow', 'Common'); + $def->addElement('figure', 'Block', 'Optional: (figcaption, Flow) | (Flow, figcaption) | Flow', 'Common'); + $def->addElement('figcaption', 'Inline', 'Flow', 'Common'); + $def->addElement('s', 'Inline', 'Inline', 'Common'); + $def->addElement('var', 'Inline', 'Inline', 'Common'); + $def->addElement('sub', 'Inline', 'Inline', 'Common'); + $def->addElement('sup', 'Inline', 'Inline', 'Common'); + $def->addElement('mark', 'Inline', 'Inline', 'Common'); + $def->addElement('wbr', 'Inline', 'Empty', 'Core'); + $def->addElement('ins', 'Block', 'Flow', 'Common', array('cite' => 'URI', 'datetime' => 'Text')); + $def->addElement('del', 'Block', 'Flow', 'Common', array('cite' => 'URI', 'datetime' => 'Text')); + $time = $def->addElement('time', 'Inline', 'Inline', 'Common', array('datetime' => 'Text', 'pubdate' => 'Bool')); + $time->excludes = array('time' => true); + $def->addElement('audio', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', array( + 'src' => 'URI', + 'type' => 'Text', + 'preload' => 'Enum#auto,metadata,none', + 'controls' => 'Bool', + 'muted' => 'Bool', + 'loop' => 'Bool', + )); + $def->addElement('video', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', array( + 'src' => 'URI', + 'type' => 'Text', + 'width' => 'Length', + 'height' => 'Length', + 'poster' => 'URI', + 'preload' => 'Enum#auto,metadata,none', + 'controls' => 'Bool', + 'muted' => 'Bool', + 'loop' => 'Bool', + )); + $def->addElement('source', 'Block', 'Empty', 'Common', array( + 'src' => 'URI', + 'media' => 'Text', + 'type' => 'Text', + )); + $def->addElement('track', 'Block', 'Empty', 'Common', array( + 'src' => 'URI', + 'srclang' => 'Text', + 'label' => 'Text', + 'kind' => 'Enum#captions,chapters,descriptions,metadata,subtitles', + 'default' => 'Bool', + )); + + // Cache our instance of HTMLPurifier. + self::$_htmlpurifier = new \HTMLPurifier($config); + } + + // Return the cached instance. + return self::$_htmlpurifier; + } + + /** + * Get the iframe whitelist as a regular expression. + * + * @return string + */ + protected static function _getIframeWhitelist() + { + $domains = \EmbedFilter::getInstance()->getWhiteIframeUrlList(); + $result = array(); + foreach($domains as $domain) + { + $result[] = preg_quote($domain, '%'); + } + return '%^https?://(' . implode('|', $result) . ')%'; + } + + /** + * Encode widgets and editor components before processing. + * + * @param string $content + * @return string + */ + protected static function _encodeWidgetsAndEditorComponents($content) + { + preg_match_all('!<(div|img)([^>]*)(editor_component="[^"]+"|class="zbxe_widget_output")([^>]*)>!i', $content, $matches, \PREG_SET_ORDER); + foreach ($matches as $match) + { + $attrs = array(); + $html = $match[0]; + preg_match_all('/([a-zA-Z0-9_-]+)="([^"]+)"/', $match[2] . ' ' . $match[4], $found_attrs, \PREG_SET_ORDER); + foreach ($found_attrs as $attr) + { + $attrkey = strtolower($attr[1]); + if (strtolower($match[1]) === 'img' && ($attrkey === 'width' || $attrkey === 'height' || $attrkey === 'alt')) + { + continue; + } + if ($attrkey === 'src' || $attrkey === 'style' || substr($attrkey, 0, 2) === 'on') + { + continue; + } + $attrs[$attrkey] = htmlspecialchars_decode($attr[2]); + $html = str_replace($attr[0], '', $html); + } + if (strtolower($match[1]) === 'img' && !isset($attrs['src'])) + { + //$html = substr($html, 0, 4) . ' src=""' . substr($html, 4); + } + $encoded_properties = base64_encode(json_encode($attrs)); + $html = substr($html, 0, 4) . ' rx_encoded_properties="' . $encoded_properties . '"' . substr($html, 4); + $content = str_replace($match[0], $html, $content); + } + return $content; + } + + /** + * Decode widgets and editor components after processing. + * + * @param string $content + * @return string + */ + protected static function _decodeWidgetsAndEditorComponents($content) + { + preg_match_all('!<(div|img)([^>]*)(rx_encoded_properties="([^"]+)")!i', $content, $matches, \PREG_SET_ORDER); + foreach ($matches as $match) + { + $attrs = array(); + $decoded_properties = @json_decode(base64_decode($match[4])) ?: array(); + foreach ($decoded_properties as $key => $val) + { + $attrs[] = $key . '="' . htmlspecialchars($val) . '"'; + } + $content = str_replace($match[3], implode(' ', $attrs), $content); + } + return $content; + } +} diff --git a/common/legacy.php b/common/legacy.php index 9f07cf373..b37293abf 100644 --- a/common/legacy.php +++ b/common/legacy.php @@ -815,8 +815,7 @@ function url_decode($str) function purifierHtml(&$content) { - $oPurifier = Purifier::getInstance(); - $oPurifier->purify($content); + $content = Rhymix\Framework\Security\HTMLFilter::clean($content); } /** @@ -830,7 +829,7 @@ function removeHackTag($content) $oEmbedFilter = EmbedFilter::getInstance(); $oEmbedFilter->check($content); - purifierHtml($content); + $content = Rhymix\Framework\Security\HTMLFilter::clean($content); // change the specific tags to the common texts $content = preg_replace('@<(\/?(?:html|body|head|title|meta|base|link|script|style|applet)(/*).*?>)@i', '<$1', $content); From e405fc69acf5506aa7b4ec6b48100c6d84cd8983 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 11 Mar 2016 15:36:36 +0900 Subject: [PATCH 038/205] Update unit tests --- tests/unit/framework/DebugTest.php | 9 +-- tests/unit/framework/SecurityTest.php | 0 .../framework/security/HTMLFilterTest.php | 68 +++++++++++++++++++ tests/unit/functions/LegacyTest.php | 65 +----------------- 4 files changed, 74 insertions(+), 68 deletions(-) create mode 100644 tests/unit/framework/SecurityTest.php create mode 100644 tests/unit/framework/security/HTMLFilterTest.php diff --git a/tests/unit/framework/DebugTest.php b/tests/unit/framework/DebugTest.php index b6e4328d5..444ebac1f 100644 --- a/tests/unit/framework/DebugTest.php +++ b/tests/unit/framework/DebugTest.php @@ -45,10 +45,11 @@ class DebugTest extends \Codeception\TestCase\Test 'backtrace' => array(), )); $queries = Rhymix\Framework\Debug::getQueries(); - $this->assertEquals(1, count($queries)); - $this->assertEquals('SELECT foo FROM bar', $queries[0]->query_string); - $this->assertEquals('This is a unit test', $queries[0]->message); - $this->assertEquals(1234, $queries[0]->error_code); + $this->assertGreaterThanOrEqual(1, count($queries)); + $query = array_pop($queries); + $this->assertEquals('SELECT foo FROM bar', $query->query_string); + $this->assertEquals('This is a unit test', $query->message); + $this->assertEquals(1234, $query->error_code); } public function testDebugTranslateFilename() diff --git a/tests/unit/framework/SecurityTest.php b/tests/unit/framework/SecurityTest.php new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/framework/security/HTMLFilterTest.php b/tests/unit/framework/security/HTMLFilterTest.php new file mode 100644 index 000000000..d8fe40f28 --- /dev/null +++ b/tests/unit/framework/security/HTMLFilterTest.php @@ -0,0 +1,68 @@ +

IFrame

', + '' + ), + // expression + array( + '
', + '
' + ), + // no quotes and no semicolon - http://ha.ckers.org/xss.html + array( + '', + '' + ), + // embedded encoded tab to break up XSS - http://ha.ckers.org/xss.html + array( + '', + '' + ), + // issue 178 + array( + '', + '' + ), + // issue 534 + array( + '*/" onerror="console.log(\'Yet another XSS\')">', + '*/" onerror="console.log(\'Yet another XSS\')">' + ), + // issue 602 + array( + 'test', + 'test' + ), + // issue #1813 https://github.com/xpressengine/xe-core/issues/1813 + array( + 'dummy', + 'dummy' + ), + array( + 'dummy', + 'dummy' + ), + array( + "\"dummy\"", + 'dummy' + ), + array( + "\"dummy\"", + 'dummy' + ) + ); + + foreach ($tests as $test) + { + $result = removeHackTag($test[0]); + $this->assertEquals($test[1], $result); + } + } +} diff --git a/tests/unit/functions/LegacyTest.php b/tests/unit/functions/LegacyTest.php index 65550545f..413b1f9cd 100644 --- a/tests/unit/functions/LegacyTest.php +++ b/tests/unit/functions/LegacyTest.php @@ -2,68 +2,5 @@ class LegacyTest extends \Codeception\TestCase\Test { - public function testRemoveHackTag() - { - $tests = array( - // remove iframe - array( - '', - // '
<iframe src="path/to/file.html"></iframe>

IFrame

' - '' - ), - // expression - array( - '
', - '
' - ), - // no quotes and no semicolon - http://ha.ckers.org/xss.html - array( - '', - '' - ), - // embedded encoded tab to break up XSS - http://ha.ckers.org/xss.html - array( - '', - 'jav ascript:alert(\'XSS\');' - ), - // issue 178 - array( - '', - 'invalid.jpg' - ), - // issue 534 - array( - '*/" onerror="console.log(\'Yet another XSS\')">', - 'as"df dummy=*/" onerror="console.log(\'Yet another XSS\')">' - ), - // issue 602 - array( - 'test', - 'test' - ), - // issue #1813 https://github.com/xpressengine/xe-core/issues/1813 - array( - 'dummy', - 'dummy' - ), - array( - 'dummy', - 'dummy' - ), - array( - "\"dummy\"", - 'dummy' - ), - array( - "\"dummy\"", - 'dummy' - ) - ); - - foreach ($tests as $test) - { - $result = removeHackTag($test[0]); - $this->assertEquals($test[1], $result); - } - } + } From 10e8962fa14b2cbfc77f57169915442433ea1c2d Mon Sep 17 00:00:00 2001 From: conory Date: Fri, 11 Mar 2016 16:15:47 +0900 Subject: [PATCH 039/205] =?UTF-8?q?=EB=AC=B8=EC=84=9C=EA=B0=80=20=EC=97=86?= =?UTF-8?q?=EC=9D=84=20=EA=B2=BD=EC=9A=B0=20404=20=EC=97=90=EB=9F=AC?= =?UTF-8?q?=EB=A5=BC=20=EB=82=B4=EB=8F=84=EB=A1=9D=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- classes/module/ModuleHandler.class.php | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/classes/module/ModuleHandler.class.php b/classes/module/ModuleHandler.class.php index 14ea2e547..5f6990fad 100644 --- a/classes/module/ModuleHandler.class.php +++ b/classes/module/ModuleHandler.class.php @@ -168,12 +168,14 @@ class ModuleHandler extends Handler // Get module's information based on document_srl, if it's specified if($this->document_srl) { - $module_info = $oModuleModel->getModuleInfoByDocumentSrl($this->document_srl); - // If the document does not exist, remove document_srl + + // redirect, if the document does not exist if(!$module_info) { - unset($this->document_srl); + $this->error = 'The document does not exist'; + $this->httpStatusCode = '404'; + return true; } else { @@ -181,20 +183,17 @@ class ModuleHandler extends Handler // if mids are not matching, set it as the document's mid if(!$this->mid || ($this->mid != $module_info->mid)) { - if(Context::getRequestMethod() == 'GET') { Context::setCacheControl(0); - $this->mid = $module_info->mid; - header('location: ' . getNotEncodedSiteUrl($site_module_info->domain, 'mid', $this->mid, 'document_srl', $this->document_srl), true, 301); - return FALSE; + header('location: ' . getNotEncodedSiteUrl($site_module_info->domain, 'mid', $module_info->mid, 'document_srl', $this->document_srl), true, 301); + return false; } else { $this->mid = $module_info->mid; Context::set('mid', $this->mid); } - } // if requested module is different from one of the document, remove the module information retrieved based on the document number if($this->module && $module_info->module != $this->module) @@ -202,7 +201,6 @@ class ModuleHandler extends Handler unset($module_info); } } - } // If module_info is not set yet, and there exists mid information, get module information based on the mid @@ -218,7 +216,7 @@ class ModuleHandler extends Handler Context::setCacheControl(0); $site_info = $oModuleModel->getSiteInfo($site_module_info->module_site_srl); header('location: ' . getNotEncodedSiteUrl($site_info->domain, 'mid', $site_module_info->mid), true, 301); - return FALSE; + return false; } // If module_info is not set still, and $module does not exist, find the default module @@ -249,7 +247,7 @@ class ModuleHandler extends Handler Context::setCacheControl(0); header("Location: $redirect_url", true, 301); - return FALSE; + return false; } // If module info was set, retrieve variables from the module information @@ -297,6 +295,7 @@ class ModuleHandler extends Handler { $this->error = 'msg_module_is_not_exists'; $this->httpStatusCode = '404'; + return true; } // If mid exists, set mid into context @@ -310,13 +309,13 @@ class ModuleHandler extends Handler if(!$output->toBool()) { $this->error = $output->getMessage(); - return TRUE; + return true; } // Set current module info into context Context::set('current_module_info', $this->module_info); - return TRUE; + return true; } /** From 4594c4032116fa96547165ceb5a4c31355762da5 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Fri, 11 Mar 2016 19:55:41 +0900 Subject: [PATCH 040/205] Disable useless UploadFileFilter --- classes/security/UploadFileFilter.class.php | 33 +-------------------- 1 file changed, 1 insertion(+), 32 deletions(-) diff --git a/classes/security/UploadFileFilter.class.php b/classes/security/UploadFileFilter.class.php index 56cfdb9bf..b19bd6889 100644 --- a/classes/security/UploadFileFilter.class.php +++ b/classes/security/UploadFileFilter.class.php @@ -3,40 +3,9 @@ class UploadFileFilter { - private static $_block_list = array ('exec', 'system', 'passthru', 'show_source', 'phpinfo', 'fopen', 'file_get_contents', 'file_put_contents', 'fwrite', 'proc_open', 'popen'); - public function check($file) { - // TODO: 기능개선후 enable - - return TRUE; // disable - if (! $file || ! FileHandler::exists($file)) return TRUE; - return self::_check ( $file ); - } - - private function _check($file) - { - if (! ($fp = fopen ( $file, 'r' ))) return FALSE; - - $has_php_tag = FALSE; - - while ( ! feof ( $fp ) ) - { - $content = fread ( $fp, 8192 ); - if (FALSE === $has_php_tag) $has_php_tag = strpos ( $content, ' Date: Fri, 11 Mar 2016 19:56:04 +0900 Subject: [PATCH 041/205] Add filename filter class --- common/framework/security/filenamefilter.php | 49 ++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 common/framework/security/filenamefilter.php diff --git a/common/framework/security/filenamefilter.php b/common/framework/security/filenamefilter.php new file mode 100644 index 000000000..ff146230d --- /dev/null +++ b/common/framework/security/filenamefilter.php @@ -0,0 +1,49 @@ +', '{', '}', ':', ';', '|', '"', '~', '`', '@', '#', '$', '%', '^', '&', '*', '?'); + $replace = array('', '', '(', ')', '(', ')', '_', ',', '_', '', '_', '\'', '_', '_', '_', '_', '_', '_', '', ''); + $filename = str_replace($illegal, $replace, $filename); + + // Remove control characters. + $filename = preg_replace('/([\\x00-\\x1f\\x7f\\xff]+)/u', '', $filename); + + // Standardize whitespace characters. + $filename = trim(preg_replace('/[\\pZ\\pC]+/u', ' ', $filename)); + + // Remove excess spaces and replacement characters. + $filename = trim($filename, ' .-_'); + $filename = preg_replace('/__+/', '_', $filename); + + // Change .php files to .phps to make them non-executable. + if (strtolower(substr($filename, strlen($filename) - 4)) === '.php') + { + $filename = substr($filename, 0, strlen($filename) - 4) . '.phps'; + } + + // Truncate filenames over 127 chars long, or extensions over 16 chars long. + if (mb_strlen($filename, 'UTF-8') > 127) + { + $extension = strrchr($filename, '.'); + if (mb_strlen($extension, 'UTF-8') > 16) $extension = mb_substr($extension, 0, 16); + $filename = mb_substr($filename, 0, 127 - mb_strlen($extension)) . $extension; + } + + return $filename; + } +} From 8cbc3792809a056606914b532637579b79830d97 Mon Sep 17 00:00:00 2001 From: conory Date: Fri, 11 Mar 2016 22:19:04 +0900 Subject: [PATCH 042/205] =?UTF-8?q?GET=20=EC=9D=BC=20=EA=B2=BD=EC=9A=B0?= =?UTF-8?q?=EC=97=90=EB=A7=8C=20404=20=EC=97=90=EB=9F=AC=EB=A5=BC=20?= =?UTF-8?q?=EB=82=B4=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- classes/module/ModuleHandler.class.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/classes/module/ModuleHandler.class.php b/classes/module/ModuleHandler.class.php index 5f6990fad..dcad62e66 100644 --- a/classes/module/ModuleHandler.class.php +++ b/classes/module/ModuleHandler.class.php @@ -170,12 +170,19 @@ class ModuleHandler extends Handler { $module_info = $oModuleModel->getModuleInfoByDocumentSrl($this->document_srl); - // redirect, if the document does not exist + // If the document does not exist, remove document_srl if(!$module_info) { - $this->error = 'The document does not exist'; - $this->httpStatusCode = '404'; - return true; + if(Context::getRequestMethod() == 'GET') + { + $this->error = 'The document does not exist'; + $this->httpStatusCode = '404'; + return true; + } + else + { + unset($this->document_srl); + } } else { @@ -471,7 +478,6 @@ class ModuleHandler extends Handler // If there is no such action in the module object if(!isset($xml_info->action->{$this->act}) || !method_exists($oModule, $this->act)) { - if(!Context::isInstalled()) { self::_setInputErrorToContext(); @@ -766,10 +772,6 @@ class ModuleHandler extends Handler } self::_setInputValueToSession(); } - else - { - - } if($error != 0) { @@ -945,13 +947,11 @@ class ModuleHandler extends Handler if($layout_srl && !$oModule->getLayoutFile()) { - // If layout_srl exists, get information of the layout, and set the location of layout_path/ layout_file $oLayoutModel = getModel('layout'); $layout_info = $oLayoutModel->getLayout($layout_srl); if($layout_info) { - // Input extra_vars into $layout_info if($layout_info->extra_var_count) { From f4e3d5762e1f8e4d5143218a091d4754e0de8524 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 12 Mar 2016 14:37:54 +0900 Subject: [PATCH 043/205] Support more CSS2 and CSS3 properties --- common/framework/security/htmlfilter.php | 283 ++++++++++++++++++----- 1 file changed, 229 insertions(+), 54 deletions(-) diff --git a/common/framework/security/htmlfilter.php b/common/framework/security/htmlfilter.php index 3c66e2d0a..562dd6644 100644 --- a/common/framework/security/htmlfilter.php +++ b/common/framework/security/htmlfilter.php @@ -133,60 +133,9 @@ class HTMLFilter $def->addAttribute('img', 'rx_encoded_properties', 'Text'); $def->addAttribute('div', 'rx_encoded_properties', 'Text'); - // Support HTML5: Based on https://github.com/xemlock/htmlpurifier-html5 - $def->addAttribute('img', 'srcset', 'Text'); - $def->addAttribute('iframe', 'allowfullscreen', 'Bool'); - $def->addElement('header', 'Block', 'Flow', 'Common'); - $def->addElement('footer', 'Block', 'Flow', 'Common'); - $def->addElement('nav', 'Block', 'Flow', 'Common'); - $def->addElement('main', 'Block', 'Flow', 'Common'); - $def->addElement('section', 'Block', 'Flow', 'Common'); - $def->addElement('article', 'Block', 'Flow', 'Common'); - $def->addElement('aside', 'Block', 'Flow', 'Common'); - $def->addElement('address', 'Block', 'Flow', 'Common'); - $def->addElement('figure', 'Block', 'Optional: (figcaption, Flow) | (Flow, figcaption) | Flow', 'Common'); - $def->addElement('figcaption', 'Inline', 'Flow', 'Common'); - $def->addElement('s', 'Inline', 'Inline', 'Common'); - $def->addElement('var', 'Inline', 'Inline', 'Common'); - $def->addElement('sub', 'Inline', 'Inline', 'Common'); - $def->addElement('sup', 'Inline', 'Inline', 'Common'); - $def->addElement('mark', 'Inline', 'Inline', 'Common'); - $def->addElement('wbr', 'Inline', 'Empty', 'Core'); - $def->addElement('ins', 'Block', 'Flow', 'Common', array('cite' => 'URI', 'datetime' => 'Text')); - $def->addElement('del', 'Block', 'Flow', 'Common', array('cite' => 'URI', 'datetime' => 'Text')); - $time = $def->addElement('time', 'Inline', 'Inline', 'Common', array('datetime' => 'Text', 'pubdate' => 'Bool')); - $time->excludes = array('time' => true); - $def->addElement('audio', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', array( - 'src' => 'URI', - 'type' => 'Text', - 'preload' => 'Enum#auto,metadata,none', - 'controls' => 'Bool', - 'muted' => 'Bool', - 'loop' => 'Bool', - )); - $def->addElement('video', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', array( - 'src' => 'URI', - 'type' => 'Text', - 'width' => 'Length', - 'height' => 'Length', - 'poster' => 'URI', - 'preload' => 'Enum#auto,metadata,none', - 'controls' => 'Bool', - 'muted' => 'Bool', - 'loop' => 'Bool', - )); - $def->addElement('source', 'Block', 'Empty', 'Common', array( - 'src' => 'URI', - 'media' => 'Text', - 'type' => 'Text', - )); - $def->addElement('track', 'Block', 'Empty', 'Common', array( - 'src' => 'URI', - 'srclang' => 'Text', - 'label' => 'Text', - 'kind' => 'Enum#captions,chapters,descriptions,metadata,subtitles', - 'default' => 'Bool', - )); + // Support HTML5 and CSS3. + self::_supportHTML5($config); + self::_supportCSS3($config); // Cache our instance of HTMLPurifier. self::$_htmlpurifier = new \HTMLPurifier($config); @@ -196,6 +145,232 @@ class HTMLFilter return self::$_htmlpurifier; } + /** + * Patch HTMLPurifier to support some HTML5 tags and attributes. + * + * These changes are based on https://github.com/xemlock/htmlpurifier-html5 + * but modified to support even more tags and attributes. + * + * @param object $config + * @return void + */ + protected static function _supportHTML5($config) + { + // Get the HTML definition. + $def = $config->getHTMLDefinition(true); + + // Add various block-level tags. + $def->addElement('header', 'Block', 'Flow', 'Common'); + $def->addElement('footer', 'Block', 'Flow', 'Common'); + $def->addElement('nav', 'Block', 'Flow', 'Common'); + $def->addElement('main', 'Block', 'Flow', 'Common'); + $def->addElement('section', 'Block', 'Flow', 'Common'); + $def->addElement('article', 'Block', 'Flow', 'Common'); + $def->addElement('aside', 'Block', 'Flow', 'Common'); + $def->addElement('address', 'Block', 'Flow', 'Common'); + + // Add various inline tags. + $def->addElement('s', 'Inline', 'Inline', 'Common'); + $def->addElement('var', 'Inline', 'Inline', 'Common'); + $def->addElement('sub', 'Inline', 'Inline', 'Common'); + $def->addElement('sup', 'Inline', 'Inline', 'Common'); + $def->addElement('mark', 'Inline', 'Inline', 'Common'); + $def->addElement('wbr', 'Inline', 'Empty', 'Core'); + + // Support figures. + $def->addElement('figure', 'Block', 'Optional: (figcaption, Flow) | (Flow, figcaption) | Flow', 'Common'); + $def->addElement('figcaption', 'Inline', 'Flow', 'Common'); + + // Support insertions and deletions. + $def->addElement('ins', 'Block', 'Flow', 'Common', array('cite' => 'URI', 'datetime' => 'Text')); + $def->addElement('del', 'Block', 'Flow', 'Common', array('cite' => 'URI', 'datetime' => 'Text')); + + // Support the
+ diff --git a/modules/board/skins/xedition/update_view.html b/modules/board/skins/xedition/update_view.html index 27bd7220c..0b025ae7a 100644 --- a/modules/board/skins/xedition/update_view.html +++ b/modules/board/skins/xedition/update_view.html @@ -27,10 +27,11 @@
-
+ - +
+ From 1c32e993abaefce4bdba93ee4cf221808039ec4a Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 12 Mar 2016 17:42:28 +0900 Subject: [PATCH 049/205] Add unit tests for FilenameFilter class --- .../framework/security/FilenameFilterTest.php | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/unit/framework/security/FilenameFilterTest.php diff --git a/tests/unit/framework/security/FilenameFilterTest.php b/tests/unit/framework/security/FilenameFilterTest.php new file mode 100644 index 000000000..935ffd473 --- /dev/null +++ b/tests/unit/framework/security/FilenameFilterTest.php @@ -0,0 +1,42 @@ + 'foo.bar', + 'foobar{baz}.jpg' => 'foobar(baz).jpg', + 'foobar^%.docx' => 'foobar_.docx', + + // Control characters + 'foobar' . chr(127) . '.gif' => 'foobar.gif', + 'foobar' . "\t\r\n" . '.gif' => 'foobar.gif', + + // Unicode whitepace characters + 'foobar' . html_entity_decode(' ') . ' space.gif' => 'foobar space.gif', + 'hello world.png' => 'hello world.png', + + // Extra symbols + '_foobar.jpg-' => 'foobar.jpg', + '.htaccess' => 'htaccess', + + // PHP extension + 'foobar.php' => 'foobar.phps', + 'foobar.php.jpg' => 'foobar.php.jpg', + + // Overlong filenames + str_repeat('f', 200) . '.' . str_repeat('b', 30) => str_repeat('f', 111) . '.' . str_repeat('b', 15), + str_repeat('한글', 100) . '.hwp' => str_repeat('한글', 61) . '한.hwp', + + ); + + foreach ($tests as $from => $to) + { + $result = Rhymix\Framework\Security\FilenameFilter::clean($from); + $this->assertEquals($to, $result); + } + } +} From 6d13142f30bfd4b5ca764f39f2333c93cd1e7a11 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 12 Mar 2016 17:44:50 +0900 Subject: [PATCH 050/205] Use FilenameFilter in file module --- modules/file/file.controller.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/file/file.controller.php b/modules/file/file.controller.php index 11b876063..78658ba80 100644 --- a/modules/file/file.controller.php +++ b/modules/file/file.controller.php @@ -732,10 +732,8 @@ class fileController extends file } } - // https://github.com/xpressengine/xe-core/issues/1713 - $file_info['name'] = preg_replace('/\.(php|phtm|phar|html?|cgi|pl|exe|jsp|asp|inc)/i', '$0-x',$file_info['name']); - $file_info['name'] = removeHackTag($file_info['name']); - $file_info['name'] = str_replace(array('<','>'),array('%3C','%3E'),$file_info['name']); + // Sanitize filename + $file_info['name'] = Rhymix\Framework\Security\FilenameFilter::clean($file_info['name']); // Get random number generator $random = new Password(); From 78238857d407dd60c4f193c5c8727d45a62570da Mon Sep 17 00:00:00 2001 From: BJRambo Date: Sat, 12 Mar 2016 17:52:38 +0900 Subject: [PATCH 051/205] add check alert. --- modules/board/lang/ko.php | 1 + modules/board/skins/xedition/update_view.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/board/lang/ko.php b/modules/board/lang/ko.php index c991091bb..0c627e9d1 100644 --- a/modules/board/lang/ko.php +++ b/modules/board/lang/ko.php @@ -64,3 +64,4 @@ $lang->msg_no_update_id = '업데이트 고유 번호는 필수입니다.'; $lang->msg_no_update_log = '업데이트 로그가 존재하지 않습니다.'; $lang->cmd_modify_by_update_log = '이 로그로 게시물을 수정하기'; $lang->msg_admin_update_log = '관리자가 수정한 적이 있는 게시물입니다. 관리자에게 문의하세요.'; +$lang->msg_update_log_revert = '정말로 이 게시물로 되돌리시겠습니까?'; diff --git a/modules/board/skins/xedition/update_view.html b/modules/board/skins/xedition/update_view.html index 0b025ae7a..487b11568 100644 --- a/modules/board/skins/xedition/update_view.html +++ b/modules/board/skins/xedition/update_view.html @@ -27,7 +27,7 @@ -
+ From b693c8cdadad1f854f5ed3a8912726342734719c Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 12 Mar 2016 19:44:05 +0900 Subject: [PATCH 052/205] Add unit tests for Security and URL classes --- common/framework/url.php | 4 +- tests/unit/framework/SecurityTest.php | 60 ++++++++++++++++++++++ tests/unit/framework/URLTest.php | 72 +++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 tests/unit/framework/URLTest.php diff --git a/common/framework/url.php b/common/framework/url.php index 5f55f4f76..1829fbe5c 100644 --- a/common/framework/url.php +++ b/common/framework/url.php @@ -44,7 +44,7 @@ class URL */ public static function getCanonicalURL($url) { - if (preg_match('#^\.?/([^/]|$)#', $url)) + if (preg_match('#^\.?/([^/]|$)#', $url) || !preg_match('#^(https?:|/)#', $url)) { $proto = \RX_SSL ? 'https://' : 'http://'; $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost'; @@ -89,7 +89,7 @@ class URL return true; } - if ($domain === self::getDomainFromURL($_SERVER['HTTP_HOST'])) + if ($domain === self::getDomainFromURL('http://' . $_SERVER['HTTP_HOST'])) { return true; } diff --git a/tests/unit/framework/SecurityTest.php b/tests/unit/framework/SecurityTest.php index e69de29bb..72b2e45a6 100644 --- a/tests/unit/framework/SecurityTest.php +++ b/tests/unit/framework/SecurityTest.php @@ -0,0 +1,60 @@ +assertEquals('foo<bar>', Rhymix\Framework\Security::sanitize('foo', 'escape')); + + // Strip + $this->assertEquals('foobar', Rhymix\Framework\Security::sanitize('foo

bar

', 'strip')); + + // HTML (more thorough tests in HTMLFilterTest) + $this->assertEquals('

safe

', Rhymix\Framework\Security::sanitize('

safe

', 'html')); + + // Filename (more thorough tests in FilenameFilterTest) + $this->assertEquals('foo(bar).xls', Rhymix\Framework\Security::sanitize('foo.xls', 'filename')); + } + + public function testCheckCSRF() + { + $_SERVER['REQUEST_METHOD'] = 'GET'; + $_SERVER['HTTP_REFERER'] = ''; + $this->assertFalse(Rhymix\Framework\Security::checkCSRF()); + + $_SERVER['REQUEST_METHOD'] = 'POST'; + $this->assertTrue(Rhymix\Framework\Security::checkCSRF()); + + $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; + $_SERVER['HTTP_REFERER'] = 'http://www.foobar.com/'; + $this->assertFalse(Rhymix\Framework\Security::checkCSRF()); + + $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; + $this->assertTrue(Rhymix\Framework\Security::checkCSRF('http://www.rhymix.org/')); + } + + public function testCheckXEE() + { + $xml = ''; + $this->assertTrue(Rhymix\Framework\Security::checkXEE($xml)); + + $xml = ''; + $this->assertTrue(Rhymix\Framework\Security::checkXEE($xml)); + + $xml = ''; + $this->assertTrue(Rhymix\Framework\Security::checkXEE($xml)); + + $xml = ''; + $this->assertFalse(Rhymix\Framework\Security::checkXEE($xml)); + + $xml = ''; + $this->assertFalse(Rhymix\Framework\Security::checkXEE($xml)); + + $xml = ''; + $this->assertFalse(Rhymix\Framework\Security::checkXEE($xml)); + + $xml = ']>'; + $this->assertFalse(Rhymix\Framework\Security::checkXEE($xml)); + } +} diff --git a/tests/unit/framework/URLTest.php b/tests/unit/framework/URLTest.php new file mode 100644 index 000000000..93410ed58 --- /dev/null +++ b/tests/unit/framework/URLTest.php @@ -0,0 +1,72 @@ +assertEquals($full_url, Rhymix\Framework\URL::getCurrentURL()); + + // Adding items to the query string + $this->assertEquals($full_url . '&var=1&arr%5B0%5D=2&arr%5B1%5D=3', Rhymix\Framework\URL::getCurrentURL(array('var' => '1', 'arr' => array(2, 3)))); + + // Removing item from the query string + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php?xe=sucks', Rhymix\Framework\URL::getCurrentURL(array('foo' => null))); + + // Adding and removing parameters at the same time + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php?xe=sucks&l=ko', Rhymix\Framework\URL::getCurrentURL(array('l' => 'ko', 'foo' => null))); + } + + public function testGetCanonicalURL() + { + $protocol = \RX_SSL ? 'https://' : 'http://'; + $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; + + $tests = array( + 'foo/bar' => $protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'foo/bar', + './foo/bar' => $protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'foo/bar', + '/foo/bar' => $protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'foo/bar', + '//www.example.com/foo' => $protocol . 'www.example.com/foo', + 'http://xn--cg4bkiv2oina.com/' => 'http://삼성전자.com/', + ); + + foreach ($tests as $from => $to) + { + $this->assertEquals($to, Rhymix\Framework\URL::getCanonicalURL($from)); + } + } + + public function testGetDomainFromURL() + { + $tests = array( + 'https://www.rhymix.org/foo/bar' => 'www.rhymix.org', + 'https://www.rhymix.org:8080/foo/bar' => 'www.rhymix.org', + 'http://xn--cg4bkiv2oina.com/' => '삼성전자.com', + ); + + foreach ($tests as $from => $to) + { + $this->assertEquals($to, Rhymix\Framework\URL::getDomainFromURL($from)); + } + } + + public function testIsInternalURL() + { + // This function is checked in Security::checkCSRF() + } + + public function testEncodeIdna() + { + $this->assertEquals('xn--9i1bl3b186bf9e.xn--3e0b707e', Rhymix\Framework\URL::encodeIdna('퓨니코드.한국')); + } + + public function testDecodeIdna() + { + $this->assertEquals('퓨니코드.한국', Rhymix\Framework\URL::decodeIdna('xn--9i1bl3b186bf9e.xn--3e0b707e')); + } +} From 37c07f7b6eebe1907727952ac7e44047a4f28b3e Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 12 Mar 2016 19:59:10 +0900 Subject: [PATCH 053/205] Rename old SecurityTest class --- tests/unit/classes/{SecurityTest.php => OldSecurityTest.php} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename tests/unit/classes/{SecurityTest.php => OldSecurityTest.php} (99%) diff --git a/tests/unit/classes/SecurityTest.php b/tests/unit/classes/OldSecurityTest.php similarity index 99% rename from tests/unit/classes/SecurityTest.php rename to tests/unit/classes/OldSecurityTest.php index f4cb7127d..f699b8d1b 100644 --- a/tests/unit/classes/SecurityTest.php +++ b/tests/unit/classes/OldSecurityTest.php @@ -1,7 +1,7 @@ Date: Sat, 12 Mar 2016 21:03:26 +0900 Subject: [PATCH 054/205] Remove all calls to checkUploadedFile() --- common/legacy.php | 2 +- modules/file/file.controller.php | 4 +-- .../integration_search.admin.controller.php | 2 +- modules/layout/layout.admin.controller.php | 7 ++--- modules/member/member.controller.php | 10 ------ modules/menu/menu.admin.controller.php | 31 +++++-------------- modules/module/module.admin.controller.php | 2 +- modules/module/module.controller.php | 6 ---- modules/rss/rss.admin.controller.php | 2 +- 9 files changed, 15 insertions(+), 51 deletions(-) diff --git a/common/legacy.php b/common/legacy.php index e2377b234..99a3bcd13 100644 --- a/common/legacy.php +++ b/common/legacy.php @@ -868,7 +868,7 @@ function blockWidgetCode($content) */ function checkUploadedFile($file) { - return UploadFileFilter::check($file); + return true; } /** diff --git a/modules/file/file.controller.php b/modules/file/file.controller.php index 78658ba80..65a4720a1 100644 --- a/modules/file/file.controller.php +++ b/modules/file/file.controller.php @@ -763,12 +763,10 @@ class fileController extends file $filename = $path.$random->createSecureSalt(32, 'hex'); $direct_download = 'N'; } + // Create a directory if(!FileHandler::makeDir($path)) return new Object(-1,'msg_not_permitted_create'); - // Check uploaded file - if(!checkUploadedFile($file_info['tmp_name'])) return new Object(-1,'msg_file_upload_error'); - // Get random number generator $random = new Password(); diff --git a/modules/integration_search/integration_search.admin.controller.php b/modules/integration_search/integration_search.admin.controller.php index 232af8d62..3bd38c4e4 100644 --- a/modules/integration_search/integration_search.admin.controller.php +++ b/modules/integration_search/integration_search.admin.controller.php @@ -85,7 +85,7 @@ class integration_searchAdminController extends integration_search continue; } // Ignore if the file is not successfully uploaded, and check uploaded file - if(!is_uploaded_file($image_obj['tmp_name']) || !checkUploadedFile($image_obj['tmp_name'])) + if(!is_uploaded_file($image_obj['tmp_name'])) { unset($obj->{$vars->name}); continue; diff --git a/modules/layout/layout.admin.controller.php b/modules/layout/layout.admin.controller.php index 6a3342021..572d052ee 100644 --- a/modules/layout/layout.admin.controller.php +++ b/modules/layout/layout.admin.controller.php @@ -435,9 +435,6 @@ class layoutAdminController extends layout $ext = substr(strrchr($filename,'.'),1); $filename = sprintf('%s.%s', md5($filename), $ext); } - - // Check uploaded file - if(!checkUploadedFile($source['tmp_name'])) return false; if(file_exists($path .'/'. $filename)) @unlink($path . $filename); if(!move_uploaded_file($source['tmp_name'], $path . $filename )) return false; @@ -690,7 +687,7 @@ class layoutAdminController extends layout // check upload if(!Context::isUploaded()) exit(); $file = Context::get('file'); - if(!is_uploaded_file($file['tmp_name']) || !checkUploadedFile($file['tmp_name'])) exit(); + if(!is_uploaded_file($file['tmp_name'])) exit(); if(substr_compare($file['name'], '.tar', -4) !== 0) exit(); @@ -925,7 +922,7 @@ class layoutAdminController extends layout $this->setTemplatePath($this->module_path.'tpl'); $this->setTemplateFile("after_upload_config_image.html"); - if(!$img['tmp_name'] || !is_uploaded_file($img['tmp_name']) || !checkUploadedFile($img['tmp_name'])) + if(!$img['tmp_name'] || !is_uploaded_file($img['tmp_name'])) { Context::set('msg', lang('upload failed')); return; diff --git a/modules/member/member.controller.php b/modules/member/member.controller.php index c6bb4c134..a085d1c4e 100644 --- a/modules/member/member.controller.php +++ b/modules/member/member.controller.php @@ -729,10 +729,6 @@ class memberController extends member */ function insertProfileImage($member_srl, $target_file) { - - // Check uploaded file - if(!checkUploadedFile($target_file)) return; - $oMemberModel = getModel('member'); $config = $oMemberModel->getMemberConfig(); @@ -808,9 +804,6 @@ class memberController extends member */ function insertImageName($member_srl, $target_file) { - // Check uploaded file - if(!checkUploadedFile($target_file)) return; - $oModuleModel = getModel('module'); $config = $oModuleModel->getModuleConfig('member'); // Get an image size @@ -917,9 +910,6 @@ class memberController extends member */ function insertImageMark($member_srl, $target_file) { - // Check uploaded file - if(!checkUploadedFile($target_file)) return; - $oModuleModel = getModel('module'); $config = $oModuleModel->getModuleConfig('member'); // Get an image size diff --git a/modules/menu/menu.admin.controller.php b/modules/menu/menu.admin.controller.php index c007af59b..b22f8f39e 100644 --- a/modules/menu/menu.admin.controller.php +++ b/modules/menu/menu.admin.controller.php @@ -1520,7 +1520,7 @@ class menuAdminController extends menu Context::set('error_messge', lang('msg_invalid_request')); } - else if(!$target_file || !is_uploaded_file($target_file['tmp_name']) || !preg_match('/\.(gif|jpeg|jpg|png)$/i',$target_file['name']) || !checkUploadedFile($target_file['tmp_name'])) + else if(!$target_file || !is_uploaded_file($target_file['tmp_name']) || !preg_match('/\.(gif|jpeg|jpg|png)$/i',$target_file['name'])) { Context::set('error_messge', lang('msg_invalid_request')); } @@ -2132,19 +2132,15 @@ class menuAdminController extends menu $returnArray = array(); $date = date('YmdHis'); + // normal button if($args->menu_normal_btn) { $tmp_arr = explode('.',$args->menu_normal_btn['name']); $ext = $tmp_arr[count($tmp_arr)-1]; - $filename = sprintf('%s%d.%s.%s.%s', $path, $args->menu_item_srl, $date, 'menu_normal_btn', $ext); - - if(checkUploadedFile($args->menu_normal_btn['tmp_name'])) - { - move_uploaded_file ( $args->menu_normal_btn ['tmp_name'], $filename ); - $returnArray ['normal_btn'] = $filename; - } + move_uploaded_file($args->menu_normal_btn['tmp_name'], $filename); + $returnArray['normal_btn'] = $filename; } // hover button @@ -2152,14 +2148,9 @@ class menuAdminController extends menu { $tmp_arr = explode('.',$args->menu_hover_btn['name']); $ext = $tmp_arr[count($tmp_arr)-1]; - $filename = sprintf('%s%d.%s.%s.%s', $path, $args->menu_item_srl, $date, 'menu_hover_btn', $ext); - - if(checkUploadedFile($args->menu_hover_btn['tmp_name'])) - { - move_uploaded_file($args->menu_hover_btn['tmp_name'], $filename); - $returnArray['hover_btn'] = $filename; - } + move_uploaded_file($args->menu_hover_btn['tmp_name'], $filename); + $returnArray['hover_btn'] = $filename; } // active button @@ -2167,15 +2158,9 @@ class menuAdminController extends menu { $tmp_arr = explode('.',$args->menu_active_btn['name']); $ext = $tmp_arr[count($tmp_arr)-1]; - $filename = sprintf('%s%d.%s.%s.%s', $path, $args->menu_item_srl, $date, 'menu_active_btn', $ext); - - if(checkUploadedFile($args->menu_active_btn['tmp_name'])) - { - move_uploaded_file($args->menu_active_btn['tmp_name'], $filename); - $returnArray['active_btn'] = $filename; - } - + move_uploaded_file($args->menu_active_btn['tmp_name'], $filename); + $returnArray['active_btn'] = $filename; } return $returnArray; } diff --git a/modules/module/module.admin.controller.php b/modules/module/module.admin.controller.php index 25319d5b8..7eaddee2f 100644 --- a/modules/module/module.admin.controller.php +++ b/modules/module/module.admin.controller.php @@ -439,7 +439,7 @@ class moduleAdminController extends module continue; } // Ignore if the file is not successfully uploaded - if(!is_uploaded_file($image_obj['tmp_name']) || !checkUploadedFile($image_obj['tmp_name'])) + if(!is_uploaded_file($image_obj['tmp_name'])) { unset($obj->{$vars->name}); continue; diff --git a/modules/module/module.controller.php b/modules/module/module.controller.php index f70057d10..2943060c8 100644 --- a/modules/module/module.controller.php +++ b/modules/module/module.controller.php @@ -1303,9 +1303,6 @@ class moduleController extends module $save_filename = sprintf('%s%s.%s',$path, $vars->module_filebox_srl, $ext); $tmp = $vars->addfile['tmp_name']; - // Check uploaded file - if(!checkUploadedFile($tmp)) return false; - if(!@move_uploaded_file($tmp, $save_filename)) { return false; @@ -1340,9 +1337,6 @@ class moduleController extends module $save_filename = sprintf('%s%s.%s',$path, $vars->module_filebox_srl, $vars->ext); $tmp = $vars->addfile['tmp_name']; - // Check uploaded file - if(!checkUploadedFile($tmp)) return false; - // upload if(!@move_uploaded_file($tmp, $save_filename)) { diff --git a/modules/rss/rss.admin.controller.php b/modules/rss/rss.admin.controller.php index cb7e9c5bd..8368cb413 100644 --- a/modules/rss/rss.admin.controller.php +++ b/modules/rss/rss.admin.controller.php @@ -44,7 +44,7 @@ class rssAdminController extends rss $total_config->image = ''; } // Ignore if the file is not the one which has been successfully uploaded - if($image_obj['tmp_name'] && is_uploaded_file($image_obj['tmp_name']) && checkUploadedFile($image_obj['tmp_name'])) + if($image_obj['tmp_name'] && is_uploaded_file($image_obj['tmp_name'])) { // Ignore if the file is not an image (swf is accepted ~) $image_obj['name'] = Context::convertEncodingStr($image_obj['name']); From 1302d77cec0613149b9294b2abbdac4ac34ea6b5 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 12 Mar 2016 21:05:09 +0900 Subject: [PATCH 055/205] Remove duplicate code from checkCSRF() --- common/legacy.php | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/common/legacy.php b/common/legacy.php index 99a3bcd13..3c3bedf57 100644 --- a/common/legacy.php +++ b/common/legacy.php @@ -1183,42 +1183,8 @@ function requirePear() */ function checkCSRF() { - // If this is not a POST request, FAIL. - if ($_SERVER['REQUEST_METHOD'] != 'POST') - { - return false; - } - - // Get the referer. If the referer is empty, PASS. - $referer = strval($_SERVER['HTTP_REFERER']); - if ($referer === '') - { - return true; - } - if (strpos($referer, 'xn--') !== false) - { - $referer = Context::decodeIdna($referer); - } - $referer_host = parse_url($referer, PHP_URL_HOST); - - // If the referer is the same domain as the current host, PASS. - $current_host = $_SERVER['HTTP_HOST']; - if (strpos($current_host, 'xn--') !== false) - { - $current_host = Context::decodeIdna($current_host); - } - if ($referer_host === $current_host) - { - return true; - } - - // If the referer is the same domain as the default URL, PASS. - $default_url = Context::getDefaultUrl(); - if (strpos($default_url, 'xn--') !== false) - { - $default_url = Context::decodeIdna($default_url); - } - if ($referer_host === parse_url($default_url, PHP_URL_HOST)) + // Use Rhymix Security class first. + if (Rhymix\Framework\Security::checkCSRF()) { return true; } From be882d0c5cdc5fe7f93eebe3b809d29144214a1c Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 12 Mar 2016 21:11:53 +0900 Subject: [PATCH 056/205] Do not check allowscriptaccess because HTMLPurifier does it better --- classes/security/EmbedFilter.class.php | 53 -------------------------- 1 file changed, 53 deletions(-) diff --git a/classes/security/EmbedFilter.class.php b/classes/security/EmbedFilter.class.php index 2f90a753e..97b3866f1 100644 --- a/classes/security/EmbedFilter.class.php +++ b/classes/security/EmbedFilter.class.php @@ -3,18 +3,11 @@ class EmbedFilter { - /** * allow script access list * @var array */ var $allowscriptaccessList = array(); - - /** - * allow script access key - * @var int - */ - var $allowscriptaccessKey = 0; var $whiteUrlList = array(); var $whiteIframeUrlList = array(); var $mimeTypeList = array(); @@ -60,9 +53,6 @@ class EmbedFilter */ function check(&$content) { - $content = preg_replace_callback('/<(object|param|embed)[^>]*/is', array($this, '_checkAllowScriptAccess'), $content); - $content = preg_replace_callback('/]*>/is', array($this, '_addAllowScriptAccess'), $content); - $this->checkObjectTag($content); $this->checkEmbedTag($content); $this->checkParamTag($content); @@ -206,49 +196,6 @@ class EmbedFilter return FALSE; } - function _checkAllowScriptAccess($m) - { - if($m[1] == 'object') - { - $this->allowscriptaccessList[] = 1; - } - - if($m[1] == 'param') - { - if(stripos($m[0], 'allowscriptaccess')) - { - $m[0] = 'allowscriptaccessList[count($this->allowscriptaccessList) - 1]--; - } - } - else if($m[1] == 'embed') - { - if(stripos($m[0], 'allowscriptaccess')) - { - $m[0] = preg_replace('/always|samedomain/i', 'never', $m[0]); - } - else - { - $m[0] = preg_replace('/\allowscriptaccessList[$this->allowscriptaccessKey] == 1) - { - $m[0] = $m[0] . ''; - } - $this->allowscriptaccessKey++; - return $m[0]; - } - /** * Make white domain list cache file from xml config file. * @param $whitelist array From 598722b0cdd0e76e454c08d75054ae17cf5ac032 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 12 Mar 2016 21:33:42 +0900 Subject: [PATCH 057/205] Remove checkXmpTag() and blockWidgetCode() because HTMLPurifier does it better --- common/framework/security/htmlfilter.php | 2 - common/legacy.php | 53 +++++++----------------- 2 files changed, 14 insertions(+), 41 deletions(-) diff --git a/common/framework/security/htmlfilter.php b/common/framework/security/htmlfilter.php index 562dd6644..75c1b3527 100644 --- a/common/framework/security/htmlfilter.php +++ b/common/framework/security/htmlfilter.php @@ -167,11 +167,9 @@ class HTMLFilter $def->addElement('section', 'Block', 'Flow', 'Common'); $def->addElement('article', 'Block', 'Flow', 'Common'); $def->addElement('aside', 'Block', 'Flow', 'Common'); - $def->addElement('address', 'Block', 'Flow', 'Common'); // Add various inline tags. $def->addElement('s', 'Inline', 'Inline', 'Common'); - $def->addElement('var', 'Inline', 'Inline', 'Common'); $def->addElement('sub', 'Inline', 'Inline', 'Common'); $def->addElement('sup', 'Inline', 'Inline', 'Common'); $def->addElement('mark', 'Inline', 'Inline', 'Common'); diff --git a/common/legacy.php b/common/legacy.php index 3c3bedf57..869f5a263 100644 --- a/common/legacy.php +++ b/common/legacy.php @@ -840,28 +840,33 @@ function removeHackTag($content) * - Issue reported by Sangwon Kim */ $content = preg_replace_callback('@<(/?)([a-z]+[0-9]?)((?>"[^"]*"|\'[^\']*\'|[^>])*?\b(?:on[a-z]+|data|style|background|href|(?:dyn|low)?src)\s*=[\s\S]*?)(/?)($|>|<)@i', 'removeSrcHack', $content); - - $content = checkXmpTag($content); - $content = blockWidgetCode($content); - return $content; } /** - * blocking widget code + * Check xmp tag (Deprecated) + * + * @param string $content Target content + * @return string + */ +function checkXmpTag($content) +{ + return $content; +} + +/** + * Block widget code (Deprecated) * * @param string $content Taget content * @return string **/ function blockWidgetCode($content) { - $content = preg_replace('/(<(?:img|div)(?:[^>]*))(widget)(?:(=([^>]*?)>))/is', '$1blocked-widget$3', $content); - - return $content; + return preg_replace('/(<(?:img|div)(?:[^>]*))(widget)(?:(=([^>]*?)>))/is', '$1blocked-widget$3', $content); } /** - * check uploaded file which may be hacking attempts + * Check uploaded file (Deprecated) * * @param string $file Taget file path * @return bool @@ -871,31 +876,6 @@ function checkUploadedFile($file) return true; } -/** - * Check xmp tag, close it. - * - * @param string $content Target content - * @return string - */ -function checkXmpTag($content) -{ - $content = preg_replace('@<(/?)xmp.*?>@i', '<\1xmp>', $content); - - if(($start_xmp = strrpos($content, '')) !== FALSE) - { - if(($close_xmp = strrpos($content, '')) === FALSE) - { - $content .= ''; - } - else if($close_xmp < $start_xmp) - { - $content .= ''; - } - } - - return $content; -} - /** * Remove src hack(preg_replace_callback) * @@ -906,11 +886,6 @@ function removeSrcHack($match) { $tag = strtolower($match[2]); - // xmp tag ?뺣━ - if($tag == 'xmp') - { - return "<{$match[1]}xmp>"; - } if($match[1]) { return $match[0]; From f577b456ec1897d60b7a18e41d6bb6d95327ff7c Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 12 Mar 2016 22:15:55 +0900 Subject: [PATCH 058/205] Absorb removeSrcHack() into HTMLFilter postprocessing method --- common/framework/security/htmlfilter.php | 61 ++++++++- common/legacy.php | 128 ++---------------- tests/unit/framework/SecurityTest.php | 2 +- .../framework/security/HTMLFilterTest.php | 8 +- 4 files changed, 72 insertions(+), 127 deletions(-) diff --git a/common/framework/security/htmlfilter.php b/common/framework/security/htmlfilter.php index 75c1b3527..732b3c1d9 100644 --- a/common/framework/security/htmlfilter.php +++ b/common/framework/security/htmlfilter.php @@ -75,9 +75,9 @@ class HTMLFilter $input = $callback($input); } - $input = self::_encodeWidgetsAndEditorComponents($input); + $input = self::_preprocess($input); $output = self::getHTMLPurifier()->purify($input); - $output = self::_decodeWidgetsAndEditorComponents($output); + $output = self::_postprocess($output); foreach (self::$_postproc as $callback) { @@ -385,6 +385,55 @@ class HTMLFilter return '%^https?://(' . implode('|', $result) . ')%'; } + /** + * Rhymix-specific preprocessing method. + * + * @param string $content + * @return string + */ + protected static function _preprocess($content) + { + // Remove tags not supported in Rhymix. Some of these may also be removed by HTMLPurifier. + $content = preg_replace_callback('!]*>!i', function($matches) { + return htmlspecialchars($matches[0], ENT_QUOTES, 'UTF-8'); + }, $content); + + // Encode widget and editor component properties so that they are not removed by HTMLPurifier. + $content = self::_encodeWidgetsAndEditorComponents($content); + return $content; + } + + /** + * Rhymix-specific postprocessing method. + * + * @param string $content + * @return string + */ + protected static function _postprocess($content) + { + // Define acts to allow and deny. + $allow_acts = array('procFileDownload'); + $deny_acts = array('dispMemberLogout', 'dispLayoutPreview'); + + // Remove URLs that may be CSRF attempts. + $content = preg_replace_callback('!\b(src|href|data|value)="([^"]+)"!i', function($matches) use($allow_acts, $deny_acts) { + $url = preg_replace('!\s+!', '', htmlspecialchars_decode(rawurldecode($matches[2]))); + if (preg_match('!\bact=((disp|proc)[^&]+)!i', $url, $urlmatches)) + { + $act = $urlmatches[1]; + if (!in_array($act, $allow_acts) && (in_array($act, $deny_acts) || $urlmatches[2] === 'proc')) + { + return $matches[1] . '=""'; + } + } + return $matches[0]; + }, $content); + + // Restore widget and editor component properties. + $content = self::_decodeWidgetsAndEditorComponents($content); + return $content; + } + /** * Encode widgets and editor components before processing. * @@ -402,20 +451,20 @@ class HTMLFilter foreach ($found_attrs as $attr) { $attrkey = strtolower($attr[1]); - if (strtolower($match[1]) === 'img' && ($attrkey === 'width' || $attrkey === 'height' || $attrkey === 'alt')) + if (strtolower($match[1]) === 'img' && preg_match('/^(?:width|height|alt)$/', $attrkey)) { continue; } - if ($attrkey === 'src' || $attrkey === 'style' || substr($attrkey, 0, 2) === 'on') + if (preg_match('/^(?:on|data-|(?:src|style|class)$)/', $attrkey)) { continue; } $attrs[$attrkey] = htmlspecialchars_decode($attr[2]); $html = str_replace($attr[0], '', $html); } - if (strtolower($match[1]) === 'img' && !isset($attrs['src'])) + if (strtolower($match[1]) === 'img' && !preg_match('/\ssrc="/', $html)) { - //$html = substr($html, 0, 4) . ' src=""' . substr($html, 4); + $html = substr($html, 0, 4) . ' src=""' . substr($html, 4); } $encoded_properties = base64_encode(json_encode($attrs)); $html = substr($html, 0, 4) . ' rx_encoded_properties="' . $encoded_properties . '"' . substr($html, 4); diff --git a/common/legacy.php b/common/legacy.php index 869f5a263..3ff522350 100644 --- a/common/legacy.php +++ b/common/legacy.php @@ -830,17 +830,7 @@ function removeHackTag($content) $oEmbedFilter = EmbedFilter::getInstance(); $oEmbedFilter->check($content); - $content = Rhymix\Framework\Security\HTMLFilter::clean($content); - - // change the specific tags to the common texts - $content = preg_replace('@<(\/?(?:html|body|head|title|meta|base|link|script|style|applet)(/*).*?>)@i', '<$1', $content); - - /** - * Remove codes to abuse the admin session in src by tags of imaages and video postings - * - Issue reported by Sangwon Kim - */ - $content = preg_replace_callback('@<(/?)([a-z]+[0-9]?)((?>"[^"]*"|\'[^\']*\'|[^>])*?\b(?:on[a-z]+|data|style|background|href|(?:dyn|low)?src)\s*=[\s\S]*?)(/?)($|>|<)@i', 'removeSrcHack', $content); - return $content; + return Rhymix\Framework\Security\HTMLFilter::clean($content); } /** @@ -865,6 +855,17 @@ function blockWidgetCode($content) return preg_replace('/(<(?:img|div)(?:[^>]*))(widget)(?:(=([^>]*?)>))/is', '$1blocked-widget$3', $content); } +/** + * Remove src hack (Deprecated) + * + * @param array $match + * @return string + */ +function removeSrcHack($match) +{ + return $match[0]; +} + /** * Check uploaded file (Deprecated) * @@ -876,111 +877,6 @@ function checkUploadedFile($file) return true; } -/** - * Remove src hack(preg_replace_callback) - * - * @param array $match - * @return string - */ -function removeSrcHack($match) -{ - $tag = strtolower($match[2]); - - if($match[1]) - { - return $match[0]; - } - if($match[4]) - { - $match[4] = ' ' . $match[4]; - } - - $attrs = array(); - if(preg_match_all('/([\w:-]+)\s*=(?:\s*(["\']))?(?(2)(.*?)\2|([^ ]+))/s', $match[3], $m)) - { - foreach($m[1] as $idx => $name) - { - if(strlen($name) >= 2 && substr_compare($name, 'on', 0, 2) === 0) - { - continue; - } - - $val = preg_replace_callback('/&#(?:x([a-fA-F0-9]+)|0*(\d+));/', function($n) {return chr($n[1] ? ('0x00' . $n[1]) : ($n[2] + 0)); }, $m[3][$idx] . $m[4][$idx]); - $val = preg_replace('/^\s+|[\t\n\r]+/', '', $val); - - if(preg_match('/^[a-z]+script:/i', $val)) - { - continue; - } - - $attrs[$name] = $val; - } - } - - //Remove ACT URL (CSRF) - $except_act = array('procFileDownload'); - $block_act = array('dispMemberLogout', 'dispLayoutPreview'); - - $filter_arrts = array('style', 'src', 'href'); - if($tag === 'object') array_push($filter_arrts, 'data'); - if($tag === 'param') array_push($filter_arrts, 'value'); - - foreach($filter_arrts as $attr) - { - if(!isset($attrs[$attr])) continue; - - $attr_value = rawurldecode($attrs[$attr]); - $attr_value = htmlspecialchars_decode($attr_value, ENT_COMPAT); - $attr_value = preg_replace('/\s+|[\t\n\r]+/', '', $attr_value); - - preg_match('@(\?|&|;)act=(disp|proc)([^&]*)@i', $attr_value, $actmatch); - $url_action = $actmatch[2].$actmatch[3]; - - if(!empty($url_action) && !in_array($url_action, $except_act)) - { - if($actmatch[2] == 'proc' || in_array($url_action, $block_act)) - { - unset($attrs[$attr]); - } - } - } - - if(isset($attrs['style']) && preg_match('@(?:/\*|\*/|\n|:\s*expression\s*\()@i', $attrs['style'])) - { - unset($attrs['style']); - } - - $attr = array(); - foreach($attrs as $name => $val) - { - if($tag == 'object' || $tag == 'embed' || $tag == 'a') - { - $attribute = strtolower(trim($name)); - if($attribute == 'data' || $attribute == 'src' || $attribute == 'href') - { - if(stripos($val, 'data:') === 0) - { - continue; - } - } - } - - if($tag == 'img') - { - $attribute = strtolower(trim($name)); - if(stripos($val, 'data:') === 0) - { - continue; - } - } - $val = str_replace('"', '"', $val); - $attr[] = $name . "=\"{$val}\""; - } - $attr = count($attr) ? ' ' . implode(' ', $attr) : ''; - - return "<{$match[1]}{$tag}{$attr}{$match[4]}>"; -} - /** * Convert hexa value to RGB * diff --git a/tests/unit/framework/SecurityTest.php b/tests/unit/framework/SecurityTest.php index 72b2e45a6..3bf9b35c0 100644 --- a/tests/unit/framework/SecurityTest.php +++ b/tests/unit/framework/SecurityTest.php @@ -11,7 +11,7 @@ class SecurityTest extends \Codeception\TestCase\Test $this->assertEquals('foobar', Rhymix\Framework\Security::sanitize('foo

bar

', 'strip')); // HTML (more thorough tests in HTMLFilterTest) - $this->assertEquals('

safe

', Rhymix\Framework\Security::sanitize('

safe

', 'html')); + $this->assertEquals('

safe<script></script>

', Rhymix\Framework\Security::sanitize('

safe

', 'html')); // Filename (more thorough tests in FilenameFilterTest) $this->assertEquals('foo(bar).xls', Rhymix\Framework\Security::sanitize('foo.xls', 'filename')); diff --git a/tests/unit/framework/security/HTMLFilterTest.php b/tests/unit/framework/security/HTMLFilterTest.php index d8fe40f28..e29ae9438 100644 --- a/tests/unit/framework/security/HTMLFilterTest.php +++ b/tests/unit/framework/security/HTMLFilterTest.php @@ -43,19 +43,19 @@ class HTMLFilterTest extends \Codeception\TestCase\Test // issue #1813 https://github.com/xpressengine/xe-core/issues/1813 array( 'dummy', - 'dummy' + 'dummy' ), array( 'dummy', - 'dummy' + 'dummy' ), array( "\"dummy\"", - 'dummy' + 'dummy' ), array( "\"dummy\"", - 'dummy' + 'dummy' ) ); From 143b65e84023bafc0383eaf33dc28cdbbe3a6dde Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 12 Mar 2016 22:18:33 +0900 Subject: [PATCH 059/205] Perform tag filtering after HTMLPurifier is done with the content --- common/framework/security/htmlfilter.php | 12 ++++++------ tests/unit/framework/SecurityTest.php | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/common/framework/security/htmlfilter.php b/common/framework/security/htmlfilter.php index 732b3c1d9..e791595eb 100644 --- a/common/framework/security/htmlfilter.php +++ b/common/framework/security/htmlfilter.php @@ -393,11 +393,6 @@ class HTMLFilter */ protected static function _preprocess($content) { - // Remove tags not supported in Rhymix. Some of these may also be removed by HTMLPurifier. - $content = preg_replace_callback('!]*>!i', function($matches) { - return htmlspecialchars($matches[0], ENT_QUOTES, 'UTF-8'); - }, $content); - // Encode widget and editor component properties so that they are not removed by HTMLPurifier. $content = self::_encodeWidgetsAndEditorComponents($content); return $content; @@ -415,7 +410,12 @@ class HTMLFilter $allow_acts = array('procFileDownload'); $deny_acts = array('dispMemberLogout', 'dispLayoutPreview'); - // Remove URLs that may be CSRF attempts. + // Remove tags not supported in Rhymix. Some of these may also have been removed by HTMLPurifier. + $content = preg_replace_callback('!]*>!i', function($matches) { + return htmlspecialchars($matches[0], ENT_QUOTES, 'UTF-8'); + }, $content); + + // Remove link URLs that may be CSRF attempts. $content = preg_replace_callback('!\b(src|href|data|value)="([^"]+)"!i', function($matches) use($allow_acts, $deny_acts) { $url = preg_replace('!\s+!', '', htmlspecialchars_decode(rawurldecode($matches[2]))); if (preg_match('!\bact=((disp|proc)[^&]+)!i', $url, $urlmatches)) diff --git a/tests/unit/framework/SecurityTest.php b/tests/unit/framework/SecurityTest.php index 3bf9b35c0..91e1f3740 100644 --- a/tests/unit/framework/SecurityTest.php +++ b/tests/unit/framework/SecurityTest.php @@ -11,7 +11,7 @@ class SecurityTest extends \Codeception\TestCase\Test $this->assertEquals('foobar', Rhymix\Framework\Security::sanitize('foo

bar

', 'strip')); // HTML (more thorough tests in HTMLFilterTest) - $this->assertEquals('

safe<script></script>

', Rhymix\Framework\Security::sanitize('

safe

', 'html')); + $this->assertEquals('

safe

', Rhymix\Framework\Security::sanitize('

safe

', 'html')); // Filename (more thorough tests in FilenameFilterTest) $this->assertEquals('foo(bar).xls', Rhymix\Framework\Security::sanitize('foo.xls', 'filename')); From 6f53a3f0689bb4d4aba37e9927f9021048d627bc Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 12 Mar 2016 22:35:43 +0900 Subject: [PATCH 060/205] Check object whitelist in HTMLFilter class, not EmbedFilter class --- classes/security/EmbedFilter.class.php | 54 +++--------------------- common/framework/security/htmlfilter.php | 31 ++++++++++++++ common/legacy.php | 3 -- 3 files changed, 36 insertions(+), 52 deletions(-) diff --git a/classes/security/EmbedFilter.class.php b/classes/security/EmbedFilter.class.php index 97b3866f1..382bf0fe0 100644 --- a/classes/security/EmbedFilter.class.php +++ b/classes/security/EmbedFilter.class.php @@ -53,9 +53,7 @@ class EmbedFilter */ function check(&$content) { - $this->checkObjectTag($content); - $this->checkEmbedTag($content); - $this->checkParamTag($content); + // This functionality has been moved to the HTMLFilter class. } /** @@ -64,8 +62,7 @@ class EmbedFilter */ function checkIframeTag(&$content) { - // check in Purifier class - return; + // This functionality has been moved to the HTMLFilter class. } /** @@ -74,21 +71,7 @@ class EmbedFilter */ function checkObjectTag(&$content) { - $content = preg_replace_callback('/<\s*object\s*[^>]+(?:\/?>?)/is', function($m) { - $html = Sunra\PhpSimple\HtmlDomParser::str_get_html($m[0]); - foreach ($html->find('object') as $element) - { - if ($element->data && !$this->isWhiteDomain($element->data)) - { - return escape($m[0], false); - } - if ($element->type && !$this->isWhiteMimetype($element->type)) - { - return escape($m[0], false); - } - } - return $m[0]; - }, $content); + // This functionality has been moved to the HTMLFilter class. } /** @@ -97,21 +80,7 @@ class EmbedFilter */ function checkEmbedTag(&$content) { - $content = preg_replace_callback('/<\s*embed\s*[^>]+(?:\/?>?)/is', function($m) { - $html = Sunra\PhpSimple\HtmlDomParser::str_get_html($m[0]); - foreach ($html->find('embed') as $element) - { - if ($element->src && !$this->isWhiteDomain($element->src)) - { - return escape($m[0], false); - } - if ($element->type && !$this->isWhiteMimetype($element->type)) - { - return escape($m[0], false); - } - } - return $m[0]; - }, $content); + // This functionality has been moved to the HTMLFilter class. } /** @@ -120,20 +89,7 @@ class EmbedFilter */ function checkParamTag(&$content) { - $content = preg_replace_callback('/<\s*param\s*[^>]+(?:\/?>?)/is', function($m) { - $html = Sunra\PhpSimple\HtmlDomParser::str_get_html($m[0]); - foreach ($html->find('param') as $element) - { - foreach (array('movie', 'src', 'href', 'url', 'source') as $attr) - { - if ($element->$attr && !$this->isWhiteDomain($element->$attr)) - { - return escape($m[0], false); - } - } - } - return $m[0]; - }, $content); + // This functionality has been moved to the HTMLFilter class. } /** diff --git a/common/framework/security/htmlfilter.php b/common/framework/security/htmlfilter.php index e791595eb..07fa31914 100644 --- a/common/framework/security/htmlfilter.php +++ b/common/framework/security/htmlfilter.php @@ -369,6 +369,22 @@ class HTMLFilter } } + /** + * Get the object whitelist as a regular expression. + * + * @return string + */ + protected static function _getObjectWhitelist() + { + $domains = \EmbedFilter::getInstance()->getWhiteUrlList(); + $result = array(); + foreach($domains as $domain) + { + $result[] = preg_quote($domain, '%'); + } + return '%^https?://(' . implode('|', $result) . ')%'; + } + /** * Get the iframe whitelist as a regular expression. * @@ -415,6 +431,21 @@ class HTMLFilter return htmlspecialchars($matches[0], ENT_QUOTES, 'UTF-8'); }, $content); + // Remove object and embed URLs that are not allowed. + $whitelist = self::_getObjectWhitelist(); + $content = preg_replace_callback('!<(object|embed|param)([^>]+)>!i', function($matches) use($whitelist) { + return preg_replace_callback('!([a-zA-Z0-9_-]+)="([^"]+)"!', function($attr) use($whitelist) { + if (in_array($attr[1], array('data', 'src', 'href', 'url', 'movie', 'source'))) + { + if (!preg_match($whitelist, htmlspecialchars_decode($attr[2]))) + { + return $attr[1] . '=""'; + } + } + return $attr[0]; + }, $matches[0]); + }, $content); + // Remove link URLs that may be CSRF attempts. $content = preg_replace_callback('!\b(src|href|data|value)="([^"]+)"!i', function($matches) use($allow_acts, $deny_acts) { $url = preg_replace('!\s+!', '', htmlspecialchars_decode(rawurldecode($matches[2]))); diff --git a/common/legacy.php b/common/legacy.php index 3ff522350..1830d3a90 100644 --- a/common/legacy.php +++ b/common/legacy.php @@ -827,9 +827,6 @@ function purifierHtml(&$content) */ function removeHackTag($content) { - $oEmbedFilter = EmbedFilter::getInstance(); - $oEmbedFilter->check($content); - return Rhymix\Framework\Security\HTMLFilter::clean($content); } From 93629d1311d40fd1b58fd37c1ef7ae68b5b331e8 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 12 Mar 2016 22:44:09 +0900 Subject: [PATCH 061/205] Refactor widget and editor component encoding with preg_replace_callback() --- common/framework/security/htmlfilter.php | 34 +++++++++--------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/common/framework/security/htmlfilter.php b/common/framework/security/htmlfilter.php index 07fa31914..cad870bc5 100644 --- a/common/framework/security/htmlfilter.php +++ b/common/framework/security/htmlfilter.php @@ -473,35 +473,28 @@ class HTMLFilter */ protected static function _encodeWidgetsAndEditorComponents($content) { - preg_match_all('!<(div|img)([^>]*)(editor_component="[^"]+"|class="zbxe_widget_output")([^>]*)>!i', $content, $matches, \PREG_SET_ORDER); - foreach ($matches as $match) - { + return preg_replace_callback('!<(div|img)([^>]*)(editor_component="[^"]+"|class="zbxe_widget_output")([^>]*)>!i', function($match) { $attrs = array(); - $html = $match[0]; - preg_match_all('/([a-zA-Z0-9_-]+)="([^"]+)"/', $match[2] . ' ' . $match[4], $found_attrs, \PREG_SET_ORDER); - foreach ($found_attrs as $attr) - { + $html = preg_replace_callback('!([a-zA-Z0-9_-]+)="([^"]+)"!', function($attr) use(&$attrs) { $attrkey = strtolower($attr[1]); if (strtolower($match[1]) === 'img' && preg_match('/^(?:width|height|alt)$/', $attrkey)) { - continue; + return $attr[0]; } - if (preg_match('/^(?:on|data-|(?:src|style|class)$)/', $attrkey)) + if (preg_match('/^(?:on|data-|(?:src|style|class|editor_component)$)/', $attrkey)) { - continue; + return $attr[0]; } $attrs[$attrkey] = htmlspecialchars_decode($attr[2]); - $html = str_replace($attr[0], '', $html); - } + return ''; + }, $match[0]); if (strtolower($match[1]) === 'img' && !preg_match('/\ssrc="/', $html)) { $html = substr($html, 0, 4) . ' src=""' . substr($html, 4); } $encoded_properties = base64_encode(json_encode($attrs)); - $html = substr($html, 0, 4) . ' rx_encoded_properties="' . $encoded_properties . '"' . substr($html, 4); - $content = str_replace($match[0], $html, $content); - } - return $content; + return substr($html, 0, 4) . ' rx_encoded_properties="' . $encoded_properties . '"' . substr($html, 4); + }, $content); } /** @@ -512,17 +505,14 @@ class HTMLFilter */ protected static function _decodeWidgetsAndEditorComponents($content) { - preg_match_all('!<(div|img)([^>]*)(rx_encoded_properties="([^"]+)")!i', $content, $matches, \PREG_SET_ORDER); - foreach ($matches as $match) - { + return preg_replace_callback('!<(div|img)([^>]*)(rx_encoded_properties="([^"]+)")!i', function($match) { $attrs = array(); $decoded_properties = @json_decode(base64_decode($match[4])) ?: array(); foreach ($decoded_properties as $key => $val) { $attrs[] = $key . '="' . htmlspecialchars($val) . '"'; } - $content = str_replace($match[3], implode(' ', $attrs), $content); - } - return $content; + return str_replace($match[3], implode(' ', $attrs), $match[0]); + }, $content); } } From 616d8940214a97872a55ba2e451f5a1f2b9ff9d1 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 12 Mar 2016 22:48:05 +0900 Subject: [PATCH 062/205] Clean up deprecated functions in common/legacy.php --- common/legacy.php | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/common/legacy.php b/common/legacy.php index 1830d3a90..29cbd58e9 100644 --- a/common/legacy.php +++ b/common/legacy.php @@ -814,15 +814,10 @@ function url_decode($str) return htmlspecialchars(utf8RawUrlDecode($str), null, 'UTF-8'); } -function purifierHtml(&$content) -{ - $content = Rhymix\Framework\Security\HTMLFilter::clean($content); -} - /** - * Pre-block the codes which may be hacking attempts + * Sanitize HTML content. * - * @param string $content Taget content + * @param string $content Target content * @return string */ function removeHackTag($content) @@ -830,6 +825,17 @@ function removeHackTag($content) return Rhymix\Framework\Security\HTMLFilter::clean($content); } +/** + * HTMLPurifier wrapper (Deprecated) + * + * @param string &$content Target content + * @return string + */ +function purifierHtml(&$content) +{ + $content = Rhymix\Framework\Security\HTMLFilter::clean($content); +} + /** * Check xmp tag (Deprecated) * @@ -918,7 +924,7 @@ function getScriptPath() */ function getRequestUriByServerEnviroment() { - return str_replace('<', '<', $_SERVER['REQUEST_URI']); + return escape($_SERVER['REQUEST_URI']); } /** From ef6f0c839f0b3352943577c8c59b257256cf7d2c Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 12 Mar 2016 23:17:37 +0900 Subject: [PATCH 063/205] Absorb core functionality of stripEmbedTagForAdmin() into HTMLFilter class --- common/framework/security/htmlfilter.php | 15 +++++++++++++ common/legacy.php | 28 +++++++++++++----------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/common/framework/security/htmlfilter.php b/common/framework/security/htmlfilter.php index cad870bc5..4cee601b0 100644 --- a/common/framework/security/htmlfilter.php +++ b/common/framework/security/htmlfilter.php @@ -87,6 +87,21 @@ class HTMLFilter return $output; } + /** + * Remove embedded media from HTML content. + * + * @param string $input + * @param string $replacement + * @return string + */ + public static function removeEmbeddedMedia($input, $replacement = '') + { + $input = preg_replace('!]*>(.*?)?!is', $replacement, $input); + $input = preg_replace('!]*>(.*?)?!is', $replacement, $input); + $input = preg_replace('!]*editor_component="multimedia_link"[^>]*>(.*?)?!is', $replacement, $input); + return $input; + } + /** * Get an instance of HTMLPurifier. * diff --git a/common/legacy.php b/common/legacy.php index 29cbd58e9..729166e14 100644 --- a/common/legacy.php +++ b/common/legacy.php @@ -1012,29 +1012,31 @@ function isCrawler($agent = NULL) */ function stripEmbedTagForAdmin(&$content, $writer_member_srl) { - if(!Context::get('is_logged')) + if (!Context::get('is_logged')) { return; } - - $oModuleModel = getModel('module'); + $logged_info = Context::get('logged_info'); - - if($writer_member_srl != $logged_info->member_srl && ($logged_info->is_admin == "Y" || $oModuleModel->isSiteAdmin($logged_info))) + if ($logged_info->member_srl == $writer_member_srl) { - if($writer_member_srl) + return; + } + + if ($logged_info->is_admin === 'Y' || getModel('module')->isSiteAdmin($logged_info)) + { + if ($writer_member_srl) { - $oMemberModel = getModel('member'); - $member_info = $oMemberModel->getMemberInfoByMemberSrl($writer_member_srl); - if($member_info->is_admin == "Y") + $member_info = getModel('member')->getMemberInfoByMemberSrl($writer_member_srl); + if ($member_info && $member_info->is_admin === 'Y') { return; } } - $security_msg = "

" . lang('security_warning_embed') . "

"; - $content = preg_replace('/]+>(.*?<\/object>)?/is', $security_msg, $content); - $content = preg_replace('/]+>(\s*<\/embed>)?/is', $security_msg, $content); - $content = preg_replace('/]+editor_component="multimedia_link"[^>]*>(\s*<\/img>)?/is', $security_msg, $content); + + $security_msg = '
' . + '

' . lang('security_warning_embed') . '

'; + $content = Rhymix\Framework\Security\HTMLFilter::removeEmbeddedMedia($content, $security_msg); } return; From 31080ddc046a33fdd79ecdc4365a5b08a3b35537 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 12 Mar 2016 23:24:00 +0900 Subject: [PATCH 064/205] Add unit tests for removeEmbeddedMedia() --- .../framework/security/HTMLFilterTest.php | 141 ++++++++++-------- 1 file changed, 78 insertions(+), 63 deletions(-) diff --git a/tests/unit/framework/security/HTMLFilterTest.php b/tests/unit/framework/security/HTMLFilterTest.php index e29ae9438..f3ab22629 100644 --- a/tests/unit/framework/security/HTMLFilterTest.php +++ b/tests/unit/framework/security/HTMLFilterTest.php @@ -2,67 +2,82 @@ class HTMLFilterTest extends \Codeception\TestCase\Test { - public function testRemoveHackTag() - { - $tests = array( - // remove iframe - array( - '', - '' - ), - // expression - array( - '
', - '
' - ), - // no quotes and no semicolon - http://ha.ckers.org/xss.html - array( - '', - '' - ), - // embedded encoded tab to break up XSS - http://ha.ckers.org/xss.html - array( - '', - '' - ), - // issue 178 - array( - '', - '' - ), - // issue 534 - array( - '*/" onerror="console.log(\'Yet another XSS\')">', - '*/" onerror="console.log(\'Yet another XSS\')">' - ), - // issue 602 - array( - 'test', - 'test' - ), - // issue #1813 https://github.com/xpressengine/xe-core/issues/1813 - array( - 'dummy', - 'dummy' - ), - array( - 'dummy', - 'dummy' - ), - array( - "\"dummy\"", - 'dummy' - ), - array( - "\"dummy\"", - 'dummy' - ) - ); - - foreach ($tests as $test) - { - $result = removeHackTag($test[0]); - $this->assertEquals($test[1], $result); - } - } + public function testRemoveHackTag() + { + $tests = array( + // remove iframe + array( + '', + '' + ), + // expression + array( + '
', + '
' + ), + // no quotes and no semicolon - http://ha.ckers.org/xss.html + array( + '', + '' + ), + // embedded encoded tab to break up XSS - http://ha.ckers.org/xss.html + array( + '', + '' + ), + // issue 178 + array( + '', + '' + ), + // issue 534 + array( + '*/" onerror="console.log(\'Yet another XSS\')">', + '*/" onerror="console.log(\'Yet another XSS\')">' + ), + // issue 602 + array( + 'test', + 'test' + ), + // issue #1813 https://github.com/xpressengine/xe-core/issues/1813 + array( + 'dummy', + 'dummy' + ), + array( + 'dummy', + 'dummy' + ), + array( + "\"dummy\"", + 'dummy' + ), + array( + "\"dummy\"", + 'dummy' + ) + ); + + foreach ($tests as $test) + { + $this->assertEquals($test[1], removeHackTag($test[0])); + } + } + + public function testRemoveEmbeddedMedia() + { + $tests = array( + '
' => '
', + '
' => '
', + '
' => '
', + '
' => '
', + '
' => '
', + ); + + foreach ($tests as $from => $to) + { + $this->assertEquals($to, Rhymix\Framework\Security\HTMLFilter::removeEmbeddedMedia($from)); + } + } } From 3d3fa6d7d3259db59225349ad6be04fd5b845e28 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 13 Mar 2016 00:11:47 +0900 Subject: [PATCH 065/205] Use HTMLFilter::clean() directly in unit tests --- tests/unit/framework/security/HTMLFilterTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/framework/security/HTMLFilterTest.php b/tests/unit/framework/security/HTMLFilterTest.php index f3ab22629..01162dc9c 100644 --- a/tests/unit/framework/security/HTMLFilterTest.php +++ b/tests/unit/framework/security/HTMLFilterTest.php @@ -2,7 +2,7 @@ class HTMLFilterTest extends \Codeception\TestCase\Test { - public function testRemoveHackTag() + public function testHTMLFilterClean() { $tests = array( // remove iframe @@ -61,7 +61,7 @@ class HTMLFilterTest extends \Codeception\TestCase\Test foreach ($tests as $test) { - $this->assertEquals($test[1], removeHackTag($test[0])); + $this->assertEquals($test[1], Rhymix\Framework\Security\HTMLFilter::clean($test[0])); } } From f41e3f590795058e62b8bdfba3aecfd5fc4ee472 Mon Sep 17 00:00:00 2001 From: MinSoo Kim Date: Sun, 13 Mar 2016 00:37:04 +0900 Subject: [PATCH 066/205] input type=color can not be empty https://www.w3.org/TR/2012/WD-html5-20120329/states-of-the-type-attribute.html#color-state-type-color --- common/js/plugins/spectrum/rx_spectrum.js | 1 + modules/document/tpl/category_list.html | 2 +- .../editor/components/image_gallery/tpl/popup.html | 4 ++-- modules/layout/tpl/faceoff_layout_menu.html | 14 +++++++------- modules/layout/tpl/layout_info_view.html | 2 +- modules/module/tpl/skin_config.html | 2 +- .../widget/tpl/widget_generate_code.include.html | 2 +- modules/widget/tpl/widget_layer.html | 10 +++++----- .../tpl/widget_style_generate_code_in_page.html | 2 +- 9 files changed, 20 insertions(+), 19 deletions(-) diff --git a/common/js/plugins/spectrum/rx_spectrum.js b/common/js/plugins/spectrum/rx_spectrum.js index 39e788732..8aed57387 100644 --- a/common/js/plugins/spectrum/rx_spectrum.js +++ b/common/js/plugins/spectrum/rx_spectrum.js @@ -12,6 +12,7 @@ jQuery(function($){ $.getScript(request_uri + "./common/js/plugins/spectrum/i18n/jquery.spectrum-"+ xe.current_lang.replace("jp", "ja").toLowerCase() +".js", function() { var settings = { showInput: true, + allowEmpty:true, showInitial: true, showPalette: true, showSelectionPalette: true, diff --git a/modules/document/tpl/category_list.html b/modules/document/tpl/category_list.html index e9f03e0ad..f1f998537 100644 --- a/modules/document/tpl/category_list.html +++ b/modules/document/tpl/category_list.html @@ -61,7 +61,7 @@
- + {$lang->help}
diff --git a/modules/editor/components/image_gallery/tpl/popup.html b/modules/editor/components/image_gallery/tpl/popup.html index d8cd62292..3e0b93d5a 100644 --- a/modules/editor/components/image_gallery/tpl/popup.html +++ b/modules/editor/components/image_gallery/tpl/popup.html @@ -40,13 +40,13 @@
- +
- +
diff --git a/modules/layout/tpl/faceoff_layout_menu.html b/modules/layout/tpl/faceoff_layout_menu.html index 13bc6d802..a2dc156d9 100644 --- a/modules/layout/tpl/faceoff_layout_menu.html +++ b/modules/layout/tpl/faceoff_layout_menu.html @@ -218,7 +218,7 @@ {$lang->layout_manager[38]} - + - + - + - + - + +
@@ -335,7 +335,7 @@
- +
diff --git a/modules/layout/tpl/layout_info_view.html b/modules/layout/tpl/layout_info_view.html index 780111419..98b48ef9b 100644 --- a/modules/layout/tpl/layout_info_view.html +++ b/modules/layout/tpl/layout_info_view.html @@ -129,7 +129,7 @@ {@ $use_colorpicker = true; } - +

{$var->description}

diff --git a/modules/module/tpl/skin_config.html b/modules/module/tpl/skin_config.html index a2de640b0..d92bb669d 100644 --- a/modules/module/tpl/skin_config.html +++ b/modules/module/tpl/skin_config.html @@ -117,7 +117,7 @@
{@ $use_colorpicker = true; } - +
{$lang->help} diff --git a/modules/widget/tpl/widget_generate_code.include.html b/modules/widget/tpl/widget_generate_code.include.html index 8d931af62..cf0d13589 100644 --- a/modules/widget/tpl/widget_generate_code.include.html +++ b/modules/widget/tpl/widget_generate_code.include.html @@ -49,7 +49,7 @@ - + diff --git a/modules/widget/tpl/widget_layer.html b/modules/widget/tpl/widget_layer.html index 6f1a2c911..584a0ebf3 100644 --- a/modules/widget/tpl/widget_layer.html +++ b/modules/widget/tpl/widget_layer.html @@ -62,7 +62,7 @@ - + @@ -72,7 +72,7 @@ - + px @@ -80,7 +80,7 @@ - + @@ -90,12 +90,12 @@ - + {$lang->cmd_widget_background_color} - + {$lang->cmd_widget_background_image_url} diff --git a/modules/widget/tpl/widget_style_generate_code_in_page.html b/modules/widget/tpl/widget_style_generate_code_in_page.html index 25c6b0f01..b7127a8e0 100644 --- a/modules/widget/tpl/widget_style_generate_code_in_page.html +++ b/modules/widget/tpl/widget_style_generate_code_in_page.html @@ -70,7 +70,7 @@
- +
From 7ecd9230c297877e1fcb28c2f8f6014312856c08 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 13 Mar 2016 09:51:15 +0900 Subject: [PATCH 067/205] Add MediaFilter class to absorb most EmbedFilter functionality --- classes/security/EmbedFilter.class.php | 210 +-- common/defaults/whitelist.php | 1648 ----------------- common/framework/security/htmlfilter.php | 51 +- common/framework/security/mediafilter.php | 180 ++ common/legacy.php | 2 +- .../framework/security/HTMLFilterTest.php | 16 - .../framework/security/MediaFilterTest.php | 47 + 7 files changed, 266 insertions(+), 1888 deletions(-) create mode 100644 common/framework/security/mediafilter.php create mode 100644 tests/unit/framework/security/MediaFilterTest.php diff --git a/classes/security/EmbedFilter.class.php b/classes/security/EmbedFilter.class.php index 382bf0fe0..d6e9e5f01 100644 --- a/classes/security/EmbedFilter.class.php +++ b/classes/security/EmbedFilter.class.php @@ -4,216 +4,78 @@ class EmbedFilter { /** - * allow script access list + * Deprecated properties * @var array */ - var $allowscriptaccessList = array(); - var $whiteUrlList = array(); - var $whiteIframeUrlList = array(); - var $mimeTypeList = array(); - var $extList = array(); - var $parser = NULL; - - /** - * @constructor - * @return void - */ - function __construct() - { - $this->_makeWhiteDomainList(); - } + public $whiteUrlList = array(); + public $whiteIframeUrlList = array(); + public $mimeTypeList = array(); + public $extList = array(); /** * Return EmbedFilter object - * This method for singleton + * * @return EmbedFilter */ function getInstance() { - if(!isset($GLOBALS['__EMBEDFILTER_INSTANCE__'])) - { - $GLOBALS['__EMBEDFILTER_INSTANCE__'] = new EmbedFilter(); - } - return $GLOBALS['__EMBEDFILTER_INSTANCE__']; + return new self(); } - + public function getWhiteUrlList() { - return $this->whiteUrlList; + return Rhymix\Framework\Security\MediaFilter::getObjectWhitelist(); } - + public function getWhiteIframeUrlList() { - return $this->whiteIframeUrlList; + return Rhymix\Framework\Security\MediaFilter::getIframeWhitelist(); } - - /** - * Check the content. - * @return void - */ + + function isWhiteDomain($urlAttribute) + { + return Rhymix\Framework\Security\MediaFilter::matchObjectWhitelist($urlAttribute); + } + + function isWhiteIframeDomain($urlAttribute) + { + return Rhymix\Framework\Security\MediaFilter::matchIframeWhitelist($urlAttribute); + } + + function isWhiteMimetype($mimeType) + { + return true; + } + + function isWhiteExt($ext) + { + return true; + } + function check(&$content) { // This functionality has been moved to the HTMLFilter class. } - - /** - * Check iframe tag in the content. - * @return void - */ + function checkIframeTag(&$content) { // This functionality has been moved to the HTMLFilter class. } - - /** - * Check object tag in the content. - * @return void - */ + function checkObjectTag(&$content) { // This functionality has been moved to the HTMLFilter class. } - - /** - * Check embed tag in the content. - * @return void - */ + function checkEmbedTag(&$content) { // This functionality has been moved to the HTMLFilter class. } - - /** - * Check param tag in the content. - * @return void - */ + function checkParamTag(&$content) { // This functionality has been moved to the HTMLFilter class. } - - /** - * Check white domain in object data attribute or embed src attribute. - * @return string - */ - function isWhiteDomain($urlAttribute) - { - if(is_array($this->whiteUrlList)) - { - foreach($this->whiteUrlList AS $key => $value) - { - if(preg_match('@^https?://' . preg_quote($value, '@') . '@i', $urlAttribute)) - { - return TRUE; - } - } - } - return FALSE; - } - - /** - * Check white domain in iframe src attribute. - * @return string - */ - function isWhiteIframeDomain($urlAttribute) - { - if(is_array($this->whiteIframeUrlList)) - { - foreach($this->whiteIframeUrlList AS $key => $value) - { - if(preg_match('@^https?://' . preg_quote($value, '@') . '@i', $urlAttribute)) - { - return TRUE; - } - } - } - return FALSE; - } - - /** - * Check white mime type in object type attribute or embed type attribute. - * @return string - */ - function isWhiteMimetype($mimeType) - { - if(isset($this->mimeTypeList[$mimeType])) - { - return TRUE; - } - return FALSE; - } - - function isWhiteExt($ext) - { - if(isset($this->extList[$ext])) - { - return TRUE; - } - return FALSE; - } - - /** - * Make white domain list cache file from xml config file. - * @param $whitelist array - * @return void - */ - function _makeWhiteDomainList($whitelist = NULL) - { - $whiteUrlDefaultList = (include RX_BASEDIR . 'common/defaults/whitelist.php'); - $this->extList = $whiteUrlDefaultList['extensions']; - $this->mimeTypeList = $whiteUrlDefaultList['mime']; - $this->whiteUrlList = array(); - $this->whiteIframeUrlList = array(); - - if($whitelist !== NULL) - { - if(!is_array($whitelist) || !isset($whitelist['object']) || !isset($whitelist['iframe'])) - { - $whitelist = array( - 'object' => isset($whitelist->object) ? $whitelist->object : array(), - 'iframe' => isset($whitelist->iframe) ? $whitelist->iframe : array(), - ); - } - foreach ($whitelist['object'] as $prefix) - { - $this->whiteUrlList[] = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix; - } - foreach ($whitelist['iframe'] as $prefix) - { - $this->whiteIframeUrlList[] = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix; - } - } - else - { - foreach ($whiteUrlDefaultList['object'] as $prefix) - { - $this->whiteUrlList[] = $prefix; - } - foreach ($whiteUrlDefaultList['iframe'] as $prefix) - { - $this->whiteIframeUrlList[] = $prefix; - } - if ($embedfilter_object = config('embedfilter.object')) - { - foreach ($embedfilter_object as $prefix) - { - $this->whiteUrlList[] = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix; - } - } - if ($embedfilter_iframe = config('embedfilter.iframe')) - { - foreach ($embedfilter_iframe as $prefix) - { - $this->whiteIframeUrlList[] = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix; - } - } - } - - $this->whiteUrlList = array_unique($this->whiteUrlList); - $this->whiteIframeUrlList = array_unique($this->whiteIframeUrlList); - natcasesort($this->whiteUrlList); - natcasesort($this->whiteIframeUrlList); - } } /* End of file : EmbedFilter.class.php */ /* Location: ./classes/security/EmbedFilter.class.php */ diff --git a/common/defaults/whitelist.php b/common/defaults/whitelist.php index 272da733b..7fb02f008 100644 --- a/common/defaults/whitelist.php +++ b/common/defaults/whitelist.php @@ -77,1652 +77,4 @@ return array( // Afreeca 'afree.ca/', ), - - /** - * Allowed extensions in or tag - */ - 'extensions' => array( - '123' => 1, - '3ds' => 1, - '3g2' => 1, - '3gp' => 1, - '7z' => 1, - 'aab' => 1, - 'aac' => 1, - 'aam' => 1, - 'aas' => 1, - 'abw' => 1, - 'ac' => 1, - 'acc' => 1, - 'ace' => 1, - 'acu' => 1, - 'acutc' => 1, - 'adp' => 1, - 'aep' => 1, - 'afm' => 1, - 'afp' => 1, - 'ahead' => 1, - 'ai' => 1, - 'aif' => 1, - 'aifc' => 1, - 'aiff' => 1, - 'air' => 1, - 'ait' => 1, - 'ami' => 1, - 'apk' => 1, - 'application' => 1, - 'apr' => 1, - 'arc' => 1, - 'asc' => 1, - 'asf' => 1, - 'aso' => 1, - 'asx' => 1, - 'atc' => 1, - 'atom' => 1, - 'atomcat' => 1, - 'atomsvc' => 1, - 'atx' => 1, - 'au' => 1, - 'avi' => 1, - 'aw' => 1, - 'azf' => 1, - 'azs' => 1, - 'azw' => 1, - 'bat' => 1, - 'bcpio' => 1, - 'bdf' => 1, - 'bdm' => 1, - 'bed' => 1, - 'bh2' => 1, - 'bin' => 1, - 'blb' => 1, - 'blorb' => 1, - 'bmi' => 1, - 'bmp' => 1, - 'book' => 1, - 'box' => 1, - 'boz' => 1, - 'bpk' => 1, - 'btif' => 1, - 'bz' => 1, - 'bz2' => 1, - 'c11amc' => 1, - 'c11amz' => 1, - 'c4d' => 1, - 'c4f' => 1, - 'c4g' => 1, - 'c4p' => 1, - 'c4u' => 1, - 'cab' => 1, - 'caf' => 1, - 'cap' => 1, - 'car' => 1, - 'cat' => 1, - 'cb7' => 1, - 'cba' => 1, - 'cbr' => 1, - 'cbt' => 1, - 'cbz' => 1, - 'cct' => 1, - 'ccxml' => 1, - 'cdbcmsg' => 1, - 'cdf' => 1, - 'cdkey' => 1, - 'cdmia' => 1, - 'cdmic' => 1, - 'cdmid' => 1, - 'cdmio' => 1, - 'cdmiq' => 1, - 'cdx' => 1, - 'cdxml' => 1, - 'cdy' => 1, - 'cer' => 1, - 'cfs' => 1, - 'cgm' => 1, - 'chat' => 1, - 'chm' => 1, - 'chrt' => 1, - 'cif' => 1, - 'cii' => 1, - 'cil' => 1, - 'cla' => 1, - 'class' => 1, - 'clkk' => 1, - 'clkp' => 1, - 'clkt' => 1, - 'clkw' => 1, - 'clkx' => 1, - 'clp' => 1, - 'cmc' => 1, - 'cmdf' => 1, - 'cml' => 1, - 'cmp' => 1, - 'cmx' => 1, - 'cod' => 1, - 'com' => 1, - 'cpio' => 1, - 'cpt' => 1, - 'crd' => 1, - 'crl' => 1, - 'crt' => 1, - 'cryptonote' => 1, - 'csh' => 1, - 'csml' => 1, - 'csp' => 1, - 'cst' => 1, - 'cu' => 1, - 'cww' => 1, - 'cxt' => 1, - 'dae' => 1, - 'daf' => 1, - 'dart' => 1, - 'dataless' => 1, - 'davmount' => 1, - 'dbk' => 1, - 'dcr' => 1, - 'dd2' => 1, - 'ddd' => 1, - 'deb' => 1, - 'deploy' => 1, - 'der' => 1, - 'dfac' => 1, - 'dgc' => 1, - 'dir' => 1, - 'dis' => 1, - 'dist' => 1, - 'distz' => 1, - 'djv' => 1, - 'djvu' => 1, - 'dll' => 1, - 'dmg' => 1, - 'dmp' => 1, - 'dms' => 1, - 'dna' => 1, - 'doc dot' => 1, - 'docm' => 1, - 'docx' => 1, - 'dotm' => 1, - 'dotx' => 1, - 'dp' => 1, - 'dpg' => 1, - 'dra' => 1, - 'dssc' => 1, - 'dtb' => 1, - 'dtd' => 1, - 'dts' => 1, - 'dtshd' => 1, - 'dump' => 1, - 'dvb' => 1, - 'dvi' => 1, - 'dwf' => 1, - 'dwg' => 1, - 'dxf' => 1, - 'dxp' => 1, - 'dxr' => 1, - 'ecelp4800' => 1, - 'ecelp7470' => 1, - 'ecelp9600' => 1, - 'ecma' => 1, - 'edm' => 1, - 'edx' => 1, - 'efif' => 1, - 'ei6' => 1, - 'elc' => 1, - 'emf' => 1, - 'eml' => 1, - 'emma' => 1, - 'emz' => 1, - 'eol' => 1, - 'eot' => 1, - 'eps' => 1, - 'epub' => 1, - 'es3' => 1, - 'esa' => 1, - 'esf' => 1, - 'et3' => 1, - 'eva' => 1, - 'evy' => 1, - 'exe' => 1, - 'exi' => 1, - 'ext' => 1, - 'ez' => 1, - 'ez2' => 1, - 'ez3' => 1, - 'f4v' => 1, - 'fbs' => 1, - 'fcdt' => 1, - 'fcs' => 1, - 'fdf' => 1, - 'fe_launch' => 1, - 'fg5' => 1, - 'fgd' => 1, - 'fh' => 1, - 'fh4' => 1, - 'fh5' => 1, - 'fh7' => 1, - 'fhc' => 1, - 'fig' => 1, - 'flac' => 1, - 'fli' => 1, - 'flo' => 1, - 'flv' => 1, - 'flw' => 1, - 'fm' => 1, - 'fnc' => 1, - 'fpx' => 1, - 'frame' => 1, - 'fsc' => 1, - 'fst' => 1, - 'ftc' => 1, - 'fti' => 1, - 'fvt' => 1, - 'fxp' => 1, - 'fxpl' => 1, - 'fzs' => 1, - 'g2w' => 1, - 'g3' => 1, - 'g3w' => 1, - 'gac' => 1, - 'gam' => 1, - 'gbr' => 1, - 'gca' => 1, - 'gdl' => 1, - 'geo' => 1, - 'gex' => 1, - 'ggb' => 1, - 'ggt' => 1, - 'ghf' => 1, - 'gif' => 1, - 'gim' => 1, - 'gml' => 1, - 'gmx' => 1, - 'gnumeric' => 1, - 'gph' => 1, - 'gpx' => 1, - 'gqf' => 1, - 'gqs' => 1, - 'gram' => 1, - 'gramps' => 1, - 'gre' => 1, - 'grv' => 1, - 'grxml' => 1, - 'gsf' => 1, - 'gtar' => 1, - 'gtm' => 1, - 'gtw' => 1, - 'gxf' => 1, - 'gxt' => 1, - 'h261' => 1, - 'h263' => 1, - 'h264' => 1, - 'hal' => 1, - 'hbci' => 1, - 'hdf' => 1, - 'hlp' => 1, - 'hpgl' => 1, - 'hpid' => 1, - 'hps' => 1, - 'hqx' => 1, - 'htke' => 1, - 'hvd' => 1, - 'hvp' => 1, - 'hvs' => 1, - 'i2g' => 1, - 'icc' => 1, - 'ice' => 1, - 'icm' => 1, - 'ico' => 1, - 'ief' => 1, - 'ifm' => 1, - 'iges' => 1, - 'igl' => 1, - 'igm' => 1, - 'igs' => 1, - 'igx' => 1, - 'iif' => 1, - 'imp' => 1, - 'ims' => 1, - 'ink' => 1, - 'inkml' => 1, - 'install' => 1, - 'iota' => 1, - 'ipfix' => 1, - 'ipk' => 1, - 'irm' => 1, - 'irp' => 1, - 'iso' => 1, - 'itp' => 1, - 'ivp' => 1, - 'ivu' => 1, - 'jam' => 1, - 'jar' => 1, - 'jisp' => 1, - 'jlt' => 1, - 'jnlp' => 1, - 'joda' => 1, - 'jpe' => 1, - 'jpeg' => 1, - 'jpg' => 1, - 'jpgm' => 1, - 'jpgv' => 1, - 'jpm' => 1, - 'js' => 1, - 'json' => 1, - 'jsonml' => 1, - 'kar' => 1, - 'karbon' => 1, - 'kfo' => 1, - 'kia' => 1, - 'kml' => 1, - 'kmz' => 1, - 'kne' => 1, - 'knp' => 1, - 'kon' => 1, - 'kpr' => 1, - 'kpt' => 1, - 'kpxx' => 1, - 'ksp' => 1, - 'ktr' => 1, - 'ktx' => 1, - 'ktz' => 1, - 'kwd' => 1, - 'kwt' => 1, - 'lasxml' => 1, - 'latex' => 1, - 'lbd' => 1, - 'lbe' => 1, - 'les' => 1, - 'lha' => 1, - 'link66' => 1, - 'list3820' => 1, - 'listafp' => 1, - 'lnk' => 1, - 'lostxml' => 1, - 'lrf' => 1, - 'lrm' => 1, - 'ltf' => 1, - 'lvp' => 1, - 'lwp' => 1, - 'lzh' => 1, - 'm13' => 1, - 'm14' => 1, - 'm1v' => 1, - 'm21 mp21' => 1, - 'm2a' => 1, - 'm2v' => 1, - 'm3a' => 1, - 'm3u' => 1, - 'm3u8' => 1, - 'm4u' => 1, - 'm4v' => 1, - 'ma' => 1, - 'mads' => 1, - 'mag' => 1, - 'maker' => 1, - 'mar' => 1, - 'mathml' => 1, - 'mb' => 1, - 'mbk' => 1, - 'mbox' => 1, - 'mc1' => 1, - 'mcd' => 1, - 'mdb' => 1, - 'mdi' => 1, - 'mesh' => 1, - 'meta4' => 1, - 'metalink' => 1, - 'mets' => 1, - 'mfm' => 1, - 'mft' => 1, - 'mgp' => 1, - 'mgz' => 1, - 'mid' => 1, - 'midi' => 1, - 'mie' => 1, - 'mif' => 1, - 'mime' => 1, - 'mj2' => 1, - 'mjp2' => 1, - 'mk3d' => 1, - 'mka' => 1, - 'mks' => 1, - 'mkv' => 1, - 'mlp' => 1, - 'mmd' => 1, - 'mmf' => 1, - 'mmr' => 1, - 'mng' => 1, - 'mny' => 1, - 'mobi' => 1, - 'mods' => 1, - 'mov' => 1, - 'movie' => 1, - 'mp2' => 1, - 'mp2a' => 1, - 'mp3' => 1, - 'mp4' => 1, - 'mp4a' => 1, - 'mp4s' => 1, - 'mp4v' => 1, - 'mpc' => 1, - 'mpe' => 1, - 'mpeg' => 1, - 'mpg' => 1, - 'mpg4' => 1, - 'mpga' => 1, - 'mpkg' => 1, - 'mpm' => 1, - 'mpn' => 1, - 'mpp' => 1, - 'mpt' => 1, - 'mpy' => 1, - 'mqy' => 1, - 'mrc' => 1, - 'mrcx' => 1, - 'mscml' => 1, - 'mseed' => 1, - 'mseq' => 1, - 'msf' => 1, - 'msh' => 1, - 'msi' => 1, - 'msl' => 1, - 'msty' => 1, - 'mts' => 1, - 'mus' => 1, - 'musicxml' => 1, - 'mvb' => 1, - 'mwf' => 1, - 'mxf' => 1, - 'mxl' => 1, - 'mxml' => 1, - 'mxs' => 1, - 'mxu' => 1, - 'n-gage' => 1, - 'nb' => 1, - 'nbp' => 1, - 'nc' => 1, - 'ncx' => 1, - 'ngdat' => 1, - 'nitf' => 1, - 'nlu' => 1, - 'nml' => 1, - 'nnd' => 1, - 'nns' => 1, - 'nnw' => 1, - 'npx' => 1, - 'nsc' => 1, - 'nsf' => 1, - 'nzb' => 1, - 'oa2' => 1, - 'oa3' => 1, - 'oas' => 1, - 'obd' => 1, - 'obj' => 1, - 'oda' => 1, - 'odb' => 1, - 'odc' => 1, - 'odf' => 1, - 'odft' => 1, - 'odg' => 1, - 'odi' => 1, - 'odm' => 1, - 'odp' => 1, - 'ods' => 1, - 'odt' => 1, - 'oga' => 1, - 'ogg' => 1, - 'ogv' => 1, - 'ogx' => 1, - 'omdoc' => 1, - 'onepkg' => 1, - 'onetmp' => 1, - 'onetoc' => 1, - 'onetoc2' => 1, - 'opf' => 1, - 'oprc' => 1, - 'org' => 1, - 'osf' => 1, - 'osfpvg' => 1, - 'otc' => 1, - 'otf' => 1, - 'otg' => 1, - 'oth' => 1, - 'oti' => 1, - 'otp' => 1, - 'ots' => 1, - 'ott' => 1, - 'oxps' => 1, - 'oxt' => 1, - 'p10' => 1, - 'p12' => 1, - 'p7b' => 1, - 'p7c' => 1, - 'p7m' => 1, - 'p7r' => 1, - 'p7s' => 1, - 'p8' => 1, - 'paw' => 1, - 'pbd' => 1, - 'pbm' => 1, - 'pcap' => 1, - 'pcf' => 1, - 'pcl' => 1, - 'pclxl' => 1, - 'pct' => 1, - 'pcurl' => 1, - 'pcx' => 1, - 'pdb' => 1, - 'pdf' => 1, - 'pfa' => 1, - 'pfb' => 1, - 'pfm' => 1, - 'pfr' => 1, - 'pfx' => 1, - 'pgm' => 1, - 'pgn' => 1, - 'pgp' => 1, - 'pic' => 1, - 'pkg' => 1, - 'pki' => 1, - 'pkipath' => 1, - 'plb' => 1, - 'plc' => 1, - 'plf' => 1, - 'pls' => 1, - 'pml' => 1, - 'png' => 1, - 'pnm' => 1, - 'portpkg' => 1, - 'pot' => 1, - 'potm' => 1, - 'potx' => 1, - 'ppam' => 1, - 'ppd' => 1, - 'ppm' => 1, - 'pps' => 1, - 'ppsm' => 1, - 'ppsx' => 1, - 'ppt' => 1, - 'pptm' => 1, - 'pptx' => 1, - 'pqa' => 1, - 'prc' => 1, - 'pre' => 1, - 'prf' => 1, - 'ps' => 1, - 'psb' => 1, - 'psd' => 1, - 'psf' => 1, - 'pskcxml' => 1, - 'ptid' => 1, - 'pub' => 1, - 'pvb' => 1, - 'pwn' => 1, - 'pya' => 1, - 'pyv' => 1, - 'qam' => 1, - 'qbo' => 1, - 'qfx' => 1, - 'qps' => 1, - 'qt' => 1, - 'qwd' => 1, - 'qwt' => 1, - 'qxb' => 1, - 'qxd' => 1, - 'qxl' => 1, - 'qxt' => 1, - 'ra' => 1, - 'ram' => 1, - 'rar' => 1, - 'ras' => 1, - 'rcprofile' => 1, - 'rdf' => 1, - 'rdz' => 1, - 'rep' => 1, - 'res' => 1, - 'rgb' => 1, - 'rif' => 1, - 'rip' => 1, - 'ris' => 1, - 'rl' => 1, - 'rlc' => 1, - 'rld' => 1, - 'rm' => 1, - 'rmi' => 1, - 'rmp' => 1, - 'rms' => 1, - 'rmvb' => 1, - 'rnc' => 1, - 'roa' => 1, - 'rp9' => 1, - 'rpss' => 1, - 'rpst' => 1, - 'rq' => 1, - 'rs' => 1, - 'rsd' => 1, - 'rss' => 1, - 'rtf' => 1, - 's3m' => 1, - 'saf' => 1, - 'sbml' => 1, - 'sc' => 1, - 'scd' => 1, - 'scm' => 1, - 'scq' => 1, - 'scs' => 1, - 'sda' => 1, - 'sdc' => 1, - 'sdd' => 1, - 'sdkd' => 1, - 'sdkm' => 1, - 'sdp' => 1, - 'sdw' => 1, - 'see' => 1, - 'seed' => 1, - 'sema' => 1, - 'semd' => 1, - 'semf' => 1, - 'ser' => 1, - 'setpay' => 1, - 'setreg' => 1, - 'sfd-hdstx' => 1, - 'sfs' => 1, - 'sgi' => 1, - 'sgl' => 1, - 'sh' => 1, - 'shar' => 1, - 'shf' => 1, - 'sid' => 1, - 'sig' => 1, - 'sil' => 1, - 'silo' => 1, - 'sis' => 1, - 'sisx' => 1, - 'sit' => 1, - 'sitx' => 1, - 'skd' => 1, - 'skm' => 1, - 'skp' => 1, - 'skt' => 1, - 'sldm' => 1, - 'sldx' => 1, - 'slt' => 1, - 'sm' => 1, - 'smf' => 1, - 'smi' => 1, - 'smil' => 1, - 'smv' => 1, - 'smzip' => 1, - 'snd' => 1, - 'snf' => 1, - 'so' => 1, - 'spc' => 1, - 'spf' => 1, - 'spl' => 1, - 'spp' => 1, - 'spq' => 1, - 'spx' => 1, - 'sql' => 1, - 'src' => 1, - 'srt' => 1, - 'sru' => 1, - 'srx' => 1, - 'ssdl' => 1, - 'sse' => 1, - 'ssf' => 1, - 'ssml' => 1, - 'st' => 1, - 'stc' => 1, - 'std' => 1, - 'stf' => 1, - 'sti' => 1, - 'stk' => 1, - 'stl' => 1, - 'str' => 1, - 'stw' => 1, - 'sub' => 1, - 'sus' => 1, - 'susp' => 1, - 'sv4cpio' => 1, - 'sv4crc' => 1, - 'svc' => 1, - 'svd' => 1, - 'svg' => 1, - 'svgz' => 1, - 'swa' => 1, - 'swf' => 1, - 'swi' => 1, - 'sxc' => 1, - 'sxd' => 1, - 'sxg' => 1, - 'sxi' => 1, - 'sxm' => 1, - 'sxw' => 1, - 't3' => 1, - 'taglet' => 1, - 'tao' => 1, - 'tar' => 1, - 'tcap' => 1, - 'tcl' => 1, - 'teacher' => 1, - 'tei' => 1, - 'teicorpus' => 1, - 'tex' => 1, - 'texi' => 1, - 'texinfo' => 1, - 'tfi' => 1, - 'tfm' => 1, - 'tga' => 1, - 'thmx' => 1, - 'tif' => 1, - 'tiff' => 1, - 'tmo' => 1, - 'torrent' => 1, - 'tpl' => 1, - 'tpt' => 1, - 'tra' => 1, - 'trm' => 1, - 'tsd' => 1, - 'ttc' => 1, - 'ttf' => 1, - 'twd' => 1, - 'twds' => 1, - 'txd' => 1, - 'txf' => 1, - 'u32' => 1, - 'udeb' => 1, - 'ufd' => 1, - 'ufdl' => 1, - 'ulx' => 1, - 'umj' => 1, - 'unityweb' => 1, - 'uoml' => 1, - 'ustar' => 1, - 'utz' => 1, - 'uva' => 1, - 'uvd' => 1, - 'uvf' => 1, - 'uvg' => 1, - 'uvh' => 1, - 'uvi' => 1, - 'uvm' => 1, - 'uvp' => 1, - 'uvs' => 1, - 'uvt' => 1, - 'uvu' => 1, - 'uvv' => 1, - 'uvva' => 1, - 'uvvd' => 1, - 'uvvf' => 1, - 'uvvg' => 1, - 'uvvh' => 1, - 'uvvi' => 1, - 'uvvm' => 1, - 'uvvp' => 1, - 'uvvs' => 1, - 'uvvt' => 1, - 'uvvu' => 1, - 'uvvv' => 1, - 'uvvx' => 1, - 'uvvz' => 1, - 'uvx' => 1, - 'uvz' => 1, - 'vcd' => 1, - 'vcg' => 1, - 'vcx' => 1, - 'vis' => 1, - 'viv' => 1, - 'vob' => 1, - 'vor' => 1, - 'vox' => 1, - 'vrml' => 1, - 'vsd' => 1, - 'vsf' => 1, - 'vss' => 1, - 'vst' => 1, - 'vsw' => 1, - 'vtu' => 1, - 'vxml' => 1, - 'w3d' => 1, - 'wad' => 1, - 'wav' => 1, - 'wax' => 1, - 'wbmp' => 1, - 'wbs' => 1, - 'wbxml' => 1, - 'wcm' => 1, - 'wdb' => 1, - 'wdp' => 1, - 'weba' => 1, - 'webm' => 1, - 'webp' => 1, - 'wg' => 1, - 'wgt' => 1, - 'wks' => 1, - 'wm' => 1, - 'wma' => 1, - 'wmd' => 1, - 'wmf' => 1, - 'wmlc' => 1, - 'wmlsc' => 1, - 'wmv' => 1, - 'wmx' => 1, - 'wmz' => 1, - 'woff' => 1, - 'wpd' => 1, - 'wpl' => 1, - 'wps' => 1, - 'wqd' => 1, - 'wri' => 1, - 'wrl' => 1, - 'wsdl' => 1, - 'wspolicy' => 1, - 'wtb' => 1, - 'wvx' => 1, - 'x32' => 1, - 'x3d' => 1, - 'x3db' => 1, - 'x3dbz' => 1, - 'x3dv' => 1, - 'x3dvz' => 1, - 'x3dz' => 1, - 'xaml' => 1, - 'xap' => 1, - 'xar' => 1, - 'xbap' => 1, - 'xbd' => 1, - 'xbm' => 1, - 'xdf' => 1, - 'xdm' => 1, - 'xdp' => 1, - 'xdssc' => 1, - 'xdw' => 1, - 'xenc' => 1, - 'xer' => 1, - 'xfdf' => 1, - 'xfdl' => 1, - 'xht' => 1, - 'xhtml' => 1, - 'xhvml' => 1, - 'xif' => 1, - 'xla' => 1, - 'xlam' => 1, - 'xlc' => 1, - 'xlf' => 1, - 'xlm' => 1, - 'xls' => 1, - 'xlsb' => 1, - 'xlsm' => 1, - 'xlsx' => 1, - 'xlt' => 1, - 'xltm' => 1, - 'xltx' => 1, - 'xlw' => 1, - 'xm' => 1, - 'xml' => 1, - 'xo' => 1, - 'xop' => 1, - 'xpi' => 1, - 'xpl' => 1, - 'xpm' => 1, - 'xpr' => 1, - 'xps' => 1, - 'xpw' => 1, - 'xpx' => 1, - 'xsl' => 1, - 'xslt' => 1, - 'xsm' => 1, - 'xspf' => 1, - 'xul' => 1, - 'xvm' => 1, - 'xvml' => 1, - 'xwd' => 1, - 'xyz' => 1, - 'xz' => 1, - 'yang' => 1, - 'yin' => 1, - 'z1' => 1, - 'z2' => 1, - 'z3' => 1, - 'z4' => 1, - 'z5' => 1, - 'z6' => 1, - 'z7' => 1, - 'z8' => 1, - 'zaz' => 1, - 'zip' => 1, - 'zir' => 1, - 'zirz' => 1, - 'zmm' => 1, - ), - - // Allowed MIME types - 'mime' => array( - 'application/andrew-inset' => 1, - 'application/applixware' => 1, - 'application/atom+xml' => 1, - 'application/atomcat+xml' => 1, - 'application/atomsvc+xml' => 1, - 'application/ccxml+xml' => 1, - 'application/cdmi-capability' => 1, - 'application/cdmi-container' => 1, - 'application/cdmi-domain' => 1, - 'application/cdmi-object' => 1, - 'application/cdmi-queue' => 1, - 'application/cu-seeme' => 1, - 'application/davmount+xml' => 1, - 'application/docbook+xml' => 1, - 'application/dssc+der' => 1, - 'application/dssc+xml' => 1, - 'application/ecmascript' => 1, - 'application/emma+xml' => 1, - 'application/epub+zip' => 1, - 'application/exi' => 1, - 'application/font-tdpfr' => 1, - 'application/gml+xml' => 1, - 'application/gpx+xml' => 1, - 'application/gxf' => 1, - 'application/hyperstudio' => 1, - 'application/inkml+xml' => 1, - 'application/ipfix' => 1, - 'application/java-archive' => 1, - 'application/java-serialized-object' => 1, - 'application/java-vm' => 1, - 'application/javascript' => 1, - 'application/json' => 1, - 'application/jsonml+json' => 1, - 'application/lost+xml' => 1, - 'application/mac-binhex40' => 1, - 'application/mac-compactpro' => 1, - 'application/mads+xml' => 1, - 'application/marc' => 1, - 'application/marcxml+xml' => 1, - 'application/mathematica' => 1, - 'application/mathml+xml' => 1, - 'application/mbox' => 1, - 'application/mediaservercontrol+xml' => 1, - 'application/metalink+xml' => 1, - 'application/metalink4+xml' => 1, - 'application/mets+xml' => 1, - 'application/mods+xml' => 1, - 'application/mp21' => 1, - 'application/mp4' => 1, - 'application/msword' => 1, - 'application/mxf' => 1, - 'application/octet-stream' => 1, - 'application/oda' => 1, - 'application/oebps-package+xml' => 1, - 'application/ogg' => 1, - 'application/omdoc+xml' => 1, - 'application/onenote' => 1, - 'application/oxps' => 1, - 'application/patch-ops-error+xml' => 1, - 'application/pdf' => 1, - 'application/pgp-encrypted' => 1, - 'application/pgp-signature' => 1, - 'application/pics-rules' => 1, - 'application/pkcs10' => 1, - 'application/pkcs7-mime' => 1, - 'application/pkcs7-signature' => 1, - 'application/pkcs8' => 1, - 'application/pkix-attr-cert' => 1, - 'application/pkix-cert' => 1, - 'application/pkix-crl' => 1, - 'application/pkix-pkipath' => 1, - 'application/pkixcmp' => 1, - 'application/pls+xml' => 1, - 'application/postscript' => 1, - 'application/prs.cww' => 1, - 'application/pskc+xml' => 1, - 'application/rdf+xml' => 1, - 'application/reginfo+xml' => 1, - 'application/relax-ng-compact-syntax' => 1, - 'application/resource-lists+xml' => 1, - 'application/resource-lists-diff+xml' => 1, - 'application/rls-services+xml' => 1, - 'application/rpki-ghostbusters' => 1, - 'application/rpki-manifest' => 1, - 'application/rpki-roa' => 1, - 'application/rsd+xml' => 1, - 'application/rss+xml' => 1, - 'application/rtf' => 1, - 'application/sbml+xml' => 1, - 'application/scvp-cv-request' => 1, - 'application/scvp-cv-response' => 1, - 'application/scvp-vp-request' => 1, - 'application/scvp-vp-response' => 1, - 'application/sdp' => 1, - 'application/set-payment-initiation' => 1, - 'application/set-registration-initiation' => 1, - 'application/shf+xml' => 1, - 'application/smil+xml' => 1, - 'application/sparql-query' => 1, - 'application/sparql-results+xml' => 1, - 'application/srgs' => 1, - 'application/srgs+xml' => 1, - 'application/sru+xml' => 1, - 'application/ssdl+xml' => 1, - 'application/ssml+xml' => 1, - 'application/tei+xml' => 1, - 'application/thraud+xml' => 1, - 'application/timestamped-data' => 1, - 'application/vnd.3gpp.pic-bw-large' => 1, - 'application/vnd.3gpp.pic-bw-small' => 1, - 'application/vnd.3gpp.pic-bw-var' => 1, - 'application/vnd.3gpp2.tcap' => 1, - 'application/vnd.3m.post-it-notes' => 1, - 'application/vnd.accpac.simply.aso' => 1, - 'application/vnd.accpac.simply.imp' => 1, - 'application/vnd.acucobol' => 1, - 'application/vnd.acucorp' => 1, - 'application/vnd.adobe.air-application-installer-package+zip' => 1, - 'application/vnd.adobe.formscentral.fcdt' => 1, - 'application/vnd.adobe.fxp' => 1, - 'application/vnd.adobe.xdp+xml' => 1, - 'application/vnd.adobe.xfdf' => 1, - 'application/vnd.ahead.space' => 1, - 'application/vnd.airzip.filesecure.azf' => 1, - 'application/vnd.airzip.filesecure.azs' => 1, - 'application/vnd.amazon.ebook' => 1, - 'application/vnd.americandynamics.acc' => 1, - 'application/vnd.amiga.ami' => 1, - 'application/vnd.android.package-archive' => 1, - 'application/vnd.anser-web-certificate-issue-initiation' => 1, - 'application/vnd.anser-web-funds-transfer-initiation' => 1, - 'application/vnd.antix.game-component' => 1, - 'application/vnd.apple.installer+xml' => 1, - 'application/vnd.apple.mpegurl' => 1, - 'application/vnd.aristanetworks.swi' => 1, - 'application/vnd.astraea-software.iota' => 1, - 'application/vnd.audiograph' => 1, - 'application/vnd.blueice.multipass' => 1, - 'application/vnd.bmi' => 1, - 'application/vnd.businessobjects' => 1, - 'application/vnd.chemdraw+xml' => 1, - 'application/vnd.chipnuts.karaoke-mmd' => 1, - 'application/vnd.cinderella' => 1, - 'application/vnd.claymore' => 1, - 'application/vnd.cloanto.rp9' => 1, - 'application/vnd.clonk.c4group' => 1, - 'application/vnd.cluetrust.cartomobile-config' => 1, - 'application/vnd.cluetrust.cartomobile-config-pkg' => 1, - 'application/vnd.commonspace' => 1, - 'application/vnd.contact.cmsg' => 1, - 'application/vnd.cosmocaller' => 1, - 'application/vnd.crick.clicker' => 1, - 'application/vnd.crick.clicker.keyboard' => 1, - 'application/vnd.crick.clicker.palette' => 1, - 'application/vnd.crick.clicker.template' => 1, - 'application/vnd.crick.clicker.wordbank' => 1, - 'application/vnd.criticaltools.wbs+xml' => 1, - 'application/vnd.ctc-posml' => 1, - 'application/vnd.cups-ppd' => 1, - 'application/vnd.curl.car' => 1, - 'application/vnd.curl.pcurl' => 1, - 'application/vnd.dart' => 1, - 'application/vnd.data-vision.rdz' => 1, - 'application/vnd.dece.data' => 1, - 'application/vnd.dece.ttml+xml' => 1, - 'application/vnd.dece.unspecified' => 1, - 'application/vnd.dece.zip' => 1, - 'application/vnd.denovo.fcselayout-link' => 1, - 'application/vnd.dna' => 1, - 'application/vnd.dolby.mlp' => 1, - 'application/vnd.dpgraph' => 1, - 'application/vnd.dreamfactory' => 1, - 'application/vnd.ds-keypoint' => 1, - 'application/vnd.dvb.ait' => 1, - 'application/vnd.dvb.service' => 1, - 'application/vnd.dynageo' => 1, - 'application/vnd.ecowin.chart' => 1, - 'application/vnd.enliven' => 1, - 'application/vnd.epson.esf' => 1, - 'application/vnd.epson.msf' => 1, - 'application/vnd.epson.quickanime' => 1, - 'application/vnd.epson.salt' => 1, - 'application/vnd.epson.ssf' => 1, - 'application/vnd.eszigno3+xml' => 1, - 'application/vnd.ezpix-album' => 1, - 'application/vnd.ezpix-package' => 1, - 'application/vnd.fdf' => 1, - 'application/vnd.fdsn.mseed' => 1, - 'application/vnd.fdsn.seed' => 1, - 'application/vnd.flographit' => 1, - 'application/vnd.fluxtime.clip' => 1, - 'application/vnd.framemaker' => 1, - 'application/vnd.frogans.fnc' => 1, - 'application/vnd.frogans.ltf' => 1, - 'application/vnd.fsc.weblaunch' => 1, - 'application/vnd.fujitsu.oasys' => 1, - 'application/vnd.fujitsu.oasys2' => 1, - 'application/vnd.fujitsu.oasys3' => 1, - 'application/vnd.fujitsu.oasysgp' => 1, - 'application/vnd.fujitsu.oasysprs' => 1, - 'application/vnd.fujixerox.ddd' => 1, - 'application/vnd.fujixerox.docuworks' => 1, - 'application/vnd.fujixerox.docuworks.binder' => 1, - 'application/vnd.fuzzysheet' => 1, - 'application/vnd.genomatix.tuxedo' => 1, - 'application/vnd.geogebra.file' => 1, - 'application/vnd.geogebra.tool' => 1, - 'application/vnd.geometry-explorer' => 1, - 'application/vnd.geonext' => 1, - 'application/vnd.geoplan' => 1, - 'application/vnd.geospace' => 1, - 'application/vnd.gmx' => 1, - 'application/vnd.google-earth.kml+xml' => 1, - 'application/vnd.google-earth.kmz' => 1, - 'application/vnd.grafeq' => 1, - 'application/vnd.groove-account' => 1, - 'application/vnd.groove-help' => 1, - 'application/vnd.groove-identity-message' => 1, - 'application/vnd.groove-injector' => 1, - 'application/vnd.groove-tool-message' => 1, - 'application/vnd.groove-tool-template' => 1, - 'application/vnd.groove-vcard' => 1, - 'application/vnd.hal+xml' => 1, - 'application/vnd.handheld-entertainment+xml' => 1, - 'application/vnd.hbci' => 1, - 'application/vnd.hhe.lesson-player' => 1, - 'application/vnd.hp-hpgl' => 1, - 'application/vnd.hp-hpid' => 1, - 'application/vnd.hp-hps' => 1, - 'application/vnd.hp-jlyt' => 1, - 'application/vnd.hp-pcl' => 1, - 'application/vnd.hp-pclxl' => 1, - 'application/vnd.hydrostatix.sof-data' => 1, - 'application/vnd.ibm.minipay' => 1, - 'application/vnd.ibm.modcap' => 1, - 'application/vnd.ibm.rights-management' => 1, - 'application/vnd.ibm.secure-container' => 1, - 'application/vnd.iccprofile' => 1, - 'application/vnd.igloader' => 1, - 'application/vnd.immervision-ivp' => 1, - 'application/vnd.immervision-ivu' => 1, - 'application/vnd.insors.igm' => 1, - 'application/vnd.intercon.formnet' => 1, - 'application/vnd.intergeo' => 1, - 'application/vnd.intu.qbo' => 1, - 'application/vnd.intu.qfx' => 1, - 'application/vnd.ipunplugged.rcprofile' => 1, - 'application/vnd.irepository.package+xml' => 1, - 'application/vnd.is-xpr' => 1, - 'application/vnd.isac.fcs' => 1, - 'application/vnd.jam' => 1, - 'application/vnd.jcp.javame.midlet-rms' => 1, - 'application/vnd.jisp' => 1, - 'application/vnd.joost.joda-archive' => 1, - 'application/vnd.kahootz' => 1, - 'application/vnd.kde.karbon' => 1, - 'application/vnd.kde.kchart' => 1, - 'application/vnd.kde.kformula' => 1, - 'application/vnd.kde.kivio' => 1, - 'application/vnd.kde.kontour' => 1, - 'application/vnd.kde.kpresenter' => 1, - 'application/vnd.kde.kspread' => 1, - 'application/vnd.kde.kword' => 1, - 'application/vnd.kenameaapp' => 1, - 'application/vnd.kidspiration' => 1, - 'application/vnd.kinar' => 1, - 'application/vnd.koan' => 1, - 'application/vnd.kodak-descriptor' => 1, - 'application/vnd.las.las+xml' => 1, - 'application/vnd.llamagraphics.life-balance.desktop' => 1, - 'application/vnd.llamagraphics.life-balance.exchange+xml' => 1, - 'application/vnd.lotus-1-2-3' => 1, - 'application/vnd.lotus-approach' => 1, - 'application/vnd.lotus-freelance' => 1, - 'application/vnd.lotus-notes' => 1, - 'application/vnd.lotus-organizer' => 1, - 'application/vnd.lotus-screencam' => 1, - 'application/vnd.lotus-wordpro' => 1, - 'application/vnd.macports.portpkg' => 1, - 'application/vnd.mcd' => 1, - 'application/vnd.medcalcdata' => 1, - 'application/vnd.mediastation.cdkey' => 1, - 'application/vnd.mfer' => 1, - 'application/vnd.mfmp' => 1, - 'application/vnd.micrografx.flo' => 1, - 'application/vnd.micrografx.igx' => 1, - 'application/vnd.mif' => 1, - 'application/vnd.mobius.daf' => 1, - 'application/vnd.mobius.dis' => 1, - 'application/vnd.mobius.mbk' => 1, - 'application/vnd.mobius.mqy' => 1, - 'application/vnd.mobius.msl' => 1, - 'application/vnd.mobius.plc' => 1, - 'application/vnd.mobius.txf' => 1, - 'application/vnd.mophun.application' => 1, - 'application/vnd.mophun.certificate' => 1, - 'application/vnd.mozilla.xul+xml' => 1, - 'application/vnd.ms-artgalry' => 1, - 'application/vnd.ms-cab-compressed' => 1, - 'application/vnd.ms-excel' => 1, - 'application/vnd.ms-excel.addin.macroenabled.12' => 1, - 'application/vnd.ms-excel.sheet.binary.macroenabled.12' => 1, - 'application/vnd.ms-excel.sheet.macroenabled.12' => 1, - 'application/vnd.ms-excel.template.macroenabled.12' => 1, - 'application/vnd.ms-fontobject' => 1, - 'application/vnd.ms-htmlhelp' => 1, - 'application/vnd.ms-ims' => 1, - 'application/vnd.ms-lrm' => 1, - 'application/vnd.ms-officetheme' => 1, - 'application/vnd.ms-pki.seccat' => 1, - 'application/vnd.ms-pki.stl' => 1, - 'application/vnd.ms-powerpoint' => 1, - 'application/vnd.ms-powerpoint.addin.macroenabled.12' => 1, - 'application/vnd.ms-powerpoint.presentation.macroenabled.12' => 1, - 'application/vnd.ms-powerpoint.slide.macroenabled.12' => 1, - 'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => 1, - 'application/vnd.ms-powerpoint.template.macroenabled.12' => 1, - 'application/vnd.ms-project' => 1, - 'application/vnd.ms-word.document.macroenabled.12' => 1, - 'application/vnd.ms-word.template.macroenabled.12' => 1, - 'application/vnd.ms-works' => 1, - 'application/vnd.ms-wpl' => 1, - 'application/vnd.ms-xpsdocument' => 1, - 'application/vnd.mseq' => 1, - 'application/vnd.musician' => 1, - 'application/vnd.muvee.style' => 1, - 'application/vnd.mynfc' => 1, - 'application/vnd.neurolanguage.nlu' => 1, - 'application/vnd.nitf' => 1, - 'application/vnd.noblenet-directory' => 1, - 'application/vnd.noblenet-sealer' => 1, - 'application/vnd.noblenet-web' => 1, - 'application/vnd.nokia.n-gage.data' => 1, - 'application/vnd.nokia.n-gage.symbian.install' => 1, - 'application/vnd.nokia.radio-preset' => 1, - 'application/vnd.nokia.radio-presets' => 1, - 'application/vnd.novadigm.edm' => 1, - 'application/vnd.novadigm.edx' => 1, - 'application/vnd.novadigm.ext' => 1, - 'application/vnd.oasis.opendocument.chart' => 1, - 'application/vnd.oasis.opendocument.chart-template' => 1, - 'application/vnd.oasis.opendocument.database' => 1, - 'application/vnd.oasis.opendocument.formula' => 1, - 'application/vnd.oasis.opendocument.formula-template' => 1, - 'application/vnd.oasis.opendocument.graphics' => 1, - 'application/vnd.oasis.opendocument.graphics-template' => 1, - 'application/vnd.oasis.opendocument.image' => 1, - 'application/vnd.oasis.opendocument.image-template' => 1, - 'application/vnd.oasis.opendocument.presentation' => 1, - 'application/vnd.oasis.opendocument.presentation-template' => 1, - 'application/vnd.oasis.opendocument.spreadsheet' => 1, - 'application/vnd.oasis.opendocument.spreadsheet-template' => 1, - 'application/vnd.oasis.opendocument.text' => 1, - 'application/vnd.oasis.opendocument.text-master' => 1, - 'application/vnd.oasis.opendocument.text-template' => 1, - 'application/vnd.oasis.opendocument.text-web' => 1, - 'application/vnd.olpc-sugar' => 1, - 'application/vnd.oma.dd2+xml' => 1, - 'application/vnd.openofficeorg.extension' => 1, - 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 1, - 'application/vnd.openxmlformats-officedocument.presentationml.slide' => 1, - 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => 1, - 'application/vnd.openxmlformats-officedocument.presentationml.template' => 1, - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 1, - 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => 1, - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 1, - 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => 1, - 'application/vnd.osgeo.mapguide.package' => 1, - 'application/vnd.osgi.dp' => 1, - 'application/vnd.osgi.subsystem' => 1, - 'application/vnd.palm' => 1, - 'application/vnd.pawaafile' => 1, - 'application/vnd.pg.format' => 1, - 'application/vnd.pg.osasli' => 1, - 'application/vnd.picsel' => 1, - 'application/vnd.pmi.widget' => 1, - 'application/vnd.pocketlearn' => 1, - 'application/vnd.powerbuilder6' => 1, - 'application/vnd.previewsystems.box' => 1, - 'application/vnd.proteus.magazine' => 1, - 'application/vnd.publishare-delta-tree' => 1, - 'application/vnd.pvi.ptid1' => 1, - 'application/vnd.quark.quarkxpress' => 1, - 'application/vnd.realvnc.bed' => 1, - 'application/vnd.recordare.musicxml' => 1, - 'application/vnd.recordare.musicxml+xml' => 1, - 'application/vnd.rig.cryptonote' => 1, - 'application/vnd.rim.cod' => 1, - 'application/vnd.rn-realmedia' => 1, - 'application/vnd.rn-realmedia-vbr' => 1, - 'application/vnd.route66.link66+xml' => 1, - 'application/vnd.sailingtracker.track' => 1, - 'application/vnd.seemail' => 1, - 'application/vnd.sema' => 1, - 'application/vnd.semd' => 1, - 'application/vnd.semf' => 1, - 'application/vnd.shana.informed.formdata' => 1, - 'application/vnd.shana.informed.formtemplate' => 1, - 'application/vnd.shana.informed.interchange' => 1, - 'application/vnd.shana.informed.package' => 1, - 'application/vnd.simtech-mindmapper' => 1, - 'application/vnd.smaf' => 1, - 'application/vnd.smart.teacher' => 1, - 'application/vnd.solent.sdkm+xml' => 1, - 'application/vnd.spotfire.dxp' => 1, - 'application/vnd.spotfire.sfs' => 1, - 'application/vnd.stardivision.calc' => 1, - 'application/vnd.stardivision.draw' => 1, - 'application/vnd.stardivision.impress' => 1, - 'application/vnd.stardivision.math' => 1, - 'application/vnd.stardivision.writer' => 1, - 'application/vnd.stardivision.writer-global' => 1, - 'application/vnd.stepmania.package' => 1, - 'application/vnd.stepmania.stepchart' => 1, - 'application/vnd.sun.xml.calc' => 1, - 'application/vnd.sun.xml.calc.template' => 1, - 'application/vnd.sun.xml.draw' => 1, - 'application/vnd.sun.xml.draw.template' => 1, - 'application/vnd.sun.xml.impress' => 1, - 'application/vnd.sun.xml.impress.template' => 1, - 'application/vnd.sun.xml.math' => 1, - 'application/vnd.sun.xml.writer' => 1, - 'application/vnd.sun.xml.writer.global' => 1, - 'application/vnd.sun.xml.writer.template' => 1, - 'application/vnd.sus-calendar' => 1, - 'application/vnd.svd' => 1, - 'application/vnd.symbian.install' => 1, - 'application/vnd.syncml+xml' => 1, - 'application/vnd.syncml.dm+wbxml' => 1, - 'application/vnd.syncml.dm+xml' => 1, - 'application/vnd.tao.intent-module-archive' => 1, - 'application/vnd.tcpdump.pcap' => 1, - 'application/vnd.tmobile-livetv' => 1, - 'application/vnd.trid.tpt' => 1, - 'application/vnd.triscape.mxs' => 1, - 'application/vnd.trueapp' => 1, - 'application/vnd.ufdl' => 1, - 'application/vnd.uiq.theme' => 1, - 'application/vnd.umajin' => 1, - 'application/vnd.unity' => 1, - 'application/vnd.uoml+xml' => 1, - 'application/vnd.vcx' => 1, - 'application/vnd.visio' => 1, - 'application/vnd.visionary' => 1, - 'application/vnd.vsf' => 1, - 'application/vnd.wap.wbxml' => 1, - 'application/vnd.wap.wmlc' => 1, - 'application/vnd.wap.wmlscriptc' => 1, - 'application/vnd.webturbo' => 1, - 'application/vnd.wolfram.player' => 1, - 'application/vnd.wordperfect' => 1, - 'application/vnd.wqd' => 1, - 'application/vnd.wt.stf' => 1, - 'application/vnd.xara' => 1, - 'application/vnd.xfdl' => 1, - 'application/vnd.yamaha.hv-dic' => 1, - 'application/vnd.yamaha.hv-script' => 1, - 'application/vnd.yamaha.hv-voice' => 1, - 'application/vnd.yamaha.openscoreformat' => 1, - 'application/vnd.yamaha.openscoreformat.osfpvg+xml' => 1, - 'application/vnd.yamaha.smaf-audio' => 1, - 'application/vnd.yamaha.smaf-phrase' => 1, - 'application/vnd.yellowriver-custom-menu' => 1, - 'application/vnd.zul' => 1, - 'application/vnd.zzazz.deck+xml' => 1, - 'application/voicexml+xml' => 1, - 'application/widget' => 1, - 'application/winhlp' => 1, - 'application/wsdl+xml' => 1, - 'application/wspolicy+xml' => 1, - 'application/x-7z-compressed' => 1, - 'application/x-abiword' => 1, - 'application/x-ace-compressed' => 1, - 'application/x-apple-diskimage' => 1, - 'application/x-authorware-bin' => 1, - 'application/x-authorware-map' => 1, - 'application/x-authorware-seg' => 1, - 'application/x-bcpio' => 1, - 'application/x-bittorrent' => 1, - 'application/x-blorb' => 1, - 'application/x-bzip' => 1, - 'application/x-bzip2' => 1, - 'application/x-cbr' => 1, - 'application/x-cdlink' => 1, - 'application/x-cfs-compressed' => 1, - 'application/x-chat' => 1, - 'application/x-chess-pgn' => 1, - 'application/x-conference' => 1, - 'application/x-cpio' => 1, - 'application/x-csh' => 1, - 'application/x-debian-package' => 1, - 'application/x-dgc-compressed' => 1, - 'application/x-director' => 1, - 'application/x-doom' => 1, - 'application/x-dtbncx+xml' => 1, - 'application/x-dtbook+xml' => 1, - 'application/x-dtbresource+xml' => 1, - 'application/x-dvi' => 1, - 'application/x-envoy' => 1, - 'application/x-eva' => 1, - 'application/x-font-bdf' => 1, - 'application/x-font-ghostscript' => 1, - 'application/x-font-linux-psf' => 1, - 'application/x-font-otf' => 1, - 'application/x-font-pcf' => 1, - 'application/x-font-snf' => 1, - 'application/x-font-ttf' => 1, - 'application/x-font-type1' => 1, - 'application/x-font-woff' => 1, - 'application/x-freearc' => 1, - 'application/x-futuresplash' => 1, - 'application/x-gca-compressed' => 1, - 'application/x-glulx' => 1, - 'application/x-gnumeric' => 1, - 'application/x-gramps-xml' => 1, - 'application/x-gtar' => 1, - 'application/x-hdf' => 1, - 'application/x-install-instructions' => 1, - 'application/x-iso9660-image' => 1, - 'application/x-java-jnlp-file' => 1, - 'application/x-latex' => 1, - 'application/x-lzh-compressed' => 1, - 'application/x-mie' => 1, - 'application/x-mobipocket-ebook' => 1, - 'application/x-ms-application' => 1, - 'application/x-ms-shortcut' => 1, - 'application/x-ms-wmd' => 1, - 'application/x-ms-wmz' => 1, - 'application/x-ms-xbap' => 1, - 'application/x-msaccess' => 1, - 'application/x-msbinder' => 1, - 'application/x-mscardfile' => 1, - 'application/x-msclip' => 1, - 'application/x-msdownload' => 1, - 'application/x-msmediaview' => 1, - 'application/x-msmetafile' => 1, - 'application/x-msmoney' => 1, - 'application/x-mspublisher' => 1, - 'application/x-msschedule' => 1, - 'application/x-msterminal' => 1, - 'application/x-mswrite' => 1, - 'application/x-netcdf' => 1, - 'application/x-nzb' => 1, - 'application/x-pkcs12' => 1, - 'application/x-pkcs7-certificates' => 1, - 'application/x-pkcs7-certreqresp' => 1, - 'application/x-rar-compressed' => 1, - 'application/x-research-info-systems' => 1, - 'application/x-sh' => 1, - 'application/x-shar' => 1, - 'application/x-shockwave-flash' => 1, - 'application/x-silverlight-app' => 1, - 'application/x-silverlight-2' => 1, - 'application/x-sql' => 1, - 'application/x-stuffit' => 1, - 'application/x-stuffitx' => 1, - 'application/x-subrip' => 1, - 'application/x-sv4cpio' => 1, - 'application/x-sv4crc' => 1, - 'application/x-t3vm-image' => 1, - 'application/x-tads' => 1, - 'application/x-tar' => 1, - 'application/x-tcl' => 1, - 'application/x-tex' => 1, - 'application/x-tex-tfm' => 1, - 'application/x-texinfo' => 1, - 'application/x-tgif' => 1, - 'application/x-ustar' => 1, - 'application/x-wais-source' => 1, - 'application/x-x509-ca-cert' => 1, - 'application/x-xfig' => 1, - 'application/x-xliff+xml' => 1, - 'application/x-xpinstall' => 1, - 'application/x-xz' => 1, - 'application/x-zmachine' => 1, - 'application/xaml+xml' => 1, - 'application/xcap-diff+xml' => 1, - 'application/xenc+xml' => 1, - 'application/xhtml+xml' => 1, - 'application/xml' => 1, - 'application/xml-dtd' => 1, - 'application/xop+xml' => 1, - 'application/xproc+xml' => 1, - 'application/xslt+xml' => 1, - 'application/xspf+xml' => 1, - 'application/xv+xml' => 1, - 'application/yang' => 1, - 'application/yin+xml' => 1, - 'application/zip' => 1, - 'audio/adpcm' => 1, - 'audio/basic' => 1, - 'audio/midi' => 1, - 'audio/mp4' => 1, - 'audio/mpeg' => 1, - 'audio/ogg' => 1, - 'audio/s3m' => 1, - 'audio/silk' => 1, - 'audio/vnd.dece.audio' => 1, - 'audio/vnd.digital-winds' => 1, - 'audio/vnd.dra' => 1, - 'audio/vnd.dts' => 1, - 'audio/vnd.dts.hd' => 1, - 'audio/vnd.lucent.voice' => 1, - 'audio/vnd.ms-playready.media.pya' => 1, - 'audio/vnd.nuera.ecelp4800' => 1, - 'audio/vnd.nuera.ecelp7470' => 1, - 'audio/vnd.nuera.ecelp9600' => 1, - 'audio/vnd.rip' => 1, - 'audio/webm' => 1, - 'audio/x-aac' => 1, - 'audio/x-aiff' => 1, - 'audio/x-caf' => 1, - 'audio/x-flac' => 1, - 'audio/x-matroska' => 1, - 'audio/x-mpegurl' => 1, - 'audio/x-ms-wax' => 1, - 'audio/x-ms-wma' => 1, - 'audio/x-pn-realaudio' => 1, - 'audio/x-pn-realaudio-plugin' => 1, - 'audio/x-wav' => 1, - 'audio/xm' => 1, - 'chemical/x-cdx' => 1, - 'chemical/x-cif' => 1, - 'chemical/x-cmdf' => 1, - 'chemical/x-cml' => 1, - 'chemical/x-csml' => 1, - 'chemical/x-xyz' => 1, - 'image/bmp' => 1, - 'image/cgm' => 1, - 'image/g3fax' => 1, - 'image/gif' => 1, - 'image/ief' => 1, - 'image/jpeg' => 1, - 'image/ktx' => 1, - 'image/png' => 1, - 'image/prs.btif' => 1, - 'image/sgi' => 1, - 'image/svg+xml' => 1, - 'image/tiff' => 1, - 'image/vnd.adobe.photoshop' => 1, - 'image/vnd.dece.graphic' => 1, - 'image/vnd.dvb.subtitle' => 1, - 'image/vnd.djvu' => 1, - 'image/vnd.dwg' => 1, - 'image/vnd.dxf' => 1, - 'image/vnd.fastbidsheet' => 1, - 'image/vnd.fpx' => 1, - 'image/vnd.fst' => 1, - 'image/vnd.fujixerox.edmics-mmr' => 1, - 'image/vnd.fujixerox.edmics-rlc' => 1, - 'image/vnd.ms-modi' => 1, - 'image/vnd.ms-photo' => 1, - 'image/vnd.net-fpx' => 1, - 'image/vnd.wap.wbmp' => 1, - 'image/vnd.xiff' => 1, - 'image/webp' => 1, - 'image/x-3ds' => 1, - 'image/x-cmu-raster' => 1, - 'image/x-cmx' => 1, - 'image/x-freehand' => 1, - 'image/x-icon' => 1, - 'image/x-mrsid-image' => 1, - 'image/x-pcx' => 1, - 'image/x-pict' => 1, - 'image/x-portable-anymap' => 1, - 'image/x-portable-bitmap' => 1, - 'image/x-portable-graymap' => 1, - 'image/x-portable-pixmap' => 1, - 'image/x-rgb' => 1, - 'image/x-tga' => 1, - 'image/x-xbitmap' => 1, - 'image/x-xpixmap' => 1, - 'image/x-xwindowdump' => 1, - 'message/rfc822' => 1, - 'model/iges' => 1, - 'model/mesh' => 1, - 'model/vnd.collada+xml' => 1, - 'model/vnd.dwf' => 1, - 'model/vnd.gdl' => 1, - 'model/vnd.gtw' => 1, - 'model/vnd.mts' => 1, - 'model/vnd.vtu' => 1, - 'model/vrml' => 1, - 'model/x3d+binary' => 1, - 'model/x3d+vrml' => 1, - 'model/x3d+xml' => 1, - 'video/3gpp' => 1, - 'video/3gpp2' => 1, - 'video/h261' => 1, - 'video/h263' => 1, - 'video/h264' => 1, - 'video/jpeg' => 1, - 'video/jpm' => 1, - 'video/mj2' => 1, - 'video/mp4' => 1, - 'video/mpeg' => 1, - 'video/ogg' => 1, - 'video/quicktime' => 1, - 'video/vnd.dece.hd' => 1, - 'video/vnd.dece.mobile' => 1, - 'video/vnd.dece.pd' => 1, - 'video/vnd.dece.sd' => 1, - 'video/vnd.dece.video' => 1, - 'video/vnd.dvb.file' => 1, - 'video/vnd.fvt' => 1, - 'video/vnd.mpegurl' => 1, - 'video/vnd.ms-playready.media.pyv' => 1, - 'video/vnd.uvvu.mp4' => 1, - 'video/vnd.vivo' => 1, - 'video/webm' => 1, - 'video/x-f4v' => 1, - 'video/x-fli' => 1, - 'video/x-flv' => 1, - 'video/x-m4v' => 1, - 'video/x-matroska' => 1, - 'video/x-mng' => 1, - 'video/x-ms-asf' => 1, - 'video/x-ms-vob' => 1, - 'video/x-ms-wm' => 1, - 'video/x-ms-wmv' => 1, - 'video/x-ms-wmx' => 1, - 'video/x-ms-wvx' => 1, - 'video/x-msvideo' => 1, - 'video/x-sgi-movie' => 1, - 'video/x-smv' => 1, - 'x-conference/x-cooltalk' => 1, - ), ); diff --git a/common/framework/security/htmlfilter.php b/common/framework/security/htmlfilter.php index 4cee601b0..fce40cf71 100644 --- a/common/framework/security/htmlfilter.php +++ b/common/framework/security/htmlfilter.php @@ -87,21 +87,6 @@ class HTMLFilter return $output; } - /** - * Remove embedded media from HTML content. - * - * @param string $input - * @param string $replacement - * @return string - */ - public static function removeEmbeddedMedia($input, $replacement = '') - { - $input = preg_replace('!]*>(.*?)?!is', $replacement, $input); - $input = preg_replace('!]*>(.*?)?!is', $replacement, $input); - $input = preg_replace('!]*editor_component="multimedia_link"[^>]*>(.*?)?!is', $replacement, $input); - return $input; - } - /** * Get an instance of HTMLPurifier. * @@ -136,7 +121,7 @@ class HTMLFilter $config->set('HTML.SafeEmbed', true); $config->set('HTML.SafeIframe', true); $config->set('HTML.SafeObject', true); - $config->set('URI.SafeIframeRegexp', self::_getIframeWhitelist()); + $config->set('URI.SafeIframeRegexp', MediaFilter::getIframeWhitelistRegex()); // Set the serializer path. $config->set('Cache.SerializerPath', RX_BASEDIR . 'files/cache/htmlpurifier'); @@ -384,38 +369,6 @@ class HTMLFilter } } - /** - * Get the object whitelist as a regular expression. - * - * @return string - */ - protected static function _getObjectWhitelist() - { - $domains = \EmbedFilter::getInstance()->getWhiteUrlList(); - $result = array(); - foreach($domains as $domain) - { - $result[] = preg_quote($domain, '%'); - } - return '%^https?://(' . implode('|', $result) . ')%'; - } - - /** - * Get the iframe whitelist as a regular expression. - * - * @return string - */ - protected static function _getIframeWhitelist() - { - $domains = \EmbedFilter::getInstance()->getWhiteIframeUrlList(); - $result = array(); - foreach($domains as $domain) - { - $result[] = preg_quote($domain, '%'); - } - return '%^https?://(' . implode('|', $result) . ')%'; - } - /** * Rhymix-specific preprocessing method. * @@ -447,7 +400,7 @@ class HTMLFilter }, $content); // Remove object and embed URLs that are not allowed. - $whitelist = self::_getObjectWhitelist(); + $whitelist = MediaFilter::getObjectWhitelistRegex(); $content = preg_replace_callback('!<(object|embed|param)([^>]+)>!i', function($matches) use($whitelist) { return preg_replace_callback('!([a-zA-Z0-9_-]+)="([^"]+)"!', function($attr) use($whitelist) { if (in_array($attr[1], array('data', 'src', 'href', 'url', 'movie', 'source'))) diff --git a/common/framework/security/mediafilter.php b/common/framework/security/mediafilter.php new file mode 100644 index 000000000..4460b0899 --- /dev/null +++ b/common/framework/security/mediafilter.php @@ -0,0 +1,180 @@ +]*>(.*?)?!is', $replacement, $input); + $input = preg_replace('!]*>(.*?)?!is', $replacement, $input); + $input = preg_replace('!]*editor_component="multimedia_link"[^>]*>(.*?)?!is', $replacement, $input); + return $input; + } + + /** + * Load whitelists. + * + * @param array $custom_whitelist + * @return void + */ + protected static function _loadWhitelists($custom_whitelist = array()) + { + $default_whitelist = (include RX_BASEDIR . 'common/defaults/whitelist.php'); + self::$_object_whitelist = array(); + self::$_iframe_whitelist = array(); + + if(count($custom_whitelist)) + { + if(!is_array($custom_whitelist) || !isset($custom_whitelist['iframe']) || !isset($custom_whitelist['object'])) + { + $whitelist = array( + 'iframe' => isset($whitelist->iframe) ? $whitelist->iframe : array(), + 'object' => isset($whitelist->object) ? $whitelist->object : array(), + ); + } + foreach ($custom_whitelist['iframe'] as $prefix) + { + self::$_iframe_whitelist[] = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix; + } + foreach ($custom_whitelist['object'] as $prefix) + { + self::$_object_whitelist[] = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix; + } + } + else + { + foreach ($default_whitelist['iframe'] as $prefix) + { + self::$_iframe_whitelist[] = $prefix; + } + foreach ($default_whitelist['object'] as $prefix) + { + self::$_object_whitelist[] = $prefix; + } + if ($iframe_whitelist = config('mediafilter.iframe') ?: config('embedfilter.iframe')) + { + foreach ($iframe_whitelist as $prefix) + { + self::$_iframe_whitelist[] = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix; + } + } + if ($object_whitelist = config('mediafilter.object') ?: config('embedfilter.object')) + { + foreach ($object_whitelist as $prefix) + { + self::$_object_whitelist[] = preg_match('@^https?://(.*)$@i', $prefix, $matches) ? $matches[1] : $prefix; + } + } + } + + self::$_object_whitelist = array_unique(self::$_object_whitelist); + self::$_iframe_whitelist = array_unique(self::$_iframe_whitelist); + natcasesort(self::$_object_whitelist); + natcasesort(self::$_iframe_whitelist); + } +} diff --git a/common/legacy.php b/common/legacy.php index 729166e14..665d443ff 100644 --- a/common/legacy.php +++ b/common/legacy.php @@ -1036,7 +1036,7 @@ function stripEmbedTagForAdmin(&$content, $writer_member_srl) $security_msg = '
' . '

' . lang('security_warning_embed') . '

'; - $content = Rhymix\Framework\Security\HTMLFilter::removeEmbeddedMedia($content, $security_msg); + $content = Rhymix\Framework\Security\MediaFilter::removeEmbeddedMedia($content, $security_msg); } return; diff --git a/tests/unit/framework/security/HTMLFilterTest.php b/tests/unit/framework/security/HTMLFilterTest.php index 01162dc9c..169b41c48 100644 --- a/tests/unit/framework/security/HTMLFilterTest.php +++ b/tests/unit/framework/security/HTMLFilterTest.php @@ -64,20 +64,4 @@ class HTMLFilterTest extends \Codeception\TestCase\Test $this->assertEquals($test[1], Rhymix\Framework\Security\HTMLFilter::clean($test[0])); } } - - public function testRemoveEmbeddedMedia() - { - $tests = array( - '
' => '
', - '
' => '
', - '
' => '
', - '
' => '
', - '
' => '
', - ); - - foreach ($tests as $from => $to) - { - $this->assertEquals($to, Rhymix\Framework\Security\HTMLFilter::removeEmbeddedMedia($from)); - } - } } diff --git a/tests/unit/framework/security/MediaFilterTest.php b/tests/unit/framework/security/MediaFilterTest.php new file mode 100644 index 000000000..920ad2fd7 --- /dev/null +++ b/tests/unit/framework/security/MediaFilterTest.php @@ -0,0 +1,47 @@ +assertTrue(in_array('www.youtube.com/', Rhymix\Framework\Security\MediaFilter::getIframeWhitelist())); + $this->assertFalse(in_array('random-website.com/', Rhymix\Framework\Security\MediaFilter::getIframeWhitelist())); + + // iframe whitelist as regex. + $this->assertTrue(strpos(Rhymix\Framework\Security\MediaFilter::getIframeWhitelistRegex(), '|www\.youtube\.com/') !== false); + $this->assertFalse(strpos(Rhymix\Framework\Security\MediaFilter::getIframeWhitelistRegex(), 'www.youtube.com/') !== false); + + // Match individual URL against iframe whitelist. + $this->assertTrue(Rhymix\Framework\Security\MediaFilter::matchIframeWhitelist('https://www.youtube.com/v')); + $this->assertFalse(Rhymix\Framework\Security\MediaFilter::matchIframeWhitelist('http://www-youtube.com/v')); + + // object whitelist as array. + $this->assertTrue(in_array('www.youtube.com/', Rhymix\Framework\Security\MediaFilter::getObjectWhitelist())); + $this->assertFalse(in_array('random-website.com/', Rhymix\Framework\Security\MediaFilter::getObjectWhitelist())); + + // object whitelist as regex. + $this->assertTrue(strpos(Rhymix\Framework\Security\MediaFilter::getObjectWhitelistRegex(), '|www\.youtube\.com/') !== false); + $this->assertFalse(strpos(Rhymix\Framework\Security\MediaFilter::getObjectWhitelistRegex(), 'www.youtube.com/') !== false); + + // Match individual URL against object whitelist. + $this->assertTrue(Rhymix\Framework\Security\MediaFilter::matchObjectWhitelist('https://www.youtube.com/v')); + $this->assertFalse(Rhymix\Framework\Security\MediaFilter::matchObjectWhitelist('http://www-youtube.com/v')); + } + + public function testRemoveEmbeddedMedia() + { + $tests = array( + '
' => '
', + '
' => '
', + '
' => '
', + '
' => '
', + '
' => '
', + ); + + foreach ($tests as $from => $to) + { + $this->assertEquals($to, Rhymix\Framework\Security\MediaFilter::removeEmbeddedMedia($from)); + } + } +} From 2effbea06f04380ff70a8b13f04ab6a53bb9fb5d Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 13 Mar 2016 09:59:26 +0900 Subject: [PATCH 068/205] Update all other places to use new MediaFilter class --- classes/context/Context.class.php | 4 ++-- common/defaults/config.php | 2 +- common/framework/compat/configparser.php | 6 ++--- modules/admin/admin.admin.controller.php | 29 ++++++++++++++---------- modules/admin/admin.admin.view.php | 5 ++-- modules/admin/tpl/config_security.html | 8 +++---- 6 files changed, 29 insertions(+), 25 deletions(-) diff --git a/classes/context/Context.class.php b/classes/context/Context.class.php index c61deeae0..66c11a077 100644 --- a/classes/context/Context.class.php +++ b/classes/context/Context.class.php @@ -574,8 +574,8 @@ class Context $db_info->sitelock_title = $config['lock']['title']; $db_info->sitelock_message = $config['lock']['message']; $db_info->sitelock_whitelist = count($config['lock']['allow']) ? $config['lock']['allow'] : array('127.0.0.1'); - $db_info->embed_white_iframe = $config['embedfilter']['iframe']; - $db_info->embed_white_object = $config['embedfilter']['object']; + $db_info->embed_white_iframe = $config['mediafilter']['iframe'] ?: $config['embedfilter']['iframe']; + $db_info->embed_white_object = $config['mediafilter']['object'] ?: $config['embedfilter']['object']; $db_info->use_mobile_view = $config['use_mobile_view'] ? 'Y' : 'N'; $db_info->use_prepared_statements = $config['use_prepared_statements'] ? 'Y' : 'N'; $db_info->use_rewrite = $config['use_rewrite'] ? 'Y' : 'N'; diff --git a/common/defaults/config.php b/common/defaults/config.php index 0fc4fe801..05c9426dd 100644 --- a/common/defaults/config.php +++ b/common/defaults/config.php @@ -97,7 +97,7 @@ return array( 'display_to' => 'admin', 'allow' => array(), ), - 'embedfilter' => array( + 'mediafilter' => array( 'iframe' => array(), 'object' => array(), ), diff --git a/common/framework/compat/configparser.php b/common/framework/compat/configparser.php index b4d6fa067..c88300dd1 100644 --- a/common/framework/compat/configparser.php +++ b/common/framework/compat/configparser.php @@ -216,14 +216,14 @@ class ConfigParser } $config['lock']['allow'] = array_values($db_info->sitelock_whitelist); - // Convert embed filter configuration. + // Convert media filter configuration. if (is_array($db_info->embed_white_iframe)) { $whitelist = array_unique(array_map(function($item) { return preg_match('@^https?://(.*)$@i', $item, $matches) ? $matches[1] : $item; }, $db_info->embed_white_iframe)); natcasesort($whitelist); - $config['embedfilter']['iframe'] = $whitelist; + $config['mediafilter']['iframe'] = $whitelist; } if (is_array($db_info->embed_white_object)) { @@ -231,7 +231,7 @@ class ConfigParser return preg_match('@^https?://(.*)$@i', $item, $matches) ? $matches[1] : $item; }, $db_info->embed_white_object)); natcasesort($whitelist); - $config['embedfilter']['object'] = $whitelist; + $config['mediafilter']['object'] = $whitelist; } // Convert miscellaneous configuration. diff --git a/modules/admin/admin.admin.controller.php b/modules/admin/admin.admin.controller.php index 201b13756..023cd7586 100644 --- a/modules/admin/admin.admin.controller.php +++ b/modules/admin/admin.admin.controller.php @@ -556,26 +556,31 @@ class adminAdminController extends admin $vars = Context::getRequestVars(); // iframe filter - $embed_iframe = $vars->embedfilter_iframe; - $embed_iframe = array_filter(array_map('trim', preg_split('/[\r\n]/', $embed_iframe)), function($item) { + $iframe_whitelist = $vars->mediafilter_iframe; + $iframe_whitelist = array_filter(array_map('trim', preg_split('/[\r\n]/', $iframe_whitelist)), function($item) { return $item !== ''; }); - $embed_iframe = array_unique(array_map(function($item) { + $iframe_whitelist = array_unique(array_map(function($item) { return preg_match('@^https?://(.*)$@i', $item, $matches) ? $matches[1] : $item; - }, $embed_iframe)); - natcasesort($embed_iframe); - Rhymix\Framework\Config::set('embedfilter.iframe', array_values($embed_iframe)); + }, $iframe_whitelist)); + natcasesort($iframe_whitelist); + Rhymix\Framework\Config::set('mediafilter.iframe', array_values($iframe_whitelist)); // object filter - $embed_object = $vars->embedfilter_object; - $embed_object = array_filter(array_map('trim', preg_split('/[\r\n]/', $embed_object)), function($item) { + $object_whitelist = $vars->mediafilter_object; + $object_whitelist = array_filter(array_map('trim', preg_split('/[\r\n]/', $object_whitelist)), function($item) { return $item !== ''; }); - $embed_object = array_unique(array_map(function($item) { + $object_whitelist = array_unique(array_map(function($item) { return preg_match('@^https?://(.*)$@i', $item, $matches) ? $matches[1] : $item; - }, $embed_object)); - natcasesort($embed_object); - Rhymix\Framework\Config::set('embedfilter.object', array_values($embed_object)); + }, $object_whitelist)); + natcasesort($object_whitelist); + Rhymix\Framework\Config::set('mediafilter.object', array_values($object_whitelist)); + + // Remove old embed filter + $config = Rhymix\Framework\Config::getAll(); + unset($config['embedfilter']); + Rhymix\Framework\Config::setAll($config); // Admin IP access control $allowed_ip = array_map('trim', preg_split('/[\r\n]/', $vars->admin_allowed_ip)); diff --git a/modules/admin/admin.admin.view.php b/modules/admin/admin.admin.view.php index fbe68f961..2ff837e00 100644 --- a/modules/admin/admin.admin.view.php +++ b/modules/admin/admin.admin.view.php @@ -418,9 +418,8 @@ class adminAdminView extends admin function dispAdminConfigSecurity() { // Load embed filter. - $oEmbedFilter = EmbedFilter::getInstance(); - context::set('embedfilter_iframe', implode(PHP_EOL, $oEmbedFilter->whiteIframeUrlList)); - context::set('embedfilter_object', implode(PHP_EOL, $oEmbedFilter->whiteUrlList)); + context::set('mediafilter_iframe', implode(PHP_EOL, Rhymix\Framework\Security\MediaFilter::getIframeWhitelist())); + context::set('mediafilter_object', implode(PHP_EOL, Rhymix\Framework\Security\MediaFilter::getObjectWhitelist())); // Admin IP access control $allowed_ip = Rhymix\Framework\Config::get('admin.allow'); diff --git a/modules/admin/tpl/config_security.html b/modules/admin/tpl/config_security.html index ff81ba143..a8050b94c 100644 --- a/modules/admin/tpl/config_security.html +++ b/modules/admin/tpl/config_security.html @@ -8,15 +8,15 @@
- +
- +
- +
- +
From 0adb13ca30a37e5d358b7ab62eab16fd27b5784d Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 13 Mar 2016 10:18:52 +0900 Subject: [PATCH 069/205] Add inRanges() and validateRanges() methods to IpFilter class --- classes/context/Context.class.php | 8 ++--- classes/security/IpFilter.class.php | 18 ++---------- common/framework/debug.php | 8 ++--- common/framework/ipfilter.php | 37 ++++++++++++++++++++++++ modules/admin/admin.admin.controller.php | 23 ++++----------- modules/admin/admin.admin.view.php | 17 ++--------- modules/member/member.admin.model.php | 18 ++---------- 7 files changed, 52 insertions(+), 77 deletions(-) diff --git a/classes/context/Context.class.php b/classes/context/Context.class.php index 66c11a077..4a47369e1 100644 --- a/classes/context/Context.class.php +++ b/classes/context/Context.class.php @@ -1478,13 +1478,9 @@ class Context } // Allow if the current user is in the list of allowed IPs. - $allowed_list = config('lock.allow'); - foreach ($allowed_list as $allowed_ip) + if (Rhymix\Framework\IpFilter::inRanges(RX_CLIENT_IP, config('lock.allow'))) { - if (Rhymix\Framework\IpFilter::inRange(RX_CLIENT_IP, $allowed_ip)) - { - return; - } + return; } // Set headers and constants for backward compatibility. diff --git a/classes/security/IpFilter.class.php b/classes/security/IpFilter.class.php index ea49b8195..455485329 100644 --- a/classes/security/IpFilter.class.php +++ b/classes/security/IpFilter.class.php @@ -6,26 +6,12 @@ class IpFilter public function filter($ip_list, $ip = NULL) { if(!$ip) $ip = $_SERVER['REMOTE_ADDR']; - foreach($ip_list as $filter) - { - if(Rhymix\Framework\IpFilter::inRange($ip, $filter)) - { - return true; - } - } - return false; + return Rhymix\Framework\IpFilter::inRanges($ip, $ip_list); } public function validate($ip_list = array()) { - foreach($ip_list as $filter) - { - if(!Rhymix\Framework\IpFilter::validateRange($filter)) - { - return false; - } - } - return true; + return Rhymix\Framework\IpFilter::validateRanges($ip_list); } } diff --git a/common/framework/debug.php b/common/framework/debug.php index b3653a943..dd2cee5a4 100644 --- a/common/framework/debug.php +++ b/common/framework/debug.php @@ -445,13 +445,9 @@ class Debug return $cache = true; case 'ip': - $allowed_ip = Config::get('debug.allow'); - foreach ($allowed_ip as $range) + if (IpFilter::inRanges(RX_CLIENT_IP, Config::get('debug.allow'))) { - if (IpFilter::inRange(RX_CLIENT_IP, $range)) - { - return $cache = true; - } + return $cache = true; } return $cache = false; diff --git a/common/framework/ipfilter.php b/common/framework/ipfilter.php index 21a087256..e80170974 100644 --- a/common/framework/ipfilter.php +++ b/common/framework/ipfilter.php @@ -66,6 +66,25 @@ class IpFilter } } + /** + * Check whether the given IP address belongs to a set of ranges. + * + * @param string $ip + * @param array $ranges + * @return bool + */ + public static function inRanges($ip, array $ranges) + { + foreach ($ranges as $range) + { + if (self::inRange($ip, $range)) + { + return true; + } + } + return false; + } + /** * Check whether a range definition is valid. * @@ -91,6 +110,24 @@ class IpFilter return false; } + /** + * Check whether a set of range definitions is valid. + * + * @param array $ranges + * @return bool + */ + public static function validateRanges(array $ranges) + { + foreach ($ranges as $range) + { + if (!self::validateRange($range)) + { + return false; + } + } + return true; + } + /** * Get real IP from CloudFlare headers. * diff --git a/modules/admin/admin.admin.controller.php b/modules/admin/admin.admin.controller.php index 023cd7586..50c4f431e 100644 --- a/modules/admin/admin.admin.controller.php +++ b/modules/admin/admin.admin.controller.php @@ -587,7 +587,7 @@ class adminAdminController extends admin $allowed_ip = array_unique(array_filter($allowed_ip, function($item) { return $item !== ''; })); - if (!IpFilter::validate($whitelist)) { + if (!Rhymix\Framework\IpFilter::validateRanges($allowed_ip)) { return new Object(-1, 'msg_invalid_ip'); } @@ -595,7 +595,7 @@ class adminAdminController extends admin $denied_ip = array_unique(array_filter($denied_ip, function($item) { return $item !== ''; })); - if (!IpFilter::validate($whitelist)) { + if (!Rhymix\Framework\IpFilter::validateRanges($denied_ip)) { return new Object(-1, 'msg_invalid_ip'); } @@ -771,30 +771,17 @@ class adminAdminController extends admin if ($vars->sitelock_locked === 'Y') { - $allowed_localhost = false; - $allowed_current = false; - foreach ($allowed_ip as $range) - { - if (Rhymix\Framework\IpFilter::inRange('127.0.0.1', $range)) - { - $allowed_localhost = true; - } - if (Rhymix\Framework\IpFilter::inRange(RX_CLIENT_IP, $range)) - { - $allowed_current = true; - } - } - if (!$allowed_localhost) + if (!Rhymix\Framework\IpFilter::inRanges('127.0.0.1', $allowed_ip)) { array_unshift($allowed_ip, '127.0.0.1'); } - if (!$allowed_current) + if (!Rhymix\Framework\IpFilter::inRanges(RX_CLIENT_IP, $allowed_ip)) { array_unshift($allowed_ip, RX_CLIENT_IP); } } - if (!IpFilter::validate($whitelist)) + if (!Rhymix\Framework\IpFilter::validateRanges($allowed_ip)) { return new Object(-1, 'msg_invalid_ip'); } diff --git a/modules/admin/admin.admin.view.php b/modules/admin/admin.admin.view.php index 2ff837e00..6cac017c9 100644 --- a/modules/admin/admin.admin.view.php +++ b/modules/admin/admin.admin.view.php @@ -518,24 +518,11 @@ class adminAdminView extends admin Context::set('sitelock_message', escape(Rhymix\Framework\Config::get('lock.message'))); $allowed_ip = Rhymix\Framework\Config::get('lock.allow') ?: array(); - $allowed_localhost = false; - $allowed_current = false; - foreach ($allowed_ip as $range) - { - if (Rhymix\Framework\IpFilter::inRange('127.0.0.1', $range)) - { - $allowed_localhost = true; - } - if (Rhymix\Framework\IpFilter::inRange(RX_CLIENT_IP, $range)) - { - $allowed_current = true; - } - } - if (!$allowed_localhost) + if (!Rhymix\Framework\IpFilter::inRanges('127.0.0.1', $allowed_ip)) { array_unshift($allowed_ip, '127.0.0.1'); } - if (!$allowed_current) + if (!Rhymix\Framework\IpFilter::inRanges(RX_CLIENT_IP, $allowed_ip)) { array_unshift($allowed_ip, RX_CLIENT_IP); } diff --git a/modules/member/member.admin.model.php b/modules/member/member.admin.model.php index 9bca07a60..0c0c59887 100644 --- a/modules/member/member.admin.model.php +++ b/modules/member/member.admin.model.php @@ -298,26 +298,12 @@ class memberAdminModel extends member { if ($allow_list = ($allow_list === null) ? config('admin.allow') : $allow_list) { - foreach ($allow_list as $range) - { - if (Rhymix\Framework\IpFilter::inRange(RX_CLIENT_IP, $range)) - { - return true; - } - } - return false; + return Rhymix\Framework\IpFilter::inRanges(RX_CLIENT_IP, $allow_list); } if ($deny_list = ($deny_list === null) ? config('admin.deny') : $deny_list) { - foreach ($deny_list as $range) - { - if (Rhymix\Framework\IpFilter::inRange(RX_CLIENT_IP, $range)) - { - return false; - } - } - return true; + return !Rhymix\Framework\IpFilter::inRanges(RX_CLIENT_IP, $deny_list); } return true; From 544170b530c035a08113b6374af00e5d304fbafc Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 13 Mar 2016 10:24:52 +0900 Subject: [PATCH 070/205] Move IpFilter class to Security namespace --- classes/context/Context.class.php | 2 +- classes/security/IpFilter.class.php | 4 +- common/constants.php | 4 +- common/framework/debug.php | 2 +- common/framework/{ => security}/ipfilter.php | 2 +- modules/admin/admin.admin.controller.php | 12 +-- modules/admin/admin.admin.view.php | 4 +- modules/member/member.admin.model.php | 4 +- tests/unit/framework/IpFilterTest.php | 77 ------------------- .../unit/framework/security/IpFilterTest.php | 77 +++++++++++++++++++ 10 files changed, 94 insertions(+), 94 deletions(-) rename common/framework/{ => security}/ipfilter.php (99%) delete mode 100644 tests/unit/framework/IpFilterTest.php create mode 100644 tests/unit/framework/security/IpFilterTest.php diff --git a/classes/context/Context.class.php b/classes/context/Context.class.php index 4a47369e1..44ec47d6d 100644 --- a/classes/context/Context.class.php +++ b/classes/context/Context.class.php @@ -1478,7 +1478,7 @@ class Context } // Allow if the current user is in the list of allowed IPs. - if (Rhymix\Framework\IpFilter::inRanges(RX_CLIENT_IP, config('lock.allow'))) + if (Rhymix\Framework\Security\IpFilter::inRanges(RX_CLIENT_IP, config('lock.allow'))) { return; } diff --git a/classes/security/IpFilter.class.php b/classes/security/IpFilter.class.php index 455485329..23159dc79 100644 --- a/classes/security/IpFilter.class.php +++ b/classes/security/IpFilter.class.php @@ -6,12 +6,12 @@ class IpFilter public function filter($ip_list, $ip = NULL) { if(!$ip) $ip = $_SERVER['REMOTE_ADDR']; - return Rhymix\Framework\IpFilter::inRanges($ip, $ip_list); + return Rhymix\Framework\Security\IpFilter::inRanges($ip, $ip_list); } public function validate($ip_list = array()) { - return Rhymix\Framework\IpFilter::validateRanges($ip_list); + return Rhymix\Framework\Security\IpFilter::validateRanges($ip_list); } } diff --git a/common/constants.php b/common/constants.php index cab1fd256..d3d992896 100644 --- a/common/constants.php +++ b/common/constants.php @@ -53,8 +53,8 @@ else */ if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) { - include_once __DIR__ . '/framework/ipfilter.php'; - Rhymix\Framework\IpFilter::getCloudFlareRealIP(); + include_once __DIR__ . '/framework/security/ipfilter.php'; + Rhymix\Framework\Security\IpFilter::getCloudFlareRealIP(); } if (isset($_SERVER['REMOTE_ADDR']) && preg_match('/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/', $_SERVER['REMOTE_ADDR'], $matches)) { diff --git a/common/framework/debug.php b/common/framework/debug.php index dd2cee5a4..4c69a4484 100644 --- a/common/framework/debug.php +++ b/common/framework/debug.php @@ -445,7 +445,7 @@ class Debug return $cache = true; case 'ip': - if (IpFilter::inRanges(RX_CLIENT_IP, Config::get('debug.allow'))) + if (Security\IpFilter::inRanges(RX_CLIENT_IP, Config::get('debug.allow'))) { return $cache = true; } diff --git a/common/framework/ipfilter.php b/common/framework/security/ipfilter.php similarity index 99% rename from common/framework/ipfilter.php rename to common/framework/security/ipfilter.php index e80170974..b52f13ac1 100644 --- a/common/framework/ipfilter.php +++ b/common/framework/security/ipfilter.php @@ -1,6 +1,6 @@ sitelock_locked === 'Y') { - if (!Rhymix\Framework\IpFilter::inRanges('127.0.0.1', $allowed_ip)) + if (!Rhymix\Framework\Security\IpFilter::inRanges('127.0.0.1', $allowed_ip)) { array_unshift($allowed_ip, '127.0.0.1'); } - if (!Rhymix\Framework\IpFilter::inRanges(RX_CLIENT_IP, $allowed_ip)) + if (!Rhymix\Framework\Security\IpFilter::inRanges(RX_CLIENT_IP, $allowed_ip)) { array_unshift($allowed_ip, RX_CLIENT_IP); } } - if (!Rhymix\Framework\IpFilter::validateRanges($allowed_ip)) + if (!Rhymix\Framework\Security\IpFilter::validateRanges($allowed_ip)) { return new Object(-1, 'msg_invalid_ip'); } diff --git a/modules/admin/admin.admin.view.php b/modules/admin/admin.admin.view.php index 6cac017c9..a74801f38 100644 --- a/modules/admin/admin.admin.view.php +++ b/modules/admin/admin.admin.view.php @@ -518,11 +518,11 @@ class adminAdminView extends admin Context::set('sitelock_message', escape(Rhymix\Framework\Config::get('lock.message'))); $allowed_ip = Rhymix\Framework\Config::get('lock.allow') ?: array(); - if (!Rhymix\Framework\IpFilter::inRanges('127.0.0.1', $allowed_ip)) + if (!Rhymix\Framework\Security\IpFilter::inRanges('127.0.0.1', $allowed_ip)) { array_unshift($allowed_ip, '127.0.0.1'); } - if (!Rhymix\Framework\IpFilter::inRanges(RX_CLIENT_IP, $allowed_ip)) + if (!Rhymix\Framework\Security\IpFilter::inRanges(RX_CLIENT_IP, $allowed_ip)) { array_unshift($allowed_ip, RX_CLIENT_IP); } diff --git a/modules/member/member.admin.model.php b/modules/member/member.admin.model.php index 0c0c59887..8e9682bb6 100644 --- a/modules/member/member.admin.model.php +++ b/modules/member/member.admin.model.php @@ -298,12 +298,12 @@ class memberAdminModel extends member { if ($allow_list = ($allow_list === null) ? config('admin.allow') : $allow_list) { - return Rhymix\Framework\IpFilter::inRanges(RX_CLIENT_IP, $allow_list); + return Rhymix\Framework\Security\IpFilter::inRanges(RX_CLIENT_IP, $allow_list); } if ($deny_list = ($deny_list === null) ? config('admin.deny') : $deny_list) { - return !Rhymix\Framework\IpFilter::inRanges(RX_CLIENT_IP, $deny_list); + return !Rhymix\Framework\Security\IpFilter::inRanges(RX_CLIENT_IP, $deny_list); } return true; diff --git a/tests/unit/framework/IpFilterTest.php b/tests/unit/framework/IpFilterTest.php deleted file mode 100644 index 13bb96318..000000000 --- a/tests/unit/framework/IpFilterTest.php +++ /dev/null @@ -1,77 +0,0 @@ -assertTrue(Rhymix\Framework\IpFilter::inRange('10.0.127.191', '10.0.127.191')); - $this->assertFalse(Rhymix\Framework\IpFilter::inRange('10.1.131.177', '10.1.131.178')); - $this->assertTrue(Rhymix\Framework\IpFilter::inRange('127.0.0.1', '127.0.0.0/8')); - $this->assertFalse(Rhymix\Framework\IpFilter::inRange('172.34.0.0', '172.16.0.0/12')); - $this->assertTrue(Rhymix\Framework\IpFilter::inRange('192.168.18.214', '192.168.16.0/22')); - $this->assertFalse(Rhymix\Framework\IpFilter::inRange('192.168.18.214', '192.168.16.0/23')); - } - - public function testIPv6CIDR() - { - $this->assertTrue(Rhymix\Framework\IpFilter::inRange('::1', '::1/128')); - $this->assertFalse(Rhymix\Framework\IpFilter::inRange('::1', '::2')); - $this->assertTrue(Rhymix\Framework\IpFilter::inRange('2400:cb00::1234', '2400:cb00::/32')); - $this->assertFalse(Rhymix\Framework\IpFilter::inRange('2405:8100::1234', '2400:cb00::/32')); - } - - public function testIPv4Wildcard() - { - $this->assertTrue(Rhymix\Framework\IpFilter::inRange('192.168.134.241', '192.168.134.*')); - $this->assertTrue(Rhymix\Framework\IpFilter::inRange('192.168.134.241', '192.168.*.*')); - $this->assertFalse(Rhymix\Framework\IpFilter::inRange('192.168.134.241', '192.168.136.*')); - $this->assertFalse(Rhymix\Framework\IpFilter::inRange('192.168.134.241', '192.172.*.*')); - } - - public function testIPv4Hyphen() - { - $this->assertTrue(Rhymix\Framework\IpFilter::inRange('192.168.134.241', '192.168.134.0-192.168.134.255')); - $this->assertTrue(Rhymix\Framework\IpFilter::inRange('192.168.134.241', '192.168.128.16-192.168.145.0')); - $this->assertFalse(Rhymix\Framework\IpFilter::inRange('192.168.134.241', '192.168.134.242-192.168.244.7')); - $this->assertFalse(Rhymix\Framework\IpFilter::inRange('192.168.134.241', '192.168.100.255-192.168.133.19')); - } - - public function testValidator() - { - $this->assertTrue(Rhymix\Framework\IpFilter::validateRange('192.168.0.1')); - $this->assertTrue(Rhymix\Framework\IpFilter::validateRange('192.168.0.0/16')); - $this->assertTrue(Rhymix\Framework\IpFilter::validateRange('192.168.*.*')); - $this->assertTrue(Rhymix\Framework\IpFilter::validateRange('192.168.*')); - $this->assertTrue(Rhymix\Framework\IpFilter::validateRange('192.168.0.0-192.168.255.255')); - $this->assertTrue(Rhymix\Framework\IpFilter::validateRange('2400:cb00::/32')); - $this->assertFalse(Rhymix\Framework\IpFilter::validateRange('192.168.0.0~192.168.255.255')); - } - - public function testLegacy() - { - $this->assertTrue(\IpFilter::filter(array('192.168.134.241'), '192.168.134.241')); - $this->assertTrue(\IpFilter::filter(array('192.168.134.0-192.168.134.255'), '192.168.134.241')); - $this->assertTrue(\IpFilter::filter(array('127.0.0.1', '192.168.134.241'), '192.168.134.241')); - $this->assertTrue(\IpFilter::filter(array('192.168.134.*'), '192.168.134.241')); - $this->assertTrue(\IpFilter::filter(array('192.168.*'), '192.168.134.241')); - $this->assertFalse(\IpFilter::filter(array('127.0.0.1'), '192.168.134.241')); - } - - public function testCloudFlareRealIP() - { - $_SERVER['HTTP_CF_CONNECTING_IP'] = '192.168.134.241'; - - $_SERVER['REMOTE_ADDR'] = '192.168.10.1'; - $this->assertFalse(Rhymix\Framework\IpFilter::getCloudFlareRealIP()); - $this->assertEquals('192.168.10.1', $_SERVER['REMOTE_ADDR']); - - $_SERVER['REMOTE_ADDR'] = '108.162.192.121'; - $this->assertEquals('192.168.134.241', Rhymix\Framework\IpFilter::getCloudFlareRealIP()); - $this->assertEquals('192.168.134.241', $_SERVER['REMOTE_ADDR']); - - unset($_SERVER['HTTP_CF_CONNECTING_IP']); - $_SERVER['REMOTE_ADDR'] = '192.168.10.1'; - $this->assertFalse(Rhymix\Framework\IpFilter::getCloudFlareRealIP()); - $this->assertEquals('192.168.10.1', $_SERVER['REMOTE_ADDR']); - } -} diff --git a/tests/unit/framework/security/IpFilterTest.php b/tests/unit/framework/security/IpFilterTest.php new file mode 100644 index 000000000..235939253 --- /dev/null +++ b/tests/unit/framework/security/IpFilterTest.php @@ -0,0 +1,77 @@ +assertTrue(Rhymix\Framework\Security\IpFilter::inRange('10.0.127.191', '10.0.127.191')); + $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('10.1.131.177', '10.1.131.178')); + $this->assertTrue(Rhymix\Framework\Security\IpFilter::inRange('127.0.0.1', '127.0.0.0/8')); + $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('172.34.0.0', '172.16.0.0/12')); + $this->assertTrue(Rhymix\Framework\Security\IpFilter::inRange('192.168.18.214', '192.168.16.0/22')); + $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('192.168.18.214', '192.168.16.0/23')); + } + + public function testIPv6CIDR() + { + $this->assertTrue(Rhymix\Framework\Security\IpFilter::inRange('::1', '::1/128')); + $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('::1', '::2')); + $this->assertTrue(Rhymix\Framework\Security\IpFilter::inRange('2400:cb00::1234', '2400:cb00::/32')); + $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('2405:8100::1234', '2400:cb00::/32')); + } + + public function testIPv4Wildcard() + { + $this->assertTrue(Rhymix\Framework\Security\IpFilter::inRange('192.168.134.241', '192.168.134.*')); + $this->assertTrue(Rhymix\Framework\Security\IpFilter::inRange('192.168.134.241', '192.168.*.*')); + $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('192.168.134.241', '192.168.136.*')); + $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('192.168.134.241', '192.172.*.*')); + } + + public function testIPv4Hyphen() + { + $this->assertTrue(Rhymix\Framework\Security\IpFilter::inRange('192.168.134.241', '192.168.134.0-192.168.134.255')); + $this->assertTrue(Rhymix\Framework\Security\IpFilter::inRange('192.168.134.241', '192.168.128.16-192.168.145.0')); + $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('192.168.134.241', '192.168.134.242-192.168.244.7')); + $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('192.168.134.241', '192.168.100.255-192.168.133.19')); + } + + public function testValidator() + { + $this->assertTrue(Rhymix\Framework\Security\IpFilter::validateRange('192.168.0.1')); + $this->assertTrue(Rhymix\Framework\Security\IpFilter::validateRange('192.168.0.0/16')); + $this->assertTrue(Rhymix\Framework\Security\IpFilter::validateRange('192.168.*.*')); + $this->assertTrue(Rhymix\Framework\Security\IpFilter::validateRange('192.168.*')); + $this->assertTrue(Rhymix\Framework\Security\IpFilter::validateRange('192.168.0.0-192.168.255.255')); + $this->assertTrue(Rhymix\Framework\Security\IpFilter::validateRange('2400:cb00::/32')); + $this->assertFalse(Rhymix\Framework\Security\IpFilter::validateRange('192.168.0.0~192.168.255.255')); + } + + public function testLegacy() + { + $this->assertTrue(\IpFilter::filter(array('192.168.134.241'), '192.168.134.241')); + $this->assertTrue(\IpFilter::filter(array('192.168.134.0-192.168.134.255'), '192.168.134.241')); + $this->assertTrue(\IpFilter::filter(array('127.0.0.1', '192.168.134.241'), '192.168.134.241')); + $this->assertTrue(\IpFilter::filter(array('192.168.134.*'), '192.168.134.241')); + $this->assertTrue(\IpFilter::filter(array('192.168.*'), '192.168.134.241')); + $this->assertFalse(\IpFilter::filter(array('127.0.0.1'), '192.168.134.241')); + } + + public function testCloudFlareRealIP() + { + $_SERVER['HTTP_CF_CONNECTING_IP'] = '192.168.134.241'; + + $_SERVER['REMOTE_ADDR'] = '192.168.10.1'; + $this->assertFalse(Rhymix\Framework\Security\IpFilter::getCloudFlareRealIP()); + $this->assertEquals('192.168.10.1', $_SERVER['REMOTE_ADDR']); + + $_SERVER['REMOTE_ADDR'] = '108.162.192.121'; + $this->assertEquals('192.168.134.241', Rhymix\Framework\Security\IpFilter::getCloudFlareRealIP()); + $this->assertEquals('192.168.134.241', $_SERVER['REMOTE_ADDR']); + + unset($_SERVER['HTTP_CF_CONNECTING_IP']); + $_SERVER['REMOTE_ADDR'] = '192.168.10.1'; + $this->assertFalse(Rhymix\Framework\Security\IpFilter::getCloudFlareRealIP()); + $this->assertEquals('192.168.10.1', $_SERVER['REMOTE_ADDR']); + } +} From 90dcc4a2e849eb84a04cf30f63d33184af7395e4 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 13 Mar 2016 20:40:23 +0900 Subject: [PATCH 071/205] Implement encryption, decryption, and CSPRNG in Security class --- classes/security/Password.class.php | 101 +----------- common/framework/security.php | 219 ++++++++++++++++++++++++++ common/libraries/cryptocompat.php | 4 +- tests/unit/framework/SecurityTest.php | 74 +++++++++ 4 files changed, 301 insertions(+), 97 deletions(-) diff --git a/classes/security/Password.class.php b/classes/security/Password.class.php index 93e87f0e1..fa39fd906 100644 --- a/classes/security/Password.class.php +++ b/classes/security/Password.class.php @@ -173,22 +173,22 @@ class Password $hash = explode(':', $hash); $hash[3] = base64_decode($hash[3]); $hash_to_compare = $this->pbkdf2($password, $hash[2], $hash[0], intval($hash[1], 10), strlen($hash[3])); - return $this->strcmpConstantTime($hash_to_compare, $hash[3]); + return Rhymix\Framework\Security::compareStrings($hash_to_compare, $hash[3]); case 'bcrypt': $hash_to_compare = $this->bcrypt($password, $hash); - return $this->strcmpConstantTime($hash_to_compare, $hash); + return Rhymix\Framework\Security::compareStrings($hash_to_compare, $hash); default: if($algorithm && isset(self::$_custom[$algorithm])) { $hash_callback = self::$_custom[$algorithm]['callback']; $hash_to_compare = $hash_callback($password, $hash); - return $this->strcmpConstantTime($hash_to_compare, $hash); + return Rhymix\Framework\Security::compareStrings($hash_to_compare, $hash); } if(in_array($algorithm, hash_algos())) { - return $this->strcmpConstantTime(hash($algorithm, $password), $hash); + return Rhymix\Framework\Security::compareStrings(hash($algorithm, $password), $hash); } return false; } @@ -280,90 +280,7 @@ class Password */ public function createSecureSalt($length, $format = 'hex') { - // Find out how many bytes of entropy we really need - switch($format) - { - case 'hex': - $entropy_required_bytes = ceil($length / 2); - break; - case 'alnum': - case 'printable': - $entropy_required_bytes = ceil($length * 3 / 4); - break; - default: - $entropy_required_bytes = $length; - } - - // Cap entropy to 256 bits from any one source, because anything more is meaningless - $entropy_capped_bytes = min(32, $entropy_required_bytes); - - // Find and use the most secure way to generate a random string - $entropy = false; - $is_windows = (defined('PHP_OS') && strtoupper(substr(PHP_OS, 0, 3)) === 'WIN'); - if(function_exists('random_bytes')) // PHP 7 - { - $entropy = random_bytes($entropy_capped_bytes); - } - elseif(function_exists('openssl_random_pseudo_bytes') && (!$is_windows || version_compare(PHP_VERSION, '5.4', '>='))) - { - $entropy = openssl_random_pseudo_bytes($entropy_capped_bytes); - } - elseif(function_exists('mcrypt_create_iv') && (!$is_windows || version_compare(PHP_VERSION, '5.3.7', '>='))) - { - $entropy = mcrypt_create_iv($entropy_capped_bytes, MCRYPT_DEV_URANDOM); - } - elseif(function_exists('mcrypt_create_iv') && $is_windows) - { - $entropy = mcrypt_create_iv($entropy_capped_bytes, MCRYPT_RAND); - } - elseif(!$is_windows && @is_readable('/dev/urandom')) - { - $fp = fopen('/dev/urandom', 'rb'); - if (function_exists('stream_set_read_buffer')) // This function does not exist in HHVM - { - stream_set_read_buffer($fp, 0); // Prevent reading several KB of unnecessary data from urandom - } - $entropy = fread($fp, $entropy_capped_bytes); - fclose($fp); - } - - // Use built-in source of entropy if an error occurs while using other functions - if($entropy === false || strlen($entropy) < $entropy_capped_bytes) - { - $entropy = ''; - for($i = 0; $i < $entropy_capped_bytes; $i += 2) - { - $entropy .= pack('S', rand(0, 65536) ^ mt_rand(0, 65535)); - } - } - - // Mixing (see RFC 4086 section 5) - $output = ''; - for($i = 0; $i < $entropy_required_bytes; $i += 32) - { - $output .= hash('sha256', $entropy . $i . rand(), true); - } - - // Encode and return the random string - switch($format) - { - case 'hex': - return substr(bin2hex($output), 0, $length); - case 'binary': - return substr($output, 0, $length); - case 'printable': - $salt = ''; - for($i = 0; $i < $length; $i++) - { - $salt .= chr(33 + (crc32(sha1($i . $output)) % 94)); - } - return $salt; - case 'alnum': - default: - $salt = substr(base64_encode($output), 0, $length); - $replacements = chr(rand(65, 90)) . chr(rand(97, 122)) . rand(0, 9); - return strtr($salt, '+/=', $replacements); - } + return Rhymix\Framework\Security::getRandom($length, $format); } /** @@ -446,13 +363,7 @@ class Password */ function strcmpConstantTime($a, $b) { - $diff = strlen($a) ^ strlen($b); - $maxlen = min(strlen($a), strlen($b)); - for($i = 0; $i < $maxlen; $i++) - { - $diff |= ord($a[$i]) ^ ord($b[$i]); - } - return $diff === 0; + return Rhymix\Framework\Security::compareStrings($a, $b); } } /* End of file : Password.class.php */ diff --git a/common/framework/security.php b/common/framework/security.php index 5521fca39..4bae27c39 100644 --- a/common/framework/security.php +++ b/common/framework/security.php @@ -43,6 +43,225 @@ class Security } } + /** + * Encrypt a string using AES. + * + * @param string $plaintext + * @param string $key (optional) + * @param bool $force_compat (optional) + * @return string|false + */ + public static function encrypt($plaintext, $key = null, $force_compat = false) + { + // Get the encryption key. + $key = $key ?: config('crypto.encryption_key'); + $key = substr(hash('sha256', $key, true), 0, 16); + + // Use defuse/php-encryption if possible. + if (!$force_compat && version_compare(\PHP_VERSION, '5.4.0', '>=') && function_exists('openssl_encrypt')) + { + try + { + return base64_encode(\Crypto::Encrypt($plaintext, $key)); + } + catch (\Exception $e) + { + return false; + } + } + + // Otherwise, use the CryptoCompat class. + return base64_encode(\CryptoCompat::encrypt($plaintext, $key)); + } + + /** + * Decrypt a string using AES. + * + * @param string $plaintext + * @param string $key (optional) + * @param bool $force_compat (optional) + * @return string|false + */ + public static function decrypt($ciphertext, $key = null, $force_compat = false) + { + // Get the encryption key. + $key = $key ?: config('crypto.encryption_key'); + $key = substr(hash('sha256', $key, true), 0, 16); + + // Check whether the ciphertext is valid. + $ciphertext = @base64_decode($ciphertext); + if (strlen($ciphertext) < 48) + { + return false; + } + + // Use defuse/php-encryption if possible. + if (!$force_compat && version_compare(\PHP_VERSION, '5.4.0', '>=') && function_exists('openssl_decrypt')) + { + try + { + return \Crypto::Decrypt($ciphertext, $key); + } + catch (\Exception $e) + { + return false; + } + } + + // Otherwise, use the CryptoCompat class. + return \CryptoCompat::decrypt($ciphertext, $key); + } + + /** + * Generate a cryptographically secure random string. + * + * @param int $length + * @param string $format + * @return string + */ + public function getRandom($length = 32, $format = 'alnum') + { + // Find out how many bytes of entropy we really need. + switch($format) + { + case 'binary': + $entropy_required_bytes = $length; + break; + case 'hex': + $entropy_required_bytes = ceil($length / 2); + break; + case 'alnum': + case 'printable': + default: + $entropy_required_bytes = ceil($length * 3 / 4); + break; + } + + // Cap entropy to 256 bits from any one source, because anything more is meaningless. + $entropy_capped_bytes = min(32, $entropy_required_bytes); + + // Find and use the most secure way to generate a random string. + $entropy = false; + $is_windows = (defined('\PHP_OS') && strtoupper(substr(\PHP_OS, 0, 3)) === 'WIN'); + if(function_exists('random_bytes')) // PHP 7 + { + $entropy = random_bytes($entropy_capped_bytes); + } + elseif(function_exists('openssl_random_pseudo_bytes') && (!$is_windows || version_compare(\PHP_VERSION, '5.4', '>='))) + { + $entropy = openssl_random_pseudo_bytes($entropy_capped_bytes); + } + elseif(function_exists('mcrypt_create_iv') && (!$is_windows || version_compare(\PHP_VERSION, '5.3.7', '>='))) + { + $entropy = mcrypt_create_iv($entropy_capped_bytes, \MCRYPT_DEV_URANDOM); + } + elseif(function_exists('mcrypt_create_iv') && $is_windows) + { + $entropy = mcrypt_create_iv($entropy_capped_bytes, \MCRYPT_RAND); + } + elseif(!$is_windows && @is_readable('/dev/urandom')) + { + $fp = fopen('/dev/urandom', 'rb'); + if (function_exists('stream_set_read_buffer')) // This function does not exist in HHVM. + { + stream_set_read_buffer($fp, 0); // Prevent reading several KB of unnecessary data from urandom. + } + $entropy = fread($fp, $entropy_capped_bytes); + fclose($fp); + } + + // Use built-in source of entropy if an error occurs while using other functions. + if($entropy === false || strlen($entropy) < $entropy_capped_bytes) + { + $entropy = ''; + for($i = 0; $i < $entropy_capped_bytes; $i += 2) + { + $entropy .= pack('S', rand(0, 65536) ^ mt_rand(0, 65535)); + } + } + + // Mixing (see RFC 4086 section 5) + $output = ''; + for($i = 0; $i < $entropy_required_bytes; $i += 32) + { + $output .= hash('sha256', $entropy . $i . rand(), true); + } + + // Encode and return the random string. + switch($format) + { + case 'binary': + return substr($output, 0, $length); + case 'printable': + $salt = ''; + for($i = 0; $i < $length; $i++) + { + $salt .= chr(33 + (crc32(sha1($i . $output)) % 94)); + } + return $salt; + case 'hex': + return substr(bin2hex($output), 0, $length); + case 'alnum': + default: + $salt = substr(base64_encode($output), 0, $length); + $replacements = chr(rand(65, 90)) . chr(rand(97, 122)) . rand(0, 9); + return strtr($salt, '+/=', $replacements); + } + } + + /** + * Generate a cryptographically secure random number between $min and $max. + * + * @param int $min + * @param int $max + * @return int + */ + public static function getRandomNumber($min = 0, $max = 0x7fffffff) + { + if (function_exists('random_int')) + { + return random_int($min, $max); + } + else + { + $bytes_required = min(4, ceil(log($max - $min, 2) / 8) + 1); + $bytes = self::getRandom($bytes_required, 'binary'); + $offset = abs(hexdec(bin2hex($bytes)) % ($max - $min + 1)); + return intval($min + $offset); + } + } + + /** + * Generate a random UUID. + * + * @return string + */ + public static function getRandomUUID() + { + $randpool = self::getRandom(16, 'binary'); + $randpool[6] = chr(ord($randpool[6]) & 0x0f | 0x40); + $randpool[8] = chr(ord($randpool[8]) & 0x3f | 0x80); + return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($randpool), 4)); + } + + /** + * Compare two strings in constant time. + * + * @param string $a + * @param string $b + * @return bool + */ + public static function compareStrings($a, $b) + { + $diff = strlen($a) ^ strlen($b); + $maxlen = min(strlen($a), strlen($b)); + for($i = 0; $i < $maxlen; $i++) + { + $diff |= ord($a[$i]) ^ ord($b[$i]); + } + return $diff === 0; + } + /** * Check if the current request seems to be a CSRF attack. * diff --git a/common/libraries/cryptocompat.php b/common/libraries/cryptocompat.php index 0bebc5f2a..0c2645004 100644 --- a/common/libraries/cryptocompat.php +++ b/common/libraries/cryptocompat.php @@ -49,7 +49,7 @@ class CryptoCompat // Validate MAC $mac_key = self::_defuseCompatibleHKDF($key, self::ENCRYPTION_MAC_INFO); $mac_compare = hash_hmac(self::ENCRYPTION_MAC_ALGO, ($iv . $ciphertext), $mac_key, true); - if (!Password::strcmpConstantTime($mac, $mac_compare)) + if (!Rhymix\Framework\Security::compareStrings($mac, $mac_compare)) { return false; } @@ -80,7 +80,7 @@ class CryptoCompat */ protected static function _createIV() { - return hex2bin(Password::createSecureSalt(self::ENCRYPTION_BLOCK_SIZE * 2, 'hex')); + return Rhymix\Framework\Security::getRandom(self::ENCRYPTION_BLOCK_SIZE, 'binary'); } /** diff --git a/tests/unit/framework/SecurityTest.php b/tests/unit/framework/SecurityTest.php index 91e1f3740..23d658a0f 100644 --- a/tests/unit/framework/SecurityTest.php +++ b/tests/unit/framework/SecurityTest.php @@ -17,6 +17,80 @@ class SecurityTest extends \Codeception\TestCase\Test $this->assertEquals('foo(bar).xls', Rhymix\Framework\Security::sanitize('foo.xls', 'filename')); } + public function testEncryption() + { + $plaintext = Rhymix\Framework\Security::getRandom(); + + // Encryption with default key. + $encrypted = Rhymix\Framework\Security::encrypt($plaintext); + $this->assertNotEquals(false, $encrypted); + $decrypted = Rhymix\Framework\Security::decrypt($encrypted); + $this->assertEquals($plaintext, $decrypted); + + // Encryption with custom key. + $key = Rhymix\Framework\Security::getRandom(); + $encrypted = Rhymix\Framework\Security::encrypt($plaintext, $key); + $this->assertNotEquals(false, $encrypted); + $decrypted = Rhymix\Framework\Security::decrypt($encrypted, $key); + $this->assertEquals($plaintext, $decrypted); + + // Encryption with defuse/php-encryption and decryption with CryptoCompat. + $encrypted = Rhymix\Framework\Security::encrypt($plaintext); + $this->assertNotEquals(false, $encrypted); + $decrypted = Rhymix\Framework\Security::decrypt($encrypted, null, true); + $this->assertEquals($plaintext, $decrypted); + + // Encryption with CryptoCompat and decryption with defuse/php-encryption. + $encrypted = Rhymix\Framework\Security::encrypt($plaintext, null, true); + $this->assertNotEquals(false, $encrypted); + $decrypted = Rhymix\Framework\Security::decrypt($encrypted); + $this->assertEquals($plaintext, $decrypted); + + // Test invalid ciphertext. + $decrypted = Rhymix\Framework\Security::decrypt('1234' . substr($encrypted, 4)); + $this->assertEquals(false, $decrypted); + $decrypted = Rhymix\Framework\Security::decrypt(substr($encrypted, strlen($encrypted) - 4) . 'abcd'); + $this->assertEquals(false, $decrypted); + $decrypted = Rhymix\Framework\Security::decrypt($plaintext); + $this->assertEquals(false, $decrypted); + } + + public function testGetRandom() + { + $this->assertEquals(1, preg_match('/^[0-9a-zA-Z]{32}$/', Rhymix\Framework\Security::getRandom())); + $this->assertEquals(1, preg_match('/^[0-9a-zA-Z]{256}$/', Rhymix\Framework\Security::getRandom(256))); + $this->assertEquals(1, preg_match('/^[0-9a-zA-Z]{16}$/', Rhymix\Framework\Security::getRandom(16, 'alnum'))); + $this->assertEquals(1, preg_match('/^[0-9a-f]{16}$/', Rhymix\Framework\Security::getRandom(16, 'hex'))); + $this->assertEquals(1, preg_match('/^[\x21-\x7e]{16}$/', Rhymix\Framework\Security::getRandom(16, 'printable'))); + } + + public function testGetRandomNumber() + { + for ($i = 0; $i < 10; $i++) + { + $min = mt_rand(0, 10000); + $max = $min + mt_rand(0, 10000); + $random = Rhymix\Framework\Security::getRandomNumber($min, $max); + $this->assertTrue($random >= $min && $random < $max); + } + } + + public function testGetRandomUUID() + { + $regex = '/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/'; + for ($i = 0; $i < 10; $i++) + { + $this->assertEquals(1, preg_match($regex, Rhymix\Framework\Security::getRandomUUID())); + } + } + + public function testCompareStrings() + { + $this->assertTrue(Rhymix\Framework\Security::compareStrings('foobar', 'foobar')); + $this->assertFalse(Rhymix\Framework\Security::compareStrings('foobar', 'foobar*')); + $this->assertFalse(Rhymix\Framework\Security::compareStrings('foo', 'bar')); + } + public function testCheckCSRF() { $_SERVER['REQUEST_METHOD'] = 'GET'; From 647bc7c112acadd20ffd82af73da428190e6180e Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 13 Mar 2016 22:08:56 +0900 Subject: [PATCH 072/205] Implement new Password class and related unit tests --- common/framework/password.php | 443 ++++++++++++++++++++++++++ common/libraries/vendorpass.php | 6 +- tests/unit/framework/PasswordTest.php | 127 ++++++++ tests/unit/framework/SecurityTest.php | 12 +- 4 files changed, 579 insertions(+), 9 deletions(-) create mode 100644 common/framework/password.php create mode 100644 tests/unit/framework/PasswordTest.php diff --git a/common/framework/password.php b/common/framework/password.php new file mode 100644 index 000000000..2e4e67017 --- /dev/null +++ b/common/framework/password.php @@ -0,0 +1,443 @@ + '/^\$2[a-z]\$[0-9]{2}\$/', + 'pbkdf2' => '/^[a-z0-9]+:[0-9]+:/', + 'md5' => '/^[0-9a-f]{32}$/', + 'md5,sha1,md5' => '/^[0-9a-f]{32}$/', + 'sha1' => '/^[0-9a-f]{40}$/', + 'sha256' => '/^[0-9a-f]{64}$/', + 'sha384' => '/^[0-9a-f]{96}$/', + 'sha512' => '/^[0-9a-f]{128}$/', + 'ripemd160' => '/^[0-9a-f]{40}$/', + 'whirlpool' => '/^[0-9a-f]{128}$/', + 'mssql_pwdencrypt' => '/^0x0100[0-9A-F]{48}$/', + 'mysql_old_password' => '/^[0-9a-f]{16}$/', + 'mysql_new_password' => '/^\*[0-9A-F]{40}$/', + 'portable' => '/^\$P\$/', + 'drupal' => '/^\$S\$/', + 'joomla' => '/^[0-9a-f]{32}:[0-9a-zA-Z\.\+\/\=]{32}$/', + 'kimsqrb' => '/\$[1-4]\$[0-9]{14}$/', + 'crypt' => '/^([0-9a-zA-Z\.\/]{13}$|_[0-9a-zA-Z\.\/]{19}$|\$[156]\$)/', + ); + + /** + * Add a custom algorithm. + * + * @param string $name + * @param string $signature + * @param callable $callback + * @return void + */ + public static function addAlgorithm($name, $signature, $callback) + { + self::$_algorithm_signatures[$name] = $signature; + self::$_algorithm_callbacks[$name] = $callback; + } + + /** + * Check if the given sequence of algorithms is valid. + * + * @param array|string $algos + * @return bool + */ + public static function isValidAlgorithm($algos) + { + $hash_algos = hash_algos(); + $algos = is_array($algos) ? $algos : explode(',', $algos); + foreach ($algos as $algo) + { + if (array_key_exists($algo, self::$_algorithm_signatures)) + { + continue; + } + if (in_array($algo, $hash_algos)) + { + continue; + } + return false; + } + return true; + } + + /** + * Get the list of hashing algorithms supported by this server. + * + * @return array + */ + public static function getSupportedAlgorithms() + { + $retval = array(); + if (version_compare(PHP_VERSION, '5.3.7', '>=') && defined('\CRYPT_BLOWFISH')) + { + $retval['bcrypt'] = 'bcrypt'; + } + if (function_exists('hash_hmac') && in_array('sha512', hash_algos())) + { + $retval['pbkdf2'] = 'pbkdf2'; + } + $retval['sha512'] = 'sha512'; + $retval['sha256'] = 'sha256'; + $retval['sha1'] = 'sha1'; + $retval['md5'] = 'md5'; + return $retval; + } + + /** + * Get the best hashing algorithm supported by this server. + * + * @return string + */ + public static function getBestSupportedAlgorithm() + { + $algos = self::getSupportedAlgorithms(); + return key($algos); + } + + /** + * Get the currently selected hashing algorithm. + * + * @return string + */ + public static function getSelectedAlgorithm() + { + if (function_exists('getModel')) + { + $config = getModel('member')->getMemberConfig(); + $algorithm = $config->password_hashing_algorithm; + if (strval($algorithm) === '') + { + $algorithm = 'md5'; + } + } + else + { + $algorithm = 'md5'; + } + return $algorithm; + } + + /** + * Get the currently configured work factor for bcrypt and other adjustable algorithms. + * + * @return int + */ + public static function getWorkFactor() + { + if (function_exists('getModel')) + { + $config = getModel('member')->getMemberConfig(); + $work_factor = $config->password_hashing_work_factor; + if (!$work_factor || $work_factor < 4 || $work_factor > 31) + { + $work_factor = 9; + } + } + else + { + $work_factor = 9; + } + + return $work_factor; + } + + /** + * Hash a password. + * + * To use multiple algorithms in series, provide them as an array. + * Salted algorithms such as bcrypt, pbkdf2, or portable must be used last. + * On error, false will be returned. + * + * @param string $password + * @param string|array $algos + * @param string $salt (optional) + * @return string|false + */ + public static function hashPassword($password, $algos, $salt = null) + { + // Initialize the chain of hashes. + $algos = array_map('strtolower', array_map('trim', is_array($algos) ? $algos : explode(',', $algos))); + $hashchain = preg_replace('/\\s+/', ' ', trim($password)); + + // Apply the given algorithms one by one. + foreach ($algos as $algo) + { + switch ($algo) + { + // bcrypt (must be used last) + case 'bcrypt': + $hashchain = self::bcrypt($hashchain, $salt, self::getWorkFactor()); + if ($hashchain[0] === '*') return false; + return $hashchain; + + // PBKDF2 (must be used last) + case 'pbkdf2': + if ($salt === null) + { + $salt = Security::getRandom(12, 'alnum'); + $hash_algorithm = 'sha512'; + $iterations = pow(2, self::getWorkFactor() + 5); + $key_length = 24; + } + else + { + $parts = explode(':', $salt); + $salt = $parts[2]; + $hash_algorithm = $parts[0]; + $iterations = $parts[1]; + $key_length = strlen(base64_decode($parts[3])); + } + return self::pbkdf2($hashchain, $salt, $hash_algorithm, $iterations, $key_length); + + // phpass portable algorithm (must be used last) + case 'portable': + $phpass = new \Hautelook\Phpass\PasswordHash(self::getWorkFactor(), true); + if ($salt === null) + { + $hashchain = $phpass->HashPassword($hashchain); + return $hashchain; + } + else + { + $match = $phpass->CheckPassword($hashchain, $salt); + return $match ? $salt : false; + } + + // Drupal's SHA-512 based algorithm (must be used last) + case 'drupal': + $hashchain = \VendorPass::drupal($password, $salt); + return $hashchain; + + // Joomla's MD5 based algorithm (must be used last) + case 'joomla': + $hashchain = \VendorPass::joomla($password, $salt); + return $hashchain; + + // KimsQ Rb algorithms (must be used last) + case 'kimsqrb': + $hashchain = \VendorPass::kimsqrb($password, $salt); + return $hashchain; + + // crypt() function (must be used last) + case 'crypt': + if ($salt === null) $salt = Security::getRandom(2, 'alnum'); + $hashchain = crypt($hashchain, $salt); + return $hashchain; + + // MS SQL's PWDENCRYPT() function (must be used last) + case 'mssql_pwdencrypt': + $hashchain = \VendorPass::mssql_pwdencrypt($hashchain, $salt); + return $hashchain; + + // MySQL's old PASSWORD() function. + case 'mysql_old_password': + $hashchain = \VendorPass::mysql_old_password($hashchain); + break; + + // MySQL's new PASSWORD() function. + case 'mysql_new_password': + $hashchain = \VendorPass::mysql_new_password($hashchain); + break; + + // A dummy algorithm that does nothing. + case 'null': + break; + + // All other algorithms will be passed to hash() or treated as a function name. + default: + if (isset(self::$_algorithm_callbacks[$algo])) + { + $callback = self::$_algorithm_callbacks[$algo]; + $hashchain = $callback($hashchain, $salt); + } + elseif (in_array($algo, hash_algos())) + { + $hashchain = hash($algo, $hashchain); + } + elseif (function_exists($algo)) + { + $hashchain = $algo($hashchain, $salt); + } + else + { + return false; + } + } + } + + return $hashchain; + } + + /** + * Check a password against a hash. + * + * This method returns true if the password is correct, and false otherwise. + * If the algorithm is not specified, it will be guessed from the format of the hash. + * + * @param string $password + * @param string $hash + * @param array|string $algos + * @return bool + */ + public static function checkPassword($password, $hash, $algos = null) + { + if (!$algos) + { + $algos = self::checkAlgorithm($hash); + } + if (!is_array($algos)) + { + $algos = explode(',', $algos); + } + + return Security::compareStrings($hash, self::hashPassword($password, $algos, $hash)); + } + + /** + * Guess which algorithm(s) were used to generate the given hash. + * + * If there are multiple possibilities, all of them will be returned in an array. + * + * @param string $hash + * @return array + */ + public static function checkAlgorithm($hash) + { + $candidates = array(); + foreach (self::$_algorithm_signatures as $name => $signature) + { + if (preg_match($signature, $hash)) $candidates[] = $name; + } + return $candidates; + } + + /** + * Check the work factor of a hash. + * + * @param string $hash + * @return int + */ + public static function checkWorkFactor($hash) + { + if(preg_match('/^\$2[axy]\$([0-9]{2})\$/', $hash, $matches)) + { + return intval($matches[1], 10); + } + elseif(preg_match('/^sha[0-9]+:([0-9]+):/', $hash, $matches)) + { + return max(0, round(log($matches[1], 2)) - 5); + } + else + { + return 0; + } + } + + /** + * Generate the bcrypt hash of a string. + * + * @param string $password + * @param string $salt (optional) + * @param int $work_factor (optional) + * @return string + */ + public static function bcrypt($password, $salt = null, $work_factor = 10) + { + if ($salt === null) + { + $salt = '$2y$' . sprintf('%02d', $work_factor) . '$' . Security::getRandom(22, 'alnum'); + } + + return crypt($password, $salt); + } + + /** + * Generate the PBKDF2 hash of a string. + * + * @param string $password + * @param string $salt (optional) + * @param string $algorithm (optional) + * @param int $iterations (optional) + * @param int $length (optional) + * @return string + */ + public static function pbkdf2($password, $salt = null, $algorithm = 'sha512', $iterations = 16384, $length = 24) + { + if ($salt === null) + { + $salt = Security::getRandom(12, 'alnum'); + } + + if (function_exists('hash_pbkdf2')) + { + $hash = hash_pbkdf2($algorithm, $password, $salt, $iterations, $length, true); + } + else + { + $output = ''; + $block_count = ceil($length / strlen(hash($algorithm, '', true))); // key length divided by the length of one hash + for ($i = 1; $i <= $block_count; $i++) + { + $last = $salt . pack('N', $i); // $i encoded as 4 bytes, big endian + $last = $xorsum = hash_hmac($algorithm, $last, $password, true); // first iteration + for ($j = 1; $j < $iterations; $j++) // The other $count - 1 iterations + { + $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true)); + } + $output .= $xorsum; + } + $hash = substr($output, 0, $length); + } + + return $algorithm . ':' . sprintf('%07d', $iterations) . ':' . $salt . ':' . base64_encode($hash); + } + + /** + * Count the amount of entropy that a password contains. + * + * @param string $password + * @return int + */ + public static function countEntropyBits($password) + { + // An empty string has no entropy. + + if ($password === '') return 0; + + // Common character sets and the number of possible mutations. + + static $entropy_per_char = array( + '/^[0-9]+$/' => 10, + '/^[a-z]+$/' => 26, + '/^[A-Z]+$/' => 26, + '/^[a-z0-9]+$/' => 36, + '/^[A-Z0-9]+$/' => 36, + '/^[a-zA-Z]+$/' => 52, + '/^[a-zA-Z0-9]+$/' => 62, + '/^[a-zA-Z0-9_-]+$/' => 64, + '/^[\\x20-\\x7e]+$/' => 95, + '/^[\\x00-\\x7f]+$/' => 128, + ); + + foreach ($entropy_per_char as $regex => $entropy) + { + if (preg_match($regex, $password)) + { + return log(pow($entropy, strlen($password)), 2); + } + } + + return strlen($password) * 8; + } +} diff --git a/common/libraries/vendorpass.php b/common/libraries/vendorpass.php index 46e5e73c3..c31bfe30b 100644 --- a/common/libraries/vendorpass.php +++ b/common/libraries/vendorpass.php @@ -61,7 +61,7 @@ class VendorPass else { $iterations = 15; - $salt = Password::createSecureSalt(8, 'hex'); + $salt = Rhymix\Framework\Security::getRandom(8, 'hex'); } $count = 1 << $iterations; $hash = hash('sha512', $salt . $password, true); @@ -104,7 +104,7 @@ class VendorPass } else { - $salt = Password::createSecureSalt(32, 'hex'); + $salt = Rhymix\Framework\Security::getRandom(32, 'hex'); } return md5($password . $salt) . ':' . $salt; } @@ -137,7 +137,7 @@ class VendorPass { if (!isset($options['salt']) || !preg_match('/^[0-9a-zA-Z\.\/]{22,}$/', $options['salt'])) { - $options['salt'] = Password::createSecureSalt(22, 'alnum'); + $options['salt'] = Rhymix\Framework\Security::getRandom(22, 'alnum'); } if (!isset($options['cost']) || $options['cost'] < 4 || $options['cost'] > 31) { diff --git a/tests/unit/framework/PasswordTest.php b/tests/unit/framework/PasswordTest.php new file mode 100644 index 000000000..1bf859bbf --- /dev/null +++ b/tests/unit/framework/PasswordTest.php @@ -0,0 +1,127 @@ +assertTrue(Rhymix\Framework\Password::isValidAlgorithm('bcrypt')); + $this->assertTrue(Rhymix\Framework\Password::isValidAlgorithm('whirlpool,pbkdf2')); + $this->assertTrue(Rhymix\Framework\Password::isValidAlgorithm(array('md5', 'sha1', 'md5'))); + + $this->assertFalse(Rhymix\Framework\Password::isValidAlgorithm('bunga_bunga')); + Rhymix\Framework\Password::addAlgorithm('bunga_bunga', '/bunga_bunga/', function($hash) { return 'bunga_bunga'; }); + $this->assertTrue(Rhymix\Framework\Password::isValidAlgorithm('bunga_bunga')); + } + + public function testGetSupportedAlgorithms() + { + $algos = Rhymix\Framework\Password::getSupportedAlgorithms(); + $this->assertTrue(in_array('bcrypt', $algos)); + $this->assertTrue(in_array('pbkdf2', $algos)); + $this->assertTrue(in_array('md5', $algos)); + } + + public function testGetBestSupportedAlgorithm() + { + $algo = Rhymix\Framework\Password::getBestSupportedAlgorithm(); + $this->assertTrue($algo === 'bcrypt' || $algo === 'pbkdf2'); + } + + public function testGetSelectedAlgorithm() + { + $algo = Rhymix\Framework\Password::getSelectedAlgorithm(); + $this->assertTrue($algo === 'bcrypt' || $algo === 'pbkdf2' || $algo === 'md5'); + } + + public function testGetWorkFactor() + { + $work_factor = $algo = Rhymix\Framework\Password::getWorkFactor(); + $this->assertTrue($work_factor >= 4); + $this->assertTrue($work_factor <= 31); + } + + public function testHashPassword() + { + $password = Rhymix\Framework\Security::getRandom(32); + $this->assertEquals(md5($password), Rhymix\Framework\Password::hashPassword($password, 'md5')); + $this->assertEquals(md5(sha1(md5($password))), Rhymix\Framework\Password::hashPassword($password, 'md5,sha1,md5')); + $this->assertEquals(hash('whirlpool', $password), Rhymix\Framework\Password::hashPassword($password, 'whirlpool')); + $this->assertEquals('5d2e19393cc5ef67', Rhymix\Framework\Password::hashPassword('password', 'mysql_old_password')); + } + + public function testCheckPassword() + { + $password = Rhymix\Framework\Security::getRandom(32); + + $algos = array('whirlpool', 'ripemd160', 'bcrypt'); + $hash = Rhymix\Framework\Password::hashPassword($password, $algos); + $this->assertRegExp('/^\$2y\$/', $hash); + $this->assertEquals(60, strlen($hash)); + $this->assertTrue(Rhymix\Framework\Password::checkPassword($password, $hash, $algos)); + + $algos = array('pbkdf2'); + $hash = Rhymix\Framework\Password::hashPassword($password, $algos); + $this->assertRegExp('/^(sha256|sha512):[0-9]+:/', $hash); + $this->assertEquals(60, strlen($hash)); + $this->assertTrue(Rhymix\Framework\Password::checkPassword($password, $hash, $algos)); + + foreach (array('drupal', 'joomla', 'kimsqrb', 'mysql_old_password', 'mysql_new_password', 'mssql_pwdencrypt') as $algo) + { + $hash = Rhymix\Framework\Password::hashPassword($password, $algo); + $this->assertTrue(Rhymix\Framework\Password::checkPassword($password, $hash, $algo)); + $this->assertFalse(Rhymix\Framework\Password::checkPassword($password, $hash . 'x', $algo)); + } + } + + public function testCheckAlgorithm() + { + $password = Rhymix\Framework\Security::getRandom(32, 'hex'); + + $this->assertEquals(array('md5', 'md5,sha1,md5'), Rhymix\Framework\Password::checkAlgorithm($password)); + $this->assertEquals(array('sha512', 'whirlpool'), Rhymix\Framework\Password::checkAlgorithm(hash('sha512', $password))); + + $hash = '$2y$10$VkxBdEBTZ1HyLluZPjXCjuFffw0a6alZlbb733CF/zA22HDpBNsMm'; + $this->assertEquals(array('bcrypt'), Rhymix\Framework\Password::checkAlgorithm($hash)); + + $hash = 'sha512:0008192:hoXcLXQzIiIJ:ElokybdRf+i512M4/4PIdEiSDgZ8f0uL'; + $this->assertEquals(array('pbkdf2'), Rhymix\Framework\Password::checkAlgorithm($hash)); + } + + public function testCheckWorkFactor() + { + $hash = '$2y$10$VkxBdEBTZ1HyLluZPjXCjuFffw0a6alZlbb733CF/zA22HDpBNsMm'; + $this->assertEquals(10, Rhymix\Framework\Password::checkWorkFactor($hash)); + + $hash = 'sha512:0008192:hoXcLXQzIiIJ:ElokybdRf+i512M4/4PIdEiSDgZ8f0uL'; + $this->assertEquals(8, Rhymix\Framework\Password::checkWorkFactor($hash)); + + $hash = '5f4dcc3b5aa765d61d8327deb882cf99'; + $this->assertEquals(0, Rhymix\Framework\Password::checkWorkFactor($hash)); + } + + public function testBcrypt() + { + $password = 'password'; + $hash = '$2y$10$VkxBdEBTZ1HyLluZPjXCjuFffw0a6alZlbb733CF/zA22HDpBNsMm'; + $this->assertEquals($hash, Rhymix\Framework\Password::bcrypt($password, $hash)); + } + + public function testPBKDF2() + { + $password = 'password'; + $salt = 'rtmIxdEUoWUk'; + $hash = 'sha512:0016384:rtmIxdEUoWUk:1hrwGP3ScWvxslnqNFqyhM6Ddn4iYrwf'; + $this->assertEquals($hash, Rhymix\Framework\Password::pbkdf2($password, $salt, 'sha512', 16384, 24)); + } + + public function testCountEntropyBits() + { + $this->assertEquals(0, Rhymix\Framework\Password::countEntropyBits('')); + $this->assertEquals(13, round(Rhymix\Framework\Password::countEntropyBits('1234'))); + $this->assertEquals(20, round(Rhymix\Framework\Password::countEntropyBits('123456'))); + $this->assertEquals(28, round(Rhymix\Framework\Password::countEntropyBits('rhymix'))); + $this->assertEquals(52, round(Rhymix\Framework\Password::countEntropyBits('rhymix1234'))); + $this->assertEquals(60, round(Rhymix\Framework\Password::countEntropyBits('RhymiX1234'))); + $this->assertEquals(125, round(Rhymix\Framework\Password::countEntropyBits('Rhymix_is*the%Best!'))); + } +} diff --git a/tests/unit/framework/SecurityTest.php b/tests/unit/framework/SecurityTest.php index 23d658a0f..8c600e2c6 100644 --- a/tests/unit/framework/SecurityTest.php +++ b/tests/unit/framework/SecurityTest.php @@ -57,11 +57,11 @@ class SecurityTest extends \Codeception\TestCase\Test public function testGetRandom() { - $this->assertEquals(1, preg_match('/^[0-9a-zA-Z]{32}$/', Rhymix\Framework\Security::getRandom())); - $this->assertEquals(1, preg_match('/^[0-9a-zA-Z]{256}$/', Rhymix\Framework\Security::getRandom(256))); - $this->assertEquals(1, preg_match('/^[0-9a-zA-Z]{16}$/', Rhymix\Framework\Security::getRandom(16, 'alnum'))); - $this->assertEquals(1, preg_match('/^[0-9a-f]{16}$/', Rhymix\Framework\Security::getRandom(16, 'hex'))); - $this->assertEquals(1, preg_match('/^[\x21-\x7e]{16}$/', Rhymix\Framework\Security::getRandom(16, 'printable'))); + $this->assertRegExp('/^[0-9a-zA-Z]{32}$/', Rhymix\Framework\Security::getRandom()); + $this->assertRegExp('/^[0-9a-zA-Z]{256}$/', Rhymix\Framework\Security::getRandom(256)); + $this->assertRegExp('/^[0-9a-zA-Z]{16}$/', Rhymix\Framework\Security::getRandom(16, 'alnum')); + $this->assertRegExp('/^[0-9a-f]{16}$/', Rhymix\Framework\Security::getRandom(16, 'hex')); + $this->assertRegExp('/^[\x21-\x7e]{16}$/', Rhymix\Framework\Security::getRandom(16, 'printable')); } public function testGetRandomNumber() @@ -80,7 +80,7 @@ class SecurityTest extends \Codeception\TestCase\Test $regex = '/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/'; for ($i = 0; $i < 10; $i++) { - $this->assertEquals(1, preg_match($regex, Rhymix\Framework\Security::getRandomUUID())); + $this->assertRegExp($regex, Rhymix\Framework\Security::getRandomUUID()); } } From ddab5ce8d833169d76fd309194372253f2f576ef Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 13 Mar 2016 22:11:30 +0900 Subject: [PATCH 073/205] Fix unit tests for Security::getRandomNumber() --- tests/unit/framework/SecurityTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/framework/SecurityTest.php b/tests/unit/framework/SecurityTest.php index 8c600e2c6..b28d99ada 100644 --- a/tests/unit/framework/SecurityTest.php +++ b/tests/unit/framework/SecurityTest.php @@ -71,7 +71,7 @@ class SecurityTest extends \Codeception\TestCase\Test $min = mt_rand(0, 10000); $max = $min + mt_rand(0, 10000); $random = Rhymix\Framework\Security::getRandomNumber($min, $max); - $this->assertTrue($random >= $min && $random < $max); + $this->assertTrue($random >= $min && $random <= $max); } } From 642f048f649b2eb88925930978d4b71bbad26ef8 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 13 Mar 2016 23:01:49 +0900 Subject: [PATCH 074/205] Improve encoding and decoding of widget and editor component attributes --- common/framework/security/htmlfilter.php | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/common/framework/security/htmlfilter.php b/common/framework/security/htmlfilter.php index fce40cf71..9b241dcfe 100644 --- a/common/framework/security/htmlfilter.php +++ b/common/framework/security/htmlfilter.php @@ -442,10 +442,11 @@ class HTMLFilter protected static function _encodeWidgetsAndEditorComponents($content) { return preg_replace_callback('!<(div|img)([^>]*)(editor_component="[^"]+"|class="zbxe_widget_output")([^>]*)>!i', function($match) { + $tag = strtolower($match[1]); $attrs = array(); - $html = preg_replace_callback('!([a-zA-Z0-9_-]+)="([^"]+)"!', function($attr) use(&$attrs) { + $html = preg_replace_callback('!([a-zA-Z0-9_-]+)="([^"]+)"!', function($attr) use($tag, &$attrs) { $attrkey = strtolower($attr[1]); - if (strtolower($match[1]) === 'img' && preg_match('/^(?:width|height|alt)$/', $attrkey)) + if ($tag === 'img' && preg_match('/^(?:width|height|alt)$/', $attrkey)) { return $attr[0]; } @@ -456,11 +457,11 @@ class HTMLFilter $attrs[$attrkey] = htmlspecialchars_decode($attr[2]); return ''; }, $match[0]); - if (strtolower($match[1]) === 'img' && !preg_match('/\ssrc="/', $html)) + if ($tag === 'img' && !preg_match('/\ssrc="/', $html)) { $html = substr($html, 0, 4) . ' src=""' . substr($html, 4); } - $encoded_properties = base64_encode(json_encode($attrs)); + $encoded_properties = \Rhymix\Framework\Security::encrypt(json_encode($attrs)); return substr($html, 0, 4) . ' rx_encoded_properties="' . $encoded_properties . '"' . substr($html, 4); }, $content); } @@ -473,14 +474,23 @@ class HTMLFilter */ protected static function _decodeWidgetsAndEditorComponents($content) { - return preg_replace_callback('!<(div|img)([^>]*)(rx_encoded_properties="([^"]+)")!i', function($match) { + return preg_replace_callback('!<(div|img)([^>]*)(\srx_encoded_properties="([^"]+)")!i', function($match) { $attrs = array(); - $decoded_properties = @json_decode(base64_decode($match[4])) ?: array(); + $decoded_properties = \Rhymix\Framework\Security::decrypt($match[4]); + if (!$decoded_properties) + { + return str_replace($match[3], '', $match[0]); + } + $decoded_properties = json_decode($decoded_properties); + if (!$decoded_properties) + { + return str_replace($match[3], '', $match[0]); + } foreach ($decoded_properties as $key => $val) { $attrs[] = $key . '="' . htmlspecialchars($val) . '"'; } - return str_replace($match[3], implode(' ', $attrs), $match[0]); + return str_replace($match[3], ' ' . implode(' ', $attrs), $match[0]); }, $content); } } From f4dc7e6b214e1fa7a31bb99bbd5b13961acfff38 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 13 Mar 2016 23:23:48 +0900 Subject: [PATCH 075/205] Move all functionality of old Password class to new Password class --- classes/security/Password.class.php | 320 ++------------------------ common/framework/password.php | 55 ++++- tests/unit/framework/PasswordTest.php | 14 +- 3 files changed, 73 insertions(+), 316 deletions(-) diff --git a/classes/security/Password.class.php b/classes/security/Password.class.php index fa39fd906..c0a1da502 100644 --- a/classes/security/Password.class.php +++ b/classes/security/Password.class.php @@ -1,366 +1,76 @@ */ -/** - * This class can be used to hash passwords using various algorithms and check their validity. - * It is fully compatible with previous defaults, while also supporting bcrypt and pbkdf2. - * - * @file Password.class.php - * @author Kijin Sung (kijin@kijinsung.com) - * @package /classes/security - * @version 1.1 - */ class Password { - /** - * @brief Custom algorithms are stored here - * @var array - */ - protected static $_custom = array(); - - /** - * @brief Register a custom algorithm for password checking - * @param string $name The name of the algorithm - * @param string $regexp The regular expression to detect the algorithm - * @param callable $callback The function to call to regenerate the hash - * @return void - */ public static function registerCustomAlgorithm($name, $regexp, $callback) { - self::$_custom[$name] = array('regexp' => $regexp, 'callback' => $callback); + Rhymix\Framework\Password::addAlgorithm($name, $regexp, $callback); } - /** - * @brief Return the list of hashing algorithms supported by this server - * @return array - */ public function getSupportedAlgorithms() { - $retval = array(); - if(function_exists('hash_hmac') && in_array('sha256', hash_algos())) - { - $retval['pbkdf2'] = 'pbkdf2'; - } - if(version_compare(PHP_VERSION, '5.3.7', '>=') && defined('CRYPT_BLOWFISH')) - { - $retval['bcrypt'] = 'bcrypt'; - } - $retval['md5'] = 'md5'; - return $retval; + return Rhymix\Framework\Password::getSupportedAlgorithms(); } - /** - * @brief Return the best hashing algorithm supported by this server - * @return string - */ public function getBestAlgorithm() { - $algos = $this->getSupportedAlgorithms(); - return key($algos); + return Rhymix\Framework\Password::getBestSupportedAlgorithm(); } - /** - * @brief Return the currently selected hashing algorithm - * @return string - */ public function getCurrentlySelectedAlgorithm() { - if(function_exists('getModel')) - { - $config = getModel('member')->getMemberConfig(); - $algorithm = $config->password_hashing_algorithm; - if(strval($algorithm) === '') - { - $algorithm = 'md5'; // Historical default for XE - } - } - else - { - $algorithm = 'md5'; - } - return $algorithm; + return Rhymix\Framework\Password::getDefaultAlgorithm(); } - /** - * @brief Return the currently configured work factor for bcrypt and other adjustable algorithms - * @return int - */ public function getWorkFactor() { - if(function_exists('getModel')) - { - $config = getModel('member')->getMemberConfig(); - $work_factor = $config->password_hashing_work_factor; - if(!$work_factor || $work_factor < 4 || $work_factor > 31) - { - $work_factor = 8; // Reasonable default - } - } - else - { - $work_factor = 8; - } - return $work_factor; + return Rhymix\Framework\Password::getWorkFactor(); } - /** - * @brief Create a hash using the specified algorithm - * @param string $password The password - * @param string $algorithm The algorithm (optional) - * @return string - */ public function createHash($password, $algorithm = null) { - if($algorithm === null) - { - $algorithm = $this->getCurrentlySelectedAlgorithm(); - } - if(!array_key_exists($algorithm, $this->getSupportedAlgorithms())) - { - return false; - } - - $password = trim($password); - - switch($algorithm) - { - case 'md5': - return md5($password); - - case 'pbkdf2': - $iterations = pow(2, $this->getWorkFactor() + 5); - $salt = $this->createSecureSalt(12, 'alnum'); - $hash = base64_encode($this->pbkdf2($password, $salt, 'sha256', $iterations, 24)); - return 'sha256:'.sprintf('%07d', $iterations).':'.$salt.':'.$hash; - - case 'bcrypt': - return $this->bcrypt($password); - - default: - return false; - } + return Rhymix\Framework\Password::hashPassword($password, $algorithm); } - /** - * @brief Check if a password matches a hash - * @param string $password The password - * @param string $hash The hash - * @param string $algorithm The algorithm (optional) - * @return bool - */ public function checkPassword($password, $hash, $algorithm = null) { - if($algorithm === null) - { - $algorithm = $this->checkAlgorithm($hash); - } - - $password = trim($password); - - switch($algorithm) - { - case 'md5': - return md5($password) === $hash || md5(sha1(md5($password))) === $hash; - - case 'mysql_old_password': - return (class_exists('Context') && substr(Context::getDBType(), 0, 5) === 'mysql') ? - DB::getInstance()->isValidOldPassword($password, $hash) : false; - - case 'mysql_password': - return $hash[0] === '*' && substr($hash, 1) === strtoupper(sha1(sha1($password, true))); - - case 'pbkdf2': - $hash = explode(':', $hash); - $hash[3] = base64_decode($hash[3]); - $hash_to_compare = $this->pbkdf2($password, $hash[2], $hash[0], intval($hash[1], 10), strlen($hash[3])); - return Rhymix\Framework\Security::compareStrings($hash_to_compare, $hash[3]); - - case 'bcrypt': - $hash_to_compare = $this->bcrypt($password, $hash); - return Rhymix\Framework\Security::compareStrings($hash_to_compare, $hash); - - default: - if($algorithm && isset(self::$_custom[$algorithm])) - { - $hash_callback = self::$_custom[$algorithm]['callback']; - $hash_to_compare = $hash_callback($password, $hash); - return Rhymix\Framework\Security::compareStrings($hash_to_compare, $hash); - } - if(in_array($algorithm, hash_algos())) - { - return Rhymix\Framework\Security::compareStrings(hash($algorithm, $password), $hash); - } - return false; - } + return Rhymix\Framework\Password::checkPassword($password, $hash, $algorithm); } - /** - * @brief Check the algorithm used to create a hash - * @param string $hash The hash - * @return string - */ function checkAlgorithm($hash) { - foreach(self::$_custom as $name => $definition) - { - if(preg_match($definition['regexp'], $hash)) - { - return $name; - } - } - - if(preg_match('/^\$2[axy]\$([0-9]{2})\$/', $hash, $matches)) - { - return 'bcrypt'; - } - elseif(preg_match('/^sha[0-9]+:([0-9]+):/', $hash, $matches)) - { - return 'pbkdf2'; - } - elseif(strlen($hash) === 32 && ctype_xdigit($hash)) - { - return 'md5'; - } - elseif(strlen($hash) === 40 && ctype_xdigit($hash)) - { - return 'sha1'; - } - elseif(strlen($hash) === 64 && ctype_xdigit($hash)) - { - return 'sha256'; - } - elseif(strlen($hash) === 96 && ctype_xdigit($hash)) - { - return 'sha384'; - } - elseif(strlen($hash) === 128 && ctype_xdigit($hash)) - { - return 'sha512'; - } - elseif(strlen($hash) === 16 && ctype_xdigit($hash)) - { - return 'mysql_old_password'; - } - elseif(strlen($hash) === 41 && $hash[0] === '*') - { - return 'mysql_password'; - } - else - { - return false; - } + $algos = Rhymix\Framework\Password::checkAlgorithm($hash); + return count($algos) ? $algos[0] : false; } - /** - * @brief Check the work factor of a hash - * @param string $hash The hash - * @return int - */ function checkWorkFactor($hash) { - if(preg_match('/^\$2[axy]\$([0-9]{2})\$/', $hash, $matches)) - { - return intval($matches[1], 10); - } - elseif(preg_match('/^sha[0-9]+:([0-9]+):/', $hash, $matches)) - { - return max(0, round(log($matches[1], 2)) - 5); - } - else - { - return false; - } + return Rhymix\Framework\Password::checkWorkFactor($hash); } - /** - * @brief Generate a cryptographically secure random string to use as a salt - * @param int $length The number of bytes to return - * @param string $format hex or alnum - * @return string - */ public function createSecureSalt($length, $format = 'hex') { return Rhymix\Framework\Security::getRandom($length, $format); } - /** - * @brief Generate a temporary password using the secure salt generator - * @param int $length The number of bytes to return - * @return string - */ public function createTemporaryPassword($length = 16) { - while(true) - { - $source = base64_encode($this->createSecureSalt(64, 'binary')); - $source = strtr($source, 'iIoOjl10/', '@#$%&*-!?'); - $source_length = strlen($source); - for($i = 0; $i < $source_length - $length; $i++) - { - $candidate = substr($source, $i, $length); - if(preg_match('/[a-z]/', $candidate) && preg_match('/[A-Z]/', $candidate) && - preg_match('/[0-9]/', $candidate) && preg_match('/[^a-zA-Z0-9]/', $candidate)) - { - return $candidate; - } - } - } + return Rhymix\Framework\Password::getRandomPassword($length); } - /** - * @brief Generate the PBKDF2 hash of a string using a salt - * @param string $password The password - * @param string $salt The salt - * @param string $algorithm The algorithm (optional, default is sha256) - * @param int $iterations Iteration count (optional, default is 8192) - * @param int $length The length of the hash (optional, default is 32) - * @return string - */ public function pbkdf2($password, $salt, $algorithm = 'sha256', $iterations = 8192, $length = 24) { - if(function_exists('hash_pbkdf2')) - { - return hash_pbkdf2($algorithm, $password, $salt, $iterations, $length, true); - } - else - { - $output = ''; - $block_count = ceil($length / strlen(hash($algorithm, '', true))); // key length divided by the length of one hash - for($i = 1; $i <= $block_count; $i++) - { - $last = $salt . pack('N', $i); // $i encoded as 4 bytes, big endian - $last = $xorsum = hash_hmac($algorithm, $last, $password, true); // first iteration - for($j = 1; $j < $iterations; $j++) // The other $count - 1 iterations - { - $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true)); - } - $output .= $xorsum; - } - return substr($output, 0, $length); - } + $hash = Rhymix\Framework\Security::pbkdf2($password, $salt, $algorithm, $iterations, $length); + $hash = explode(':', $hash); + return base64_decode($hash[3]); } - /** - * @brief Generate the bcrypt hash of a string using a salt - * @param string $password The password - * @param string $salt The salt (optional, auto-generated if empty) - * @return string - */ public function bcrypt($password, $salt = null) { - if($salt === null) - { - $salt = '$2y$'.sprintf('%02d', $this->getWorkFactor()).'$'.$this->createSecureSalt(22, 'alnum'); - } - return crypt($password, $salt); + return Rhymix\Framework\Security::bcrypt($password, $salt); } - /** - * @brief Compare two strings in constant time - * @param string $a The first string - * @param string $b The second string - * @return bool - */ function strcmpConstantTime($a, $b) { return Rhymix\Framework\Security::compareStrings($a, $b); diff --git a/common/framework/password.php b/common/framework/password.php index 2e4e67017..bac5e284f 100644 --- a/common/framework/password.php +++ b/common/framework/password.php @@ -106,11 +106,11 @@ class Password } /** - * Get the currently selected hashing algorithm. + * Get the current default hashing algorithm. * * @return string */ - public static function getSelectedAlgorithm() + public static function getDefaultAlgorithm() { if (function_exists('getModel')) { @@ -152,6 +152,31 @@ class Password return $work_factor; } + /** + * Generate a reasonably strong random password. + * + * @param int $length + * @return string + */ + public static function getRandomPassword($length = 16) + { + while(true) + { + $source = base64_encode(Security::getRandom(64, 'binary')); + $source = strtr($source, 'iIoOjl10/', '@#$%&*-!?'); + $source_length = strlen($source); + for($i = 0; $i < $source_length - $length; $i++) + { + $candidate = substr($source, $i, $length); + if(preg_match('/[a-z]/', $candidate) && preg_match('/[A-Z]/', $candidate) && + preg_match('/[0-9]/', $candidate) && preg_match('/[^a-zA-Z0-9]/', $candidate)) + { + return $candidate; + } + } + } + } + /** * Hash a password. * @@ -160,12 +185,18 @@ class Password * On error, false will be returned. * * @param string $password - * @param string|array $algos + * @param string|array $algos (optional) * @param string $salt (optional) * @return string|false */ - public static function hashPassword($password, $algos, $salt = null) + public static function hashPassword($password, $algos = null, $salt = null) { + // If the algorithm is null, use the default algorithm. + if ($algos === null) + { + $algos = self::getDefaultAlgorithm(); + } + // Initialize the chain of hashes. $algos = array_map('strtolower', array_map('trim', is_array($algos) ? $algos : explode(',', $algos))); $hashchain = preg_replace('/\\s+/', ' ', trim($password)); @@ -292,16 +323,22 @@ class Password */ public static function checkPassword($password, $hash, $algos = null) { - if (!$algos) + if ($algos === null) { $algos = self::checkAlgorithm($hash); + foreach ($algos as $algo) + { + if (Security::compareStrings($hash, self::hashPassword($password, $algo, $hash))) + { + return true; + } + } + return false; } - if (!is_array($algos)) + else { - $algos = explode(',', $algos); + return Security::compareStrings($hash, self::hashPassword($password, $algos, $hash)); } - - return Security::compareStrings($hash, self::hashPassword($password, $algos, $hash)); } /** diff --git a/tests/unit/framework/PasswordTest.php b/tests/unit/framework/PasswordTest.php index 1bf859bbf..610092653 100644 --- a/tests/unit/framework/PasswordTest.php +++ b/tests/unit/framework/PasswordTest.php @@ -27,9 +27,9 @@ class PasswordTest extends \Codeception\TestCase\Test $this->assertTrue($algo === 'bcrypt' || $algo === 'pbkdf2'); } - public function testGetSelectedAlgorithm() + public function testGetDefaultAlgorithm() { - $algo = Rhymix\Framework\Password::getSelectedAlgorithm(); + $algo = Rhymix\Framework\Password::getDefaultAlgorithm(); $this->assertTrue($algo === 'bcrypt' || $algo === 'pbkdf2' || $algo === 'md5'); } @@ -40,6 +40,16 @@ class PasswordTest extends \Codeception\TestCase\Test $this->assertTrue($work_factor <= 31); } + public function testGetRandomPassword() + { + $password = Rhymix\Framework\Password::getRandomPassword(16); + $this->assertEquals(16, strlen($password)); + $this->assertRegexp('/[a-z]/', $password); + $this->assertRegexp('/[A-Z]/', $password); + $this->assertRegexp('/[0-9]/', $password); + $this->assertRegexp('/[^a-zA-Z0-9]/', $password); + } + public function testHashPassword() { $password = Rhymix\Framework\Security::getRandom(32); From 9d6284faad40f2b3dcbadeff3d08f68e88864d2f Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 13 Mar 2016 23:39:31 +0900 Subject: [PATCH 076/205] Update all references to old Password class --- common/framework/compat/configparser.php | 6 +-- modules/file/file.controller.php | 24 ++++------- modules/install/install.view.php | 2 +- modules/member/member.admin.controller.php | 3 +- modules/member/member.admin.view.php | 3 +- modules/member/member.class.php | 3 +- modules/member/member.controller.php | 20 +++------ modules/member/member.model.php | 50 ++++++++++++++-------- 8 files changed, 53 insertions(+), 58 deletions(-) diff --git a/common/framework/compat/configparser.php b/common/framework/compat/configparser.php index c88300dd1..44a0a7b59 100644 --- a/common/framework/compat/configparser.php +++ b/common/framework/compat/configparser.php @@ -151,9 +151,9 @@ class ConfigParser } // Create new crypto keys. - $config['crypto']['encryption_key'] = \Password::createSecureSalt(64, 'alnum'); - $config['crypto']['authentication_key'] = \Password::createSecureSalt(64, 'alnum'); - $config['crypto']['session_key'] = \Password::createSecureSalt(64, 'alnum'); + $config['crypto']['encryption_key'] = \Rhymix\Framework\Security::getRandom(64, 'alnum'); + $config['crypto']['authentication_key'] = \Rhymix\Framework\Security::getRandom(64, 'alnum'); + $config['crypto']['session_key'] = \Rhymix\Framework\Security::getRandom(64, 'alnum'); // Convert language configuration. if (isset($db_info->lang_type)) diff --git a/modules/file/file.controller.php b/modules/file/file.controller.php index 65a4720a1..12d618456 100644 --- a/modules/file/file.controller.php +++ b/modules/file/file.controller.php @@ -285,8 +285,7 @@ class fileController extends file // Redirect to procFileOutput using file key if(!isset($_SESSION['__XE_FILE_KEY__']) || !is_string($_SESSION['__XE_FILE_KEY__']) || strlen($_SESSION['__XE_FILE_KEY__']) != 32) { - $random = new Password(); - $_SESSION['__XE_FILE_KEY__'] = $random->createSecureSalt(32, 'hex'); + $_SESSION['__XE_FILE_KEY__'] = Rhymix\Framework\Security::getRandom(32, 'hex'); } $file_key_data = $file_obj->file_srl . $file_obj->file_size . $file_obj->uploaded_filename . $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']; $file_key = substr(hash_hmac('sha256', $file_key_data, $_SESSION['__XE_FILE_KEY__']), 0, 32); @@ -735,9 +734,6 @@ class fileController extends file // Sanitize filename $file_info['name'] = Rhymix\Framework\Security\FilenameFilter::clean($file_info['name']); - // Get random number generator - $random = new Password(); - // Set upload path by checking if the attachement is an image or other kinds of file if(preg_match("/\.(jpe?g|gif|png|wm[va]|mpe?g|avi|swf|flv|mp[1-4]|as[fx]|wav|midi?|moo?v|qt|r[am]{1,2}|m4v)$/i", $file_info['name'])) { @@ -747,7 +743,7 @@ class fileController extends file // change to random file name. because window php bug. window php is not recognize unicode character file name - by cherryfilter $ext = substr(strrchr($file_info['name'],'.'),1); //$_filename = preg_replace('/[#$&*?+%"\']/', '_', $file_info['name']); - $_filename = $random->createSecureSalt(32, 'hex').'.'.$ext; + $_filename = Rhymix\Framework\Security::getRandom(32, 'hex') . '.' . $ext; $filename = $path.$_filename; $idx = 1; while(file_exists($filename)) @@ -760,15 +756,12 @@ class fileController extends file else { $path = sprintf("./files/attach/binaries/%s/%s", $module_srl, getNumberingPath($upload_target_srl,3)); - $filename = $path.$random->createSecureSalt(32, 'hex'); + $filename = $path . Rhymix\Framework\Security::getRandom(32, 'hex'); $direct_download = 'N'; } // Create a directory if(!FileHandler::makeDir($path)) return new Object(-1,'msg_not_permitted_create'); - - // Get random number generator - $random = new Password(); // Move the file if($manual_insert) @@ -776,7 +769,7 @@ class fileController extends file @copy($file_info['tmp_name'], $filename); if(!file_exists($filename)) { - $filename = $path.$random->createSecureSalt(32, 'hex').'.'.$ext; + $filename = $path . Rhymix\Framework\Security::getRandom(32, 'hex') . '.' . $ext; @copy($file_info['tmp_name'], $filename); } } @@ -784,7 +777,7 @@ class fileController extends file { if(!@move_uploaded_file($file_info['tmp_name'], $filename)) { - $filename = $path.$random->createSecureSalt(32, 'hex').'.'.$ext; + $filename = $path . Rhymix\Framework\Security::getRandom(32, 'hex') . '.' . $ext; if(!@move_uploaded_file($file_info['tmp_name'], $filename)) return new Object(-1,'msg_file_upload_error'); } } @@ -803,7 +796,7 @@ class fileController extends file $args->file_size = @filesize($filename); $args->comment = NULL; $args->member_srl = $member_srl; - $args->sid = $random->createSecureSalt(32, 'hex'); + $args->sid = Rhymix\Framework\Security::getRandom(32, 'hex'); $output = executeQuery('file.insertFile', $args); if(!$output->toBool()) return $output; @@ -978,13 +971,12 @@ class fileController extends file if(preg_match("/\.(jpg|jpeg|gif|png|wmv|wma|mpg|mpeg|avi|swf|flv|mp1|mp2|mp3|mp4|asf|wav|asx|mid|midi|asf|mov|moov|qt|rm|ram|ra|rmm|m4v)$/i", $file_info->source_filename)) { $path = sprintf("./files/attach/images/%s/%s/", $target_module_srl,$target_srl); - $new_file = $path.$file_info->source_filename; + $new_file = $path . $file_info->source_filename; } else { $path = sprintf("./files/attach/binaries/%s/%s/", $target_module_srl, $target_srl); - $random = new Password(); - $new_file = $path.$random->createSecureSalt(32, 'hex'); + $new_file = $path . Rhymix\Framework\Security::getRandom(32, 'hex'); } // Pass if a target document to move is same if($old_file == $new_file) continue; diff --git a/modules/install/install.view.php b/modules/install/install.view.php index 0d8c0498f..27f504ea7 100644 --- a/modules/install/install.view.php +++ b/modules/install/install.view.php @@ -74,7 +74,7 @@ class installView extends install function dispInstallCheckEnv() { // Create a temporary file for mod_rewrite check. - self::$rewriteCheckString = Password::createSecureSalt(32); + self::$rewriteCheckString = Rhymix\Framework\Security::getRandom(32); FileHandler::writeFile(_XE_PATH_ . self::$rewriteCheckFilePath, self::$rewriteCheckString);; // Check if the web server is nginx. diff --git a/modules/member/member.admin.controller.php b/modules/member/member.admin.controller.php index 918838a72..f18ec15c8 100644 --- a/modules/member/member.admin.controller.php +++ b/modules/member/member.admin.controller.php @@ -166,8 +166,7 @@ class memberAdminController extends member 'update_nickname_log' ); - $oPassword = new Password(); - if(!array_key_exists($args->password_hashing_algorithm, $oPassword->getSupportedAlgorithms())) + if(!array_key_exists($args->password_hashing_algorithm, Rhymix\Framework\Password::getSupportedAlgorithms())) { $args->password_hashing_algorithm = 'md5'; } diff --git a/modules/member/member.admin.view.php b/modules/member/member.admin.view.php index 93619b3c4..a2626716b 100644 --- a/modules/member/member.admin.view.php +++ b/modules/member/member.admin.view.php @@ -129,8 +129,7 @@ class memberAdminView extends member */ public function dispMemberAdminConfig() { - $oPassword = new Password(); - Context::set('password_hashing_algos', $oPassword->getSupportedAlgorithms()); + Context::set('password_hashing_algos', Rhymix\Framework\Password::getSupportedAlgorithms()); $this->setTemplateFile('default_config'); } diff --git a/modules/member/member.class.php b/modules/member/member.class.php index b2a63f159..5922c11f7 100644 --- a/modules/member/member.class.php +++ b/modules/member/member.class.php @@ -73,8 +73,7 @@ class member extends ModuleObject { if(!$config->password_hashing_algorithm) { - $oPassword = new Password(); - $config->password_hashing_algorithm = $oPassword->getBestAlgorithm(); + $config->password_hashing_algorithm = Rhymix\Framework\Password::getBestSupportedAlgorithm(); } if(!$config->password_hashing_work_factor) { diff --git a/modules/member/member.controller.php b/modules/member/member.controller.php index a085d1c4e..6f5ea5af0 100644 --- a/modules/member/member.controller.php +++ b/modules/member/member.controller.php @@ -984,12 +984,11 @@ class memberController extends member } // Insert data into the authentication DB - $oPassword = new Password(); $args = new stdClass(); $args->user_id = $member_info->user_id; $args->member_srl = $member_info->member_srl; - $args->new_password = $oPassword->createTemporaryPassword(8); - $args->auth_key = $oPassword->createSecureSalt(40); + $args->new_password = Rhymix\Framework\Password::getRandomPassword(8); + $args->auth_key = Rhymix\Framework\Security::getRandom(40, 'hex'); $args->is_register = 'N'; $output = executeQuery('member.insertAuthMail', $args); @@ -1093,8 +1092,7 @@ class memberController extends member } // Update to a temporary password and set change_password_date to 1 - $oPassword = new Password(); - $temp_password = $oPassword->createTemporaryPassword(8); + $temp_password = Rhymix\Framework\Password::getRandomPassword(8); $args = new stdClass(); $args->member_srl = $member_srl; @@ -1323,12 +1321,11 @@ class memberController extends member $this->_clearMemberCache($args->member_srl); // generate new auth key - $oPassword = new Password(); $auth_args = new stdClass(); $auth_args->user_id = $memberInfo->user_id; $auth_args->member_srl = $memberInfo->member_srl; $auth_args->new_password = $memberInfo->password; - $auth_args->auth_key = $oPassword->createSecureSalt(40); + $auth_args->auth_key = Rhymix\Framework\Security::getRandom(40, 'hex'); $auth_args->is_register = 'Y'; $output = executeQuery('member.insertAuthMail', $auth_args); @@ -1813,8 +1810,7 @@ class memberController extends member if($keep_signed) { // Key generate for auto login - $oPassword = new Password(); - $random_key = $oPassword->createSecureSalt(32, 'hex'); + $random_key = Rhymix\Framework\Security::getRandom(32, 'hex'); $extra_key = strtolower($user_id).$this->memberInfo->password.$_SERVER['HTTP_USER_AGENT']; $extra_key = substr(hash_hmac('sha256', $extra_key, $random_key), 0, 32); $autologin_args = new stdClass; @@ -2091,12 +2087,11 @@ class memberController extends member if($args->denied == 'Y') { // Insert data into the authentication DB - $oPassword = new Password(); $auth_args = new stdClass(); $auth_args->user_id = $args->user_id; $auth_args->member_srl = $args->member_srl; $auth_args->new_password = $args->password; - $auth_args->auth_key = $oPassword->createSecureSalt(40); + $auth_args->auth_key = Rhymix\Framework\Security::getRandom(40, 'hex'); $auth_args->is_register = 'Y'; $output = executeQuery('member.insertAuthMail', $auth_args); @@ -2552,11 +2547,10 @@ class memberController extends member } unset($_SESSION['rechecked_password_step']); - $oPassword = new Password(); $auth_args = new stdClass(); $auth_args->user_id = $newEmail; $auth_args->member_srl = $member_info->member_srl; - $auth_args->auth_key = $oPassword->createSecureSalt(40); + $auth_args->auth_key = Rhymix\Framework\Security::getRandom(40, 'hex'); $auth_args->new_password = 'XE_change_emaill_address'; $oDB = &DB::getInstance(); diff --git a/modules/member/member.model.php b/modules/member/member.model.php index 073b7ab5b..01750966f 100644 --- a/modules/member/member.model.php +++ b/modules/member/member.model.php @@ -1107,10 +1107,19 @@ class memberModel extends member } // Check the password - $oPassword = new Password(); - $current_algorithm = $oPassword->checkAlgorithm($hashed_password); - $match = $oPassword->checkPassword($password_text, $hashed_password, $current_algorithm); - if(!$match) + $password_match = false; + $current_algorithm = false; + $possible_algorithms = Rhymix\Framework\Password::checkAlgorithm($hashed_password); + foreach ($possible_algorithms as $algorithm) + { + if (Rhymix\Framework\Password::checkPassword($password_text, $hashed_password, $algorithm)) + { + $password_match = true; + $current_algorithm = $algorithm; + break; + } + } + if (!$password_match) { return false; } @@ -1119,22 +1128,26 @@ class memberModel extends member $config = $this->getMemberConfig(); if($member_srl > 0 && $config->password_hashing_auto_upgrade != 'N') { - $need_upgrade = false; - - if(!$need_upgrade) + $required_algorithm = Rhymix\Framework\Password::getDefaultAlgorithm(); + if ($required_algorithm !== $current_algorithm) { - $required_algorithm = $oPassword->getCurrentlySelectedAlgorithm(); - if($required_algorithm !== $current_algorithm) $need_upgrade = true; + $need_upgrade = true; + } + else + { + $required_work_factor = Rhymix\Framework\Password::getWorkFactor(); + $current_work_factor = Rhymix\Framework\Password::checkWorkFactor($hashed_password); + if ($current_work_factor !== false && $required_work_factor > $current_work_factor) + { + $need_upgrade = true; + } + else + { + $need_upgrade = false; + } } - if(!$need_upgrade) - { - $required_work_factor = $oPassword->getWorkFactor(); - $current_work_factor = $oPassword->checkWorkFactor($hashed_password); - if($current_work_factor !== false && $required_work_factor > $current_work_factor) $need_upgrade = true; - } - - if($need_upgrade === true) + if ($need_upgrade) { $args = new stdClass(); $args->member_srl = $member_srl; @@ -1155,8 +1168,7 @@ class memberModel extends member */ function hashPassword($password_text, $algorithm = null) { - $oPassword = new Password(); - return $oPassword->createHash($password_text, $algorithm); + return Rhymix\Framework\Password::hashPassword($password_text, $algorithm); } function checkPasswordStrength($password, $strength) From 69c5147888ea82c99eab318f72b13a49bc5ed11c Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 13 Mar 2016 23:52:26 +0900 Subject: [PATCH 077/205] Add phpass and update composer libraries --- composer.json | 3 +- composer.lock | 67 +++- vendor/composer/autoload_classmap.php | 4 + vendor/composer/autoload_namespaces.php | 1 + vendor/composer/installed.json | 163 +++++---- vendor/hautelook/phpass/.gitignore | 2 + vendor/hautelook/phpass/.travis.yml | 13 + vendor/hautelook/phpass/README.md | 46 +++ vendor/hautelook/phpass/Tests/BasicTest.php | 52 +++ vendor/hautelook/phpass/Tests/bootstrap.php | 14 + vendor/hautelook/phpass/Tests/phpunit.xml | 32 ++ vendor/hautelook/phpass/composer.json | 22 ++ vendor/hautelook/phpass/lib/Makefile | 21 ++ vendor/hautelook/phpass/lib/crypt_private.c | 106 ++++++ .../src/Hautelook/Phpass/PasswordHash.php | 314 ++++++++++++++++++ vendor/matthiasmullie/minify/.coveralls.yml | 3 - vendor/matthiasmullie/minify/CONTRIBUTING.md | 59 ++++ vendor/matthiasmullie/minify/composer.json | 3 +- vendor/matthiasmullie/minify/src/CSS.php | 78 +++-- .../matthiasmullie/minify/src/Exception.php | 4 +- .../minify/src/Exceptions/BasicException.php | 12 + .../src/Exceptions/FileImportException.php | 10 + .../minify/src/Exceptions/IOException.php | 10 + vendor/matthiasmullie/minify/src/JS.php | 22 +- vendor/matthiasmullie/minify/src/Minify.php | 139 +++++--- 25 files changed, 1039 insertions(+), 161 deletions(-) create mode 100644 vendor/hautelook/phpass/.gitignore create mode 100644 vendor/hautelook/phpass/.travis.yml create mode 100644 vendor/hautelook/phpass/README.md create mode 100644 vendor/hautelook/phpass/Tests/BasicTest.php create mode 100644 vendor/hautelook/phpass/Tests/bootstrap.php create mode 100644 vendor/hautelook/phpass/Tests/phpunit.xml create mode 100644 vendor/hautelook/phpass/composer.json create mode 100644 vendor/hautelook/phpass/lib/Makefile create mode 100644 vendor/hautelook/phpass/lib/crypt_private.c create mode 100644 vendor/hautelook/phpass/src/Hautelook/Phpass/PasswordHash.php delete mode 100644 vendor/matthiasmullie/minify/.coveralls.yml create mode 100644 vendor/matthiasmullie/minify/CONTRIBUTING.md create mode 100644 vendor/matthiasmullie/minify/src/Exceptions/BasicException.php create mode 100644 vendor/matthiasmullie/minify/src/Exceptions/FileImportException.php create mode 100644 vendor/matthiasmullie/minify/src/Exceptions/IOException.php diff --git a/composer.json b/composer.json index be2c76685..5da1f2385 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ { "name": "NAVER", "email": "developers@xpressengine.com" } ], "require": { - "php": ">=5.3.0", + "php": ">=5.3.3", "ext-curl": "*", "ext-gd": "*", "ext-iconv": "*", @@ -22,6 +22,7 @@ "defuse/php-encryption": "1.2.1", "ezyang/htmlpurifier": "4.7.*", "firephp/firephp-core": "0.4.0", + "hautelook/phpass": "0.3.*", "matthiasmullie/minify": "1.3.*", "michelf/php-markdown": "1.5.*", "rmccue/requests": "1.6.*", diff --git a/composer.lock b/composer.lock index 043cbfd87..05bcf4331 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "a35061b94658a2dbdafe943dddcdc898", - "content-hash": "1a71937e1cbc600f8a237c6445d6a69f", + "hash": "9b062f27815a9b2ef1e700b4664ee864", + "content-hash": "69345a9112733f99c09fa298ba72fa22", "packages": [ { "name": "defuse/php-encryption", @@ -133,17 +133,61 @@ "time": "2013-04-23 15:28:20" }, { - "name": "matthiasmullie/minify", - "version": "1.3.32", + "name": "hautelook/phpass", + "version": "0.3.4", "source": { "type": "git", - "url": "https://github.com/matthiasmullie/minify.git", - "reference": "140c714688908afcecde87338c8309233bdc2519" + "url": "https://github.com/hautelook/phpass.git", + "reference": "f0217d804225822f9bdb0d392839029b0fcb0914" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/140c714688908afcecde87338c8309233bdc2519", - "reference": "140c714688908afcecde87338c8309233bdc2519", + "url": "https://api.github.com/repos/hautelook/phpass/zipball/f0217d804225822f9bdb0d392839029b0fcb0914", + "reference": "f0217d804225822f9bdb0d392839029b0fcb0914", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "psr-0": { + "Hautelook": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Public Domain" + ], + "authors": [ + { + "name": "Solar Designer", + "email": "solar@openwall.com", + "homepage": "http://openwall.com/phpass/" + } + ], + "description": "Portable PHP password hashing framework", + "homepage": "http://github.com/hautelook/phpass/", + "keywords": [ + "blowfish", + "crypt", + "password", + "security" + ], + "time": "2012-08-31 00:00:00" + }, + { + "name": "matthiasmullie/minify", + "version": "1.3.34", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/minify.git", + "reference": "272e46113404f66ced256659552a0cc074a7810f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/272e46113404f66ced256659552a0cc074a7810f", + "reference": "272e46113404f66ced256659552a0cc074a7810f", "shasum": "" }, "require": { @@ -153,8 +197,7 @@ }, "require-dev": { "matthiasmullie/scrapbook": "~1.0", - "phpunit/phpunit": "~4.8", - "satooshi/php-coveralls": "~1.0" + "phpunit/phpunit": "~4.8" }, "bin": [ "bin/minifycss", @@ -187,7 +230,7 @@ "minifier", "minify" ], - "time": "2016-01-11 02:10:11" + "time": "2016-03-01 08:00:27" }, { "name": "matthiasmullie/path-converter", @@ -489,7 +532,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.3.0", + "php": ">=5.3.3", "ext-curl": "*", "ext-gd": "*", "ext-iconv": "*", diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 9a99b797a..b85d24e5d 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -237,8 +237,12 @@ return array( 'HTMLPurifier_VarParser_Flexible' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Flexible.php', 'HTMLPurifier_VarParser_Native' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/VarParser/Native.php', 'HTMLPurifier_Zipper' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/Zipper.php', + 'Hautelook\\Phpass\\PasswordHash' => $vendorDir . '/hautelook/phpass/src/Hautelook/Phpass/PasswordHash.php', 'MatthiasMullie\\Minify\\CSS' => $vendorDir . '/matthiasmullie/minify/src/CSS.php', 'MatthiasMullie\\Minify\\Exception' => $vendorDir . '/matthiasmullie/minify/src/Exception.php', + 'MatthiasMullie\\Minify\\Exceptions\\BasicException' => $vendorDir . '/matthiasmullie/minify/src/Exceptions/BasicException.php', + 'MatthiasMullie\\Minify\\Exceptions\\FileImportException' => $vendorDir . '/matthiasmullie/minify/src/Exceptions/FileImportException.php', + 'MatthiasMullie\\Minify\\Exceptions\\IOException' => $vendorDir . '/matthiasmullie/minify/src/Exceptions/IOException.php', 'MatthiasMullie\\Minify\\JS' => $vendorDir . '/matthiasmullie/minify/src/JS.php', 'MatthiasMullie\\Minify\\Minify' => $vendorDir . '/matthiasmullie/minify/src/Minify.php', 'MatthiasMullie\\PathConverter\\Converter' => $vendorDir . '/matthiasmullie/path-converter/src/Converter.php', diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php index 0ba8d72dc..942fa8072 100644 --- a/vendor/composer/autoload_namespaces.php +++ b/vendor/composer/autoload_namespaces.php @@ -9,5 +9,6 @@ return array( 'Sunra\\PhpSimple\\HtmlDomParser' => array($vendorDir . '/sunra/php-simple-html-dom-parser/Src'), 'Requests' => array($vendorDir . '/rmccue/requests/library'), 'Michelf' => array($vendorDir . '/michelf/php-markdown'), + 'Hautelook' => array($vendorDir . '/hautelook/phpass/src'), 'HTMLPurifier' => array($vendorDir . '/ezyang/htmlpurifier/library'), ); diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index f37668d21..18e6cbbfb 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -293,65 +293,6 @@ "relative" ] }, - { - "name": "matthiasmullie/minify", - "version": "1.3.32", - "version_normalized": "1.3.32.0", - "source": { - "type": "git", - "url": "https://github.com/matthiasmullie/minify.git", - "reference": "140c714688908afcecde87338c8309233bdc2519" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/140c714688908afcecde87338c8309233bdc2519", - "reference": "140c714688908afcecde87338c8309233bdc2519", - "shasum": "" - }, - "require": { - "ext-pcre": "*", - "matthiasmullie/path-converter": "~1.0", - "php": ">=5.3.0" - }, - "require-dev": { - "matthiasmullie/scrapbook": "~1.0", - "phpunit/phpunit": "~4.8", - "satooshi/php-coveralls": "~1.0" - }, - "time": "2016-01-11 02:10:11", - "bin": [ - "bin/minifycss", - "bin/minifyjs" - ], - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "MatthiasMullie\\Minify\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Matthias Mullie", - "email": "minify@mullie.eu", - "homepage": "http://www.mullie.eu", - "role": "Developer" - } - ], - "description": "CSS & JS minifier", - "homepage": "http://www.minifier.org", - "keywords": [ - "JS", - "css", - "javascript", - "minifier", - "minify" - ] - }, { "name": "swiftmailer/swiftmailer", "version": "v5.4.1", @@ -492,5 +433,109 @@ ], "description": "Traditional FirePHPCore library for sending PHP variables to the browser.", "homepage": "https://github.com/firephp/firephp-core" + }, + { + "name": "matthiasmullie/minify", + "version": "1.3.34", + "version_normalized": "1.3.34.0", + "source": { + "type": "git", + "url": "https://github.com/matthiasmullie/minify.git", + "reference": "272e46113404f66ced256659552a0cc074a7810f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/272e46113404f66ced256659552a0cc074a7810f", + "reference": "272e46113404f66ced256659552a0cc074a7810f", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "matthiasmullie/path-converter": "~1.0", + "php": ">=5.3.0" + }, + "require-dev": { + "matthiasmullie/scrapbook": "~1.0", + "phpunit/phpunit": "~4.8" + }, + "time": "2016-03-01 08:00:27", + "bin": [ + "bin/minifycss", + "bin/minifyjs" + ], + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "MatthiasMullie\\Minify\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matthias Mullie", + "email": "minify@mullie.eu", + "homepage": "http://www.mullie.eu", + "role": "Developer" + } + ], + "description": "CSS & JS minifier", + "homepage": "http://www.minifier.org", + "keywords": [ + "JS", + "css", + "javascript", + "minifier", + "minify" + ] + }, + { + "name": "hautelook/phpass", + "version": "0.3.4", + "version_normalized": "0.3.4.0", + "source": { + "type": "git", + "url": "https://github.com/hautelook/phpass.git", + "reference": "f0217d804225822f9bdb0d392839029b0fcb0914" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hautelook/phpass/zipball/f0217d804225822f9bdb0d392839029b0fcb0914", + "reference": "f0217d804225822f9bdb0d392839029b0fcb0914", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "time": "2012-08-31 00:00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Hautelook": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Public Domain" + ], + "authors": [ + { + "name": "Solar Designer", + "email": "solar@openwall.com", + "homepage": "http://openwall.com/phpass/" + } + ], + "description": "Portable PHP password hashing framework", + "homepage": "http://github.com/hautelook/phpass/", + "keywords": [ + "blowfish", + "crypt", + "password", + "security" + ] } ] diff --git a/vendor/hautelook/phpass/.gitignore b/vendor/hautelook/phpass/.gitignore new file mode 100644 index 000000000..987e2a253 --- /dev/null +++ b/vendor/hautelook/phpass/.gitignore @@ -0,0 +1,2 @@ +composer.lock +vendor diff --git a/vendor/hautelook/phpass/.travis.yml b/vendor/hautelook/phpass/.travis.yml new file mode 100644 index 000000000..c8002896d --- /dev/null +++ b/vendor/hautelook/phpass/.travis.yml @@ -0,0 +1,13 @@ +language: php + +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 + - hhvm + +before_script: + - composer install + +script: cd Tests && phpunit --configuration phpunit.xml --coverage-text diff --git a/vendor/hautelook/phpass/README.md b/vendor/hautelook/phpass/README.md new file mode 100644 index 000000000..5e1310d41 --- /dev/null +++ b/vendor/hautelook/phpass/README.md @@ -0,0 +1,46 @@ +Openwall Phpass, modernized +=========================== + +[![Build Status](https://secure.travis-ci.org/hautelook/phpass.png?branch=master)](http://travis-ci.org/hautelook/phpass) +[![HHVM Status](http://hhvm.h4cc.de/badge/hautelook/phpass.png)](http://hhvm.h4cc.de/package/hautelook/phpass) + +This is Openwall's [Phpass](http://openwall.com/phpass/), based on the 0.3 release, but modernized slightly: + +- Namespaced +- Composer support (Autoloading) +- PHP 5 style +- Unit Tested + +The changes are minimal and only stylistic. The source code is in the public domain. We claim no ownership, but needed it for one of our projects, and wanted to make it available to other people as well. + +## Installation ## + +Add this requirement to your `composer.json` file and run `composer.phar install`: + + { + "require": { + "hautelook/phpass": "dev-master" + } + } + +## Usage ## + +The following example shows how to hash a password (to then store the hash in the database), and how to check whether a provided password is correct (hashes to the same value): + +``` php +HashPassword('secret'); +var_dump($password); + +$passwordMatch = $passwordHasher->CheckPassword('secret', "$2a$08$0RK6Yw6j9kSIXrrEOc3dwuDPQuT78HgR0S3/ghOFDEpOGpOkARoSu"); +var_dump($passwordMatch); + diff --git a/vendor/hautelook/phpass/Tests/BasicTest.php b/vendor/hautelook/phpass/Tests/BasicTest.php new file mode 100644 index 000000000..bc57036b1 --- /dev/null +++ b/vendor/hautelook/phpass/Tests/BasicTest.php @@ -0,0 +1,52 @@ +HashPassword($correct); + + $this->assertTrue($hasher->CheckPassword($correct, $hash)); + } + + public function testIncorrectHash() + { + $hasher = new PasswordHash(8,false); + $correct = 'test12345'; + $hash = $hasher->HashPassword($correct); + $wrong = 'test12346'; + + $this->assertFalse($hasher->CheckPassword($wrong, $hash)); + } + + public function testWeakHashes() + { + $hasher = new PasswordHash(8, true); + $correct = 'test12345'; + $hash = $hasher->HashPassword($correct); + $wrong = 'test12346'; + + $this->assertTrue($hasher->CheckPassword($correct, $hash)); + $this->assertFalse($hasher->CheckPassword($wrong, $hash)); + } + + public function testPortableHashes() + { + $hasher = new PasswordHash(8, true); + $correct = 'test12345'; + $wrong = 'test12346'; + + $this->assertTrue($hasher->CheckPassword($correct, self::PORTABLE_HASH)); + $this->assertFalse($hasher->CheckPassword($wrong, self::PORTABLE_HASH)); + } +} \ No newline at end of file diff --git a/vendor/hautelook/phpass/Tests/bootstrap.php b/vendor/hautelook/phpass/Tests/bootstrap.php new file mode 100644 index 000000000..6bf33ed1a --- /dev/null +++ b/vendor/hautelook/phpass/Tests/bootstrap.php @@ -0,0 +1,14 @@ + + + + + + + + + + + + . + + + + ../ + + + ../src/Hautelook + + ../ + ./bootstrap.php + + + + \ No newline at end of file diff --git a/vendor/hautelook/phpass/composer.json b/vendor/hautelook/phpass/composer.json new file mode 100644 index 000000000..b37cfc9c1 --- /dev/null +++ b/vendor/hautelook/phpass/composer.json @@ -0,0 +1,22 @@ +{ + "name": "hautelook/phpass", + "type": "library", + "time" : "2012-08-31", + "license" : "Public Domain", + "description": "Portable PHP password hashing framework", + "keywords": ["Blowfish", "crypt", "password", "security"], + "homepage": "http://github.com/hautelook/phpass/", + "authors": [ + { + "name" : "Solar Designer", + "email": "solar@openwall.com", + "homepage": "http://openwall.com/phpass/" + } + ], + "require": { + "php": ">=5.3.3" + }, + "autoload": { + "psr-0": {"Hautelook": "src/"} + } +} diff --git a/vendor/hautelook/phpass/lib/Makefile b/vendor/hautelook/phpass/lib/Makefile new file mode 100644 index 000000000..fe48917f7 --- /dev/null +++ b/vendor/hautelook/phpass/lib/Makefile @@ -0,0 +1,21 @@ +# +# Written by Solar Designer and placed in the public domain. +# See crypt_private.c for more information. +# +CC = gcc +LD = $(CC) +RM = rm -f +CFLAGS = -Wall -O2 -fomit-frame-pointer -funroll-loops +LDFLAGS = -s +LIBS = -lcrypto + +all: crypt_private-test + +crypt_private-test: crypt_private-test.o + $(LD) $(LDFLAGS) $(LIBS) crypt_private-test.o -o $@ + +crypt_private-test.o: crypt_private.c + $(CC) -c $(CFLAGS) crypt_private.c -DTEST -o $@ + +clean: + $(RM) crypt_private-test* diff --git a/vendor/hautelook/phpass/lib/crypt_private.c b/vendor/hautelook/phpass/lib/crypt_private.c new file mode 100644 index 000000000..6abc05bc1 --- /dev/null +++ b/vendor/hautelook/phpass/lib/crypt_private.c @@ -0,0 +1,106 @@ +/* + * This code exists for the sole purpose to serve as another implementation + * of the "private" password hashing method implemened in PasswordHash.php + * and thus to confirm that these password hashes are indeed calculated as + * intended. + * + * Other uses of this code are discouraged. There are much better password + * hashing algorithms available to C programmers; one of those is bcrypt: + * + * http://www.openwall.com/crypt/ + * + * Written by Solar Designer in 2005 and placed in + * the public domain. + * + * There's absolutely no warranty. + */ + +#include +#include + +#ifdef TEST +#include +#endif + +static char *itoa64 = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void encode64(char *dst, char *src, int count) +{ + int i, value; + + i = 0; + do { + value = (unsigned char)src[i++]; + *dst++ = itoa64[value & 0x3f]; + if (i < count) + value |= (unsigned char)src[i] << 8; + *dst++ = itoa64[(value >> 6) & 0x3f]; + if (i++ >= count) + break; + if (i < count) + value |= (unsigned char)src[i] << 16; + *dst++ = itoa64[(value >> 12) & 0x3f]; + if (i++ >= count) + break; + *dst++ = itoa64[(value >> 18) & 0x3f]; + } while (i < count); +} + +char *crypt_private(char *password, char *setting) +{ + static char output[35]; + MD5_CTX ctx; + char hash[MD5_DIGEST_LENGTH]; + char *p, *salt; + int count_log2, length, count; + + strcpy(output, "*0"); + if (!strncmp(setting, output, 2)) + output[1] = '1'; + + if (strncmp(setting, "$P$", 3)) + return output; + + p = strchr(itoa64, setting[3]); + if (!p) + return output; + count_log2 = p - itoa64; + if (count_log2 < 7 || count_log2 > 30) + return output; + + salt = setting + 4; + if (strlen(salt) < 8) + return output; + + length = strlen(password); + + MD5_Init(&ctx); + MD5_Update(&ctx, salt, 8); + MD5_Update(&ctx, password, length); + MD5_Final(hash, &ctx); + + count = 1 << count_log2; + do { + MD5_Init(&ctx); + MD5_Update(&ctx, hash, MD5_DIGEST_LENGTH); + MD5_Update(&ctx, password, length); + MD5_Final(hash, &ctx); + } while (--count); + + memcpy(output, setting, 12); + encode64(&output[12], hash, MD5_DIGEST_LENGTH); + + return output; +} + +#ifdef TEST +int main(int argc, char **argv) +{ + if (argc != 3) return 1; + + puts(crypt_private(argv[1], argv[2])); + + return 0; +} +#endif diff --git a/vendor/hautelook/phpass/src/Hautelook/Phpass/PasswordHash.php b/vendor/hautelook/phpass/src/Hautelook/Phpass/PasswordHash.php new file mode 100644 index 000000000..eb363268b --- /dev/null +++ b/vendor/hautelook/phpass/src/Hautelook/Phpass/PasswordHash.php @@ -0,0 +1,314 @@ + in 2004-2006 and placed in + * + * There's absolutely no warranty. + * + * The homepage URL for this framework is: + * + * http://www.openwall.com/phpass/ + * + * Please be sure to update the Version line if you edit this file in any way. + * It is suggested that you leave the main version number intact, but indicate + * your project name (after the slash) and add your own revision information. + * + * Please do not change the "private" password hashing method implemented in + * here, thereby making your hashes incompatible. However, if you must, please + * change the hash type identifier (the "$P$") to something different. + * + * Obviously, since this code is in the public domain, the above are not + * requirements (there can be none), but merely suggestions. + * + * @author Solar Designer + */ +class PasswordHash +{ + private $itoa64; + private $iteration_count_log2; + private $portable_hashes; + private $random_state; + + /** + * Constructor + * + * @param int $iteration_count_log2 + * @param boolean $portable_hashes + */ + public function __construct($iteration_count_log2, $portable_hashes) + { + $this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; + + if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31) { + $iteration_count_log2 = 8; + } + $this->iteration_count_log2 = $iteration_count_log2; + + $this->portable_hashes = $portable_hashes; + + $this->random_state = microtime(); + if (function_exists('getmypid')) { + $this->random_state .= getmypid(); + } + } + + /** + * @param int $count + * @return String + */ + public function get_random_bytes($count) + { + $output = ''; + if (@is_readable('/dev/urandom') && + ($fh = @fopen('/dev/urandom', 'rb'))) { + $output = fread($fh, $count); + fclose($fh); + } + + if (strlen($output) < $count) { + $output = ''; + for ($i = 0; $i < $count; $i += 16) { + $this->random_state = + md5(microtime() . $this->random_state); + $output .= + pack('H*', md5($this->random_state)); + } + $output = substr($output, 0, $count); + } + + return $output; + } + + /** + * @param String $input + * @param int $count + * @return String + */ + public function encode64($input, $count) + { + $output = ''; + $i = 0; + do { + $value = ord($input[$i++]); + $output .= $this->itoa64[$value & 0x3f]; + if ($i < $count) { + $value |= ord($input[$i]) << 8; + } + $output .= $this->itoa64[($value >> 6) & 0x3f]; + if ($i++ >= $count) { + break; + } + if ($i < $count) { + $value |= ord($input[$i]) << 16; + } + $output .= $this->itoa64[($value >> 12) & 0x3f]; + if ($i++ >= $count) { + break; + } + $output .= $this->itoa64[($value >> 18) & 0x3f]; + } while ($i < $count); + + return $output; + } + + /** + * @param String $input + * @return String + */ + public function gensalt_private($input) + { + $output = '$P$'; + $output .= $this->itoa64[min($this->iteration_count_log2 + + ((PHP_VERSION >= '5') ? 5 : 3), 30)]; + $output .= $this->encode64($input, 6); + + return $output; + } + + /** + * @param String $password + * @param String $setting + * @return String + */ + public function crypt_private($password, $setting) + { + $output = '*0'; + if (substr($setting, 0, 2) == $output) { + $output = '*1'; + } + + $id = substr($setting, 0, 3); + # We use "$P$", phpBB3 uses "$H$" for the same thing + if ($id != '$P$' && $id != '$H$') { + return $output; + } + + $count_log2 = strpos($this->itoa64, $setting[3]); + if ($count_log2 < 7 || $count_log2 > 30) { + return $output; + } + + $count = 1 << $count_log2; + + $salt = substr($setting, 4, 8); + if (strlen($salt) != 8) { + return $output; + } + + // We're kind of forced to use MD5 here since it's the only + // cryptographic primitive available in all versions of PHP + // currently in use. To implement our own low-level crypto + // in PHP would result in much worse performance and + // consequently in lower iteration counts and hashes that are + // quicker to crack (by non-PHP code). + if (PHP_VERSION >= '5') { + $hash = md5($salt . $password, TRUE); + do { + $hash = md5($hash . $password, TRUE); + } while (--$count); + } else { + $hash = pack('H*', md5($salt . $password)); + do { + $hash = pack('H*', md5($hash . $password)); + } while (--$count); + } + + $output = substr($setting, 0, 12); + $output .= $this->encode64($hash, 16); + + return $output; + } + + /** + * @param String $input + * @return String + */ + public function gensalt_extended($input) + { + $count_log2 = min($this->iteration_count_log2 + 8, 24); + // This should be odd to not reveal weak DES keys, and the + // maximum valid value is (2**24 - 1) which is odd anyway. + $count = (1 << $count_log2) - 1; + + $output = '_'; + $output .= $this->itoa64[$count & 0x3f]; + $output .= $this->itoa64[($count >> 6) & 0x3f]; + $output .= $this->itoa64[($count >> 12) & 0x3f]; + $output .= $this->itoa64[($count >> 18) & 0x3f]; + + $output .= $this->encode64($input, 3); + + return $output; + } + + /** + * @param String $input + * @return String + */ + public function gensalt_blowfish($input) + { + // This one needs to use a different order of characters and a + // different encoding scheme from the one in encode64() above. + // We care because the last character in our encoded string will + // only represent 2 bits. While two known implementations of + // bcrypt will happily accept and correct a salt string which + // has the 4 unused bits set to non-zero, we do not want to take + // chances and we also do not want to waste an additional byte + // of entropy. + $itoa64 = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + + $output = '$2a$'; + $output .= chr(ord('0') + $this->iteration_count_log2 / 10); + $output .= chr(ord('0') + $this->iteration_count_log2 % 10); + $output .= '$'; + + $i = 0; + do { + $c1 = ord($input[$i++]); + $output .= $itoa64[$c1 >> 2]; + $c1 = ($c1 & 0x03) << 4; + if ($i >= 16) { + $output .= $itoa64[$c1]; + break; + } + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 4; + $output .= $itoa64[$c1]; + $c1 = ($c2 & 0x0f) << 2; + + $c2 = ord($input[$i++]); + $c1 |= $c2 >> 6; + $output .= $itoa64[$c1]; + $output .= $itoa64[$c2 & 0x3f]; + } while (1); + + return $output; + } + + /** + * @param String $password + */ + public function HashPassword($password) + { + $random = ''; + + if (CRYPT_BLOWFISH == 1 && !$this->portable_hashes) { + $random = $this->get_random_bytes(16); + $hash = + crypt($password, $this->gensalt_blowfish($random)); + if (strlen($hash) == 60) { + return $hash; + } + } + + if (CRYPT_EXT_DES == 1 && !$this->portable_hashes) { + if (strlen($random) < 3) { + $random = $this->get_random_bytes(3); + } + $hash = + crypt($password, $this->gensalt_extended($random)); + if (strlen($hash) == 20) { + return $hash; + } + } + + if (strlen($random) < 6) { + $random = $this->get_random_bytes(6); + } + + $hash = + $this->crypt_private($password, + $this->gensalt_private($random)); + if (strlen($hash) == 34) { + return $hash; + } + + // Returning '*' on error is safe here, but would _not_ be safe + // in a crypt(3)-like function used _both_ for generating new + // hashes and for validating passwords against existing hashes. + return '*'; + } + + /** + * @param String $password + * @param String $stored_hash + * @return boolean + */ + public function CheckPassword($password, $stored_hash) + { + $hash = $this->crypt_private($password, $stored_hash); + if ($hash[0] == '*') { + $hash = crypt($password, $stored_hash); + } + + return $hash === $stored_hash; + } +} diff --git a/vendor/matthiasmullie/minify/.coveralls.yml b/vendor/matthiasmullie/minify/.coveralls.yml deleted file mode 100644 index 09bbb257d..000000000 --- a/vendor/matthiasmullie/minify/.coveralls.yml +++ /dev/null @@ -1,3 +0,0 @@ -src_dir: src -coverage_clover: build/logs/clover.xml -json_path: build/logs/coveralls-upload.json diff --git a/vendor/matthiasmullie/minify/CONTRIBUTING.md b/vendor/matthiasmullie/minify/CONTRIBUTING.md new file mode 100644 index 000000000..226cf976b --- /dev/null +++ b/vendor/matthiasmullie/minify/CONTRIBUTING.md @@ -0,0 +1,59 @@ +# How to contribute + + +## Issues + +When [filing bugs](https://github.com/matthiasmullie/minify/issues/new), +try to be as thorough as possible: +* What version did you use? +* What did you try to do? ***Please post the relevant parts of your code.*** +* What went wrong? ***Please include error messages, if any.*** +* What was the expected result? + + +## Pull requests + +Bug fixes and general improvements to the existing codebase are always welcome. +New features are also welcome, but will be judged on an individual basis. If +you'd rather not risk wasting your time implementing a new feature only to see +it turned down, please start the discussion by +[opening an issue](https://github.com/matthiasmullie/minify/issues/new). + +Don't forget to add your changes to the [changelog](CHANGELOG.md). + + +### Testing + +Please include tests for every change or addition to the code. +To run the complete test suite: + +```sh +vendor/bin/phpunit +``` + +When submitting a new pull request, please make sure that that the test suite +passes (Travis CI will run it & report back on your pull request.) + +To run the tests on Windows, run `tests/convert_symlinks_to_windows_style.sh` +from the command line in order to convert Linux-style test symlinks to +Windows-style. + + +### Coding standards + +All code must follow [PSR-2](http://www.php-fig.org/psr/psr-2/). Just make sure +to run php-cs-fixer before submitting the code, it'll take care of the +formatting for you: + +```sh +vendor/bin/php-cs-fixer fix src +vendor/bin/php-cs-fixer fix tests +``` + +Document the code thoroughly! + + +## License + +Note that minify is MIT-licensed, which basically allows anyone to do +anything they like with it, without restriction. diff --git a/vendor/matthiasmullie/minify/composer.json b/vendor/matthiasmullie/minify/composer.json index 8f81f24a9..ae1bdf0db 100644 --- a/vendor/matthiasmullie/minify/composer.json +++ b/vendor/matthiasmullie/minify/composer.json @@ -20,8 +20,7 @@ }, "require-dev": { "matthiasmullie/scrapbook": "~1.0", - "phpunit/phpunit": "~4.8", - "satooshi/php-coveralls": "~1.0" + "phpunit/phpunit": "~4.8" }, "autoload": { "psr-4": { diff --git a/vendor/matthiasmullie/minify/src/CSS.php b/vendor/matthiasmullie/minify/src/CSS.php index 3d2de11f5..ac9ff1ef2 100644 --- a/vendor/matthiasmullie/minify/src/CSS.php +++ b/vendor/matthiasmullie/minify/src/CSS.php @@ -2,6 +2,7 @@ namespace MatthiasMullie\Minify; +use MatthiasMullie\Minify\Exceptions\FileImportException; use MatthiasMullie\PathConverter\Converter; /** @@ -68,7 +69,7 @@ class CSS extends Minify /** * Move any import statements to the top. * - * @param $content string Nearly finished CSS content + * @param string $content Nearly finished CSS content * * @return string */ @@ -94,12 +95,15 @@ class CSS extends Minify * @import's will be loaded and their content merged into the original file, * to save HTTP requests. * - * @param string $source The file to combine imports for. - * @param string $content The CSS content to combine imports for. + * @param string $source The file to combine imports for. + * @param string $content The CSS content to combine imports for. + * @param string[] $parents Parent paths, for circular reference checks. * * @return string + * + * @throws FileImportException */ - protected function combineImports($source, $content) + protected function combineImports($source, $content, $parents) { $importRegexes = array( // @import url(xxx) @@ -208,14 +212,20 @@ class CSS extends Minify // only replace the import with the content if we can grab the // content of the file - if (strlen($importPath) < PHP_MAXPATHLEN && file_exists($importPath) && is_file($importPath)) { + if ($this->canImportFile($importPath)) { + // check if current file was not imported previously in the same + // import chain. + if (in_array($importPath, $parents)) { + throw new FileImportException('Failed to import file "'.$importPath.'": circular reference detected.'); + } + // grab referenced file & minify it (which may include importing // yet other @import statements recursively) $minifier = new static($importPath); - $importContent = $minifier->execute($source); + $importContent = $minifier->execute($source, $parents); // check if this is only valid for certain media - if ($match['media']) { + if (!empty($match['media'])) { $importContent = '@media '.$match['media'].'{'.$importContent.'}'; } @@ -259,20 +269,15 @@ class CSS extends Minify // only replace the import with the content if we're able to get // the content of the file, and it's relatively small - $import = file_exists($path); - $import = $import && is_file($path); - $import = $import && filesize($path) <= $this->maxImportSize * 1024; - if (!$import) { - continue; + if ($this->canImportFile($path) && $this->canImportBySize($path)) { + // grab content && base64-ize + $importContent = $this->load($path); + $importContent = base64_encode($importContent); + + // build replacement + $search[] = $match[0]; + $replace[] = 'url('.$this->importExtensions[$extension].';base64,'.$importContent.')'; } - - // grab content && base64-ize - $importContent = $this->load($path); - $importContent = base64_encode($importContent); - - // build replacement - $search[] = $match[0]; - $replace[] = 'url('.$this->importExtensions[$extension].';base64,'.$importContent.')'; } // replace the import statements @@ -286,15 +291,16 @@ class CSS extends Minify * Minify the data. * Perform CSS optimizations. * - * @param string[optional] $path Path to write the data to. + * @param string[optional] $path Path to write the data to. + * @param string[] $parents Parent paths, for circular reference checks. * * @return string The minified data. */ - public function execute($path = null) + public function execute($path = null, $parents = array()) { $content = ''; - // loop files + // loop css data (raw data and files) foreach ($this->data as $source => $css) { /* * Let's first take out strings & comments, since we can't just remove @@ -314,8 +320,9 @@ class CSS extends Minify // restore the string we've extracted earlier $css = $this->restoreExtractedData($css); - $source = $source ?: ''; - $css = $this->combineImports($source, $css); + $source = is_int($source) ? '' : $source; + $parents = $source ? array_merge($parents, array($source)) : $parents; + $css = $this->combineImports($source, $css, $parents); $css = $this->importFiles($source, $css); /* @@ -443,8 +450,15 @@ class CSS extends Minify // determine if it's a url() or an @import match $type = (strpos($match[0], '@import') === 0 ? 'import' : 'url'); + // attempting to interpret GET-params makes no sense, so let's discard them for awhile + $params = strrchr($match['path'], '?'); + $url = $params ? substr($match['path'], 0, -strlen($params)) : $match['path']; + // fix relative url - $url = $converter->convert($match['path']); + $url = $converter->convert($url); + + // now that the path has been converted, re-apply GET-params + $url .= $params; // build replacement $search[] = $match[0]; @@ -569,4 +583,16 @@ class CSS extends Minify return trim($content); } + + /** + * Check if file is small enough to be imported. + * + * @param string $path The path to the file. + * + * @return bool + */ + protected function canImportBySize($path) + { + return ($size = @filesize($path)) && $size <= $this->maxImportSize * 1024; + } } diff --git a/vendor/matthiasmullie/minify/src/Exception.php b/vendor/matthiasmullie/minify/src/Exception.php index 5f2af0dea..0cb3fab5e 100644 --- a/vendor/matthiasmullie/minify/src/Exception.php +++ b/vendor/matthiasmullie/minify/src/Exception.php @@ -3,8 +3,10 @@ namespace MatthiasMullie\Minify; /** + * @deprecated Use Exceptions\BasicException instead + * * @author Matthias Mullie */ -class Exception extends \Exception +abstract class Exception extends \Exception { } diff --git a/vendor/matthiasmullie/minify/src/Exceptions/BasicException.php b/vendor/matthiasmullie/minify/src/Exceptions/BasicException.php new file mode 100644 index 000000000..a6328f4e4 --- /dev/null +++ b/vendor/matthiasmullie/minify/src/Exceptions/BasicException.php @@ -0,0 +1,12 @@ + + */ +abstract class BasicException extends Exception +{ +} diff --git a/vendor/matthiasmullie/minify/src/Exceptions/FileImportException.php b/vendor/matthiasmullie/minify/src/Exceptions/FileImportException.php new file mode 100644 index 000000000..98fc341cc --- /dev/null +++ b/vendor/matthiasmullie/minify/src/Exceptions/FileImportException.php @@ -0,0 +1,10 @@ + + */ +class FileImportException extends BasicException +{ +} diff --git a/vendor/matthiasmullie/minify/src/Exceptions/IOException.php b/vendor/matthiasmullie/minify/src/Exceptions/IOException.php new file mode 100644 index 000000000..9b590742c --- /dev/null +++ b/vendor/matthiasmullie/minify/src/Exceptions/IOException.php @@ -0,0 +1,10 @@ + + */ +class IOException extends BasicException +{ +} diff --git a/vendor/matthiasmullie/minify/src/JS.php b/vendor/matthiasmullie/minify/src/JS.php index f0bd4fbcf..59e0da88a 100644 --- a/vendor/matthiasmullie/minify/src/JS.php +++ b/vendor/matthiasmullie/minify/src/JS.php @@ -263,12 +263,20 @@ class JS extends Minify // strip whitespace that ends in (or next line begin with) an operator // that allows statements to be broken up over multiple lines unset($before['+'], $before['-'], $after['+'], $after['-']); - $content = preg_replace('/('.implode('|', $before).')\s+/', '\\1', $content); - $content = preg_replace('/\s+('.implode('|', $after).')/', '\\1', $content); + $content = preg_replace( + array( + '/('.implode('|', $before).')\s+/', + '/\s+('.implode('|', $after).')/', + ), '\\1', $content + ); // make sure + and - can't be mistaken for, or joined into ++ and -- - $content = preg_replace('/(?getKeywordsForRegex($keywords); $keywords = '(?canImportFile($data)) { + $data = file_get_contents($data); + + // strip BOM, if any + if (substr($data, 0, 3) == "\xef\xbb\xbf") { + $data = substr($data, 3); + } + } + + return $data; + } + + /** + * Save to file. + * + * @param string $content The minified data. + * @param string $path The path to save the minified data to. + * + * @throws IOException + */ + protected function save($content, $path) + { + $handler = $this->openFileForWriting($path); + + $this->writeToFile($handler, $content); + + @fclose($handler); + } + /** * Register a pattern to execute against the source content. * * @param string $pattern PCRE pattern. * @param string|callable $replacement Replacement value for matched pattern. - * - * @throws Exception */ protected function registerPattern($pattern, $replacement = '') { @@ -316,7 +308,7 @@ abstract class Minify * placeholder text, so we've rid all strings from characters that may be * misinterpreted. Original string content will be saved in $this->extracted * and after doing all other minifying, we can restore the original content - * via restoreStrings() + * via restoreStrings(). * * @param string[optional] $chars */ @@ -325,7 +317,8 @@ abstract class Minify // PHP only supports $this inside anonymous functions since 5.4 $minifier = $this; $callback = function ($match) use ($minifier) { - if (!$match[1]) { + // check the second index here, because the first always contains a quote + if ($match[2] === '') { /* * Empty strings need no placeholder; they can't be confused for * anything else anyway. @@ -379,4 +372,50 @@ abstract class Minify return $content; } + + /** + * Check if the path is a regular file and can be read. + * + * @param string $path + * + * @return bool + */ + protected function canImportFile($path) + { + return strlen($path) < PHP_MAXPATHLEN && is_file($path) && is_readable($path); + } + + /** + * Attempts to open file specified by $path for writing. + * + * @param string $path The path to the file. + * + * @return resource Specifier for the target file. + * + * @throws IOException + */ + protected function openFileForWriting($path) + { + if (($handler = @fopen($path, 'w')) === false) { + throw new IOException('The file "'.$path.'" could not be opened for writing. Check if PHP has enough permissions.'); + } + + return $handler; + } + + /** + * Attempts to write $content to the file specified by $handler. $path is used for printing exceptions. + * + * @param resource $handler The resource to write to. + * @param string $content The content to write. + * @param string $path The path to the file (for exception printing only). + * + * @throws IOException + */ + protected function writeToFile($handler, $content, $path = '') + { + if (($result = @fwrite($handler, $content)) === false || ($result < strlen($content))) { + throw new IOException('The file "'.$path.'" could not be written to. Check your disk space and file permissions.'); + } + } } From de0d49b9f3f526ff81344387bbe9a8964510f119 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 13 Mar 2016 23:56:04 +0900 Subject: [PATCH 078/205] Add more algorithms to unit test --- tests/unit/framework/PasswordTest.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/unit/framework/PasswordTest.php b/tests/unit/framework/PasswordTest.php index 610092653..daa5229ed 100644 --- a/tests/unit/framework/PasswordTest.php +++ b/tests/unit/framework/PasswordTest.php @@ -69,12 +69,18 @@ class PasswordTest extends \Codeception\TestCase\Test $this->assertEquals(60, strlen($hash)); $this->assertTrue(Rhymix\Framework\Password::checkPassword($password, $hash, $algos)); - $algos = array('pbkdf2'); + $algos = array('sha384', 'pbkdf2'); $hash = Rhymix\Framework\Password::hashPassword($password, $algos); $this->assertRegExp('/^(sha256|sha512):[0-9]+:/', $hash); $this->assertEquals(60, strlen($hash)); $this->assertTrue(Rhymix\Framework\Password::checkPassword($password, $hash, $algos)); + $algos = array('sha1', 'portable'); + $hash = Rhymix\Framework\Password::hashPassword($password, $algos); + $this->assertRegExp('/^\$P\$/', $hash); + $this->assertEquals(34, strlen($hash)); + $this->assertTrue(Rhymix\Framework\Password::checkPassword($password, $hash, $algos)); + foreach (array('drupal', 'joomla', 'kimsqrb', 'mysql_old_password', 'mysql_new_password', 'mssql_pwdencrypt') as $algo) { $hash = Rhymix\Framework\Password::hashPassword($password, $algo); From 9c653d210b71669ace013bc5f1861edf79834d63 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Mon, 14 Mar 2016 09:41:34 +0900 Subject: [PATCH 079/205] added a column for administrators. --- modules/board/board.controller.php | 13 ++++++++----- modules/board/lang/ko.php | 1 + modules/board/skins/xedition/board.default.css | 7 +++++++ modules/board/skins/xedition/update_view.html | 4 ++-- modules/document/document.class.php | 9 +++++++++ modules/document/document.controller.php | 6 ++++++ modules/document/document.model.php | 13 +++++++++++++ .../document/queries/getUpdateLogAdminisExists.xml | 11 +++++++++++ .../document/queries/insertDocumentUpdateLog.xml | 1 + modules/document/schemas/document_update_log.xml | 1 + 10 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 modules/document/queries/getUpdateLogAdminisExists.xml diff --git a/modules/board/board.controller.php b/modules/board/board.controller.php index e2d38bd98..0b78fdfc6 100644 --- a/modules/board/board.controller.php +++ b/modules/board/board.controller.php @@ -50,6 +50,10 @@ class boardController extends board unset($obj->title_color); unset($obj->title_bold); } + else + { + $obj->is_admin = 'Y'; + } // generate document module model object $oDocumentModel = getModel('document'); @@ -227,12 +231,11 @@ class boardController extends board $isadminDocument = false; if($logged_info->is_admin != 'Y') { - $update_log_list = $oDocumentModel->getDocumentUpdateLog($update_log->document_srl); - foreach($update_log_list->data as $val) + $update_log_list = $oDocumentModel->getUpdateLogAdminisExists($update_log->document_srl); + + foreach($update_log_list as $val) { - $oMemberModel = getModel('member'); - $member_info = $oMemberModel->getMemberInfoByMemberSrl($val->update_member_srl); - if($member_info->is_admin === 'Y') + if($val->is_admin == 'Y') { $isadminDocument = true; break; diff --git a/modules/board/lang/ko.php b/modules/board/lang/ko.php index 0c627e9d1..988bdc19f 100644 --- a/modules/board/lang/ko.php +++ b/modules/board/lang/ko.php @@ -65,3 +65,4 @@ $lang->msg_no_update_log = '업데이트 로그가 존재하지 않습니다.'; $lang->cmd_modify_by_update_log = '이 로그로 게시물을 수정하기'; $lang->msg_admin_update_log = '관리자가 수정한 적이 있는 게시물입니다. 관리자에게 문의하세요.'; $lang->msg_update_log_revert = '정말로 이 게시물로 되돌리시겠습니까?'; +$lang->write_admin = '관리자작성'; diff --git a/modules/board/skins/xedition/board.default.css b/modules/board/skins/xedition/board.default.css index 30022f34c..92fd8f4c9 100644 --- a/modules/board/skins/xedition/board.default.css +++ b/modules/board/skins/xedition/board.default.css @@ -455,6 +455,13 @@ text-overflow: ellipsis; color: #555; } +.read_header h1 span.is_admin { + font-size: 15px; + font-weight: normal; + line-height:15px; + color:#FF6C00; +} + .read_header h1 a { text-decoration: none; color: #444; diff --git a/modules/board/skins/xedition/update_view.html b/modules/board/skins/xedition/update_view.html index 487b11568..bfbffda3c 100644 --- a/modules/board/skins/xedition/update_view.html +++ b/modules/board/skins/xedition/update_view.html @@ -5,7 +5,7 @@ - + diff --git a/modules/document/document.class.php b/modules/document/document.class.php index 17d9d0530..993adcd25 100644 --- a/modules/document/document.class.php +++ b/modules/document/document.class.php @@ -127,6 +127,9 @@ class document extends ModuleObject // 2016. 1. 27: Add a column(declare_message) for report if(!$oDB->isColumnExists("document_declared_log","declare_message")) return true; + // 2016. 3. 14 Add a column(document_upate_log) for admin + if(!$oDB->isColumnExists('document_update_log', 'is_admin')) return true; + return false; } @@ -328,6 +331,12 @@ class document extends ModuleObject $oDB->addColumn('document_declared_log',"declare_message","text"); } + // 2016. 3. 14 Add a column(document_update_log) for admin + if(!$oDB->isColumnExists('document_update_log', 'is_admin')) + { + $oDB->addColumn('document_update_log', 'is_admin', 'varchar', 1); + } + return new Object(0,'success_updated'); } diff --git a/modules/document/document.controller.php b/modules/document/document.controller.php index eb59687f8..593f6ea25 100644 --- a/modules/document/document.controller.php +++ b/modules/document/document.controller.php @@ -476,6 +476,7 @@ class documentController extends document { $obj->extra_vars = serialize($extra_vars); $update_output = $this->insertDocumentUpdateLog($obj); + if(!$update_output->toBool()) { $oDB->rollback(); @@ -749,6 +750,10 @@ class documentController extends document if($obj->update_log_setting === 'Y') { $obj->extra_vars = serialize($extra_vars); + if($this->grant->manager) + { + $obj->is_admin = 'Y'; + } $update_output = $this->insertDocumentUpdateLog($obj, $source_obj); if(!$update_output->toBool()) { @@ -816,6 +821,7 @@ class documentController extends document $update_args->tags = $obj->tags; $update_args->extra_vars = $obj->extra_vars; $update_args->reason_update = $obj->reason_update; + $update_args->is_admin = $obj->is_admin; $update_output = executeQuery('document.insertDocumentUpdateLog', $update_args); return $update_output; diff --git a/modules/document/document.model.php b/modules/document/document.model.php index ac5df7e32..6cbff5dcb 100644 --- a/modules/document/document.model.php +++ b/modules/document/document.model.php @@ -1567,6 +1567,19 @@ class documentModel extends document return $updage_log; } + + function getUpdateLogAdminisExists($document_srl = null) + { + if($document_srl == null) + { + return; + } + $args = new stdClass(); + $args->document_srl = $document_srl; + $output = executeQueryArray('document.getUpdateLogAdminisExists', $args); + + return $output->data; + } } /* End of file document.model.php */ /* Location: ./modules/document/document.model.php */ diff --git a/modules/document/queries/getUpdateLogAdminisExists.xml b/modules/document/queries/getUpdateLogAdminisExists.xml new file mode 100644 index 000000000..18fee9ed8 --- /dev/null +++ b/modules/document/queries/getUpdateLogAdminisExists.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/modules/document/queries/insertDocumentUpdateLog.xml b/modules/document/queries/insertDocumentUpdateLog.xml index 921dd1b51..e92360a85 100644 --- a/modules/document/queries/insertDocumentUpdateLog.xml +++ b/modules/document/queries/insertDocumentUpdateLog.xml @@ -18,5 +18,6 @@ + diff --git a/modules/document/schemas/document_update_log.xml b/modules/document/schemas/document_update_log.xml index 85a99283a..05cf6d863 100644 --- a/modules/document/schemas/document_update_log.xml +++ b/modules/document/schemas/document_update_log.xml @@ -15,4 +15,5 @@ +
From f15a4c88615470aa5f765af28771b4923dc800ec Mon Sep 17 00:00:00 2001 From: BJRambo Date: Mon, 14 Mar 2016 09:44:59 +0900 Subject: [PATCH 080/205] rename method. --- modules/board/board.controller.php | 2 +- modules/board/conf/module.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/board/board.controller.php b/modules/board/board.controller.php index 0b78fdfc6..a7fc4e2b3 100644 --- a/modules/board/board.controller.php +++ b/modules/board/board.controller.php @@ -215,7 +215,7 @@ class boardController extends board $this->setMessage($msg_code); } - function procBoardUpdateDocument() + function procBoardRevertDocument() { $update_id = Context::get('update_id'); $logged_info = Context::get('logged_info'); diff --git a/modules/board/conf/module.xml b/modules/board/conf/module.xml index 8551bed96..dfa619e9b 100644 --- a/modules/board/conf/module.xml +++ b/modules/board/conf/module.xml @@ -80,7 +80,7 @@ - + From e37a7adcc7f39911786a14e21978a48633136923 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Mon, 14 Mar 2016 09:51:47 +0900 Subject: [PATCH 081/205] rename update method. --- modules/board/tpl/filter/update.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/board/tpl/filter/update.xml b/modules/board/tpl/filter/update.xml index a4ae8bb65..a55476429 100644 --- a/modules/board/tpl/filter/update.xml +++ b/modules/board/tpl/filter/update.xml @@ -1,4 +1,4 @@ - + From e9acd7fe14283986a08bd5f44d025c20d2d8fc12 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Mon, 14 Mar 2016 10:03:58 +0900 Subject: [PATCH 082/205] Improved problem reverting records didn't write --- modules/board/board.controller.php | 2 ++ modules/board/lang/ko.php | 1 + 2 files changed, 3 insertions(+) diff --git a/modules/board/board.controller.php b/modules/board/board.controller.php index a7fc4e2b3..9d7865674 100644 --- a/modules/board/board.controller.php +++ b/modules/board/board.controller.php @@ -259,6 +259,8 @@ class boardController extends board $obj->title_bold = $update_log->title_bold; $obj->title_color = $update_log->title_color; $obj->content = $update_log->content; + $obj->update_log_setting = 'Y'; + $obj->reason_update = lang('board.revert_reason_update'); $output = $oDocumentController->updateDocument($oDocument, $obj); $this->setRedirectUrl(getNotEncodedUrl('', 'mid', Context::get('mid'),'act', '', 'document_srl', $update_log->document_srl)); $this->add('mid', Context::get('mid')); diff --git a/modules/board/lang/ko.php b/modules/board/lang/ko.php index 988bdc19f..215d099e3 100644 --- a/modules/board/lang/ko.php +++ b/modules/board/lang/ko.php @@ -66,3 +66,4 @@ $lang->cmd_modify_by_update_log = '이 로그로 게시물을 수정하기'; $lang->msg_admin_update_log = '관리자가 수정한 적이 있는 게시물입니다. 관리자에게 문의하세요.'; $lang->msg_update_log_revert = '정말로 이 게시물로 되돌리시겠습니까?'; $lang->write_admin = '관리자작성'; +$lang->revert_reason_update = '작성글을 현재글로 되돌림'; From 39cc333f333752b156806cff2faf88d9f9c55e20 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Mon, 14 Mar 2016 11:10:43 +0900 Subject: [PATCH 083/205] add index is_admin column, and more improving. --- modules/board/board.controller.php | 14 ++------------ modules/document/document.class.php | 1 + modules/document/document.model.php | 10 ++++++++-- .../document/queries/getUpdateLogAdminisExists.xml | 3 ++- modules/document/schemas/document_update_log.xml | 2 +- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/modules/board/board.controller.php b/modules/board/board.controller.php index 9d7865674..4b726a63a 100644 --- a/modules/board/board.controller.php +++ b/modules/board/board.controller.php @@ -228,20 +228,10 @@ class boardController extends board $oDocumentController = getController('document'); $update_log = $oDocumentModel->getUpdateLog($update_id); - $isadminDocument = false; if($logged_info->is_admin != 'Y') { - $update_log_list = $oDocumentModel->getUpdateLogAdminisExists($update_log->document_srl); - - foreach($update_log_list as $val) - { - if($val->is_admin == 'Y') - { - $isadminDocument = true; - break; - } - } - if($isadminDocument === true) + $Exists_log = $oDocumentModel->getUpdateLogAdminisExists($update_log->document_srl); + if($Exists_log === true) { return new Object(-1, 'msg_admin_update_log'); } diff --git a/modules/document/document.class.php b/modules/document/document.class.php index 993adcd25..3eef195d0 100644 --- a/modules/document/document.class.php +++ b/modules/document/document.class.php @@ -335,6 +335,7 @@ class document extends ModuleObject if(!$oDB->isColumnExists('document_update_log', 'is_admin')) { $oDB->addColumn('document_update_log', 'is_admin', 'varchar', 1); + $oDB->addIndex('document_update_log', 'idx_is_admin', array('is_admin')); } return new Object(0,'success_updated'); diff --git a/modules/document/document.model.php b/modules/document/document.model.php index 6cbff5dcb..16e345d59 100644 --- a/modules/document/document.model.php +++ b/modules/document/document.model.php @@ -1576,9 +1576,15 @@ class documentModel extends document } $args = new stdClass(); $args->document_srl = $document_srl; - $output = executeQueryArray('document.getUpdateLogAdminisExists', $args); + $args->is_admin = 'Y'; + $output = executeQuery('document.getUpdateLogAdminisExists', $args); - return $output->data; + if($output->data->count > 0) + { + return true; + } + + return false; } } /* End of file document.model.php */ diff --git a/modules/document/queries/getUpdateLogAdminisExists.xml b/modules/document/queries/getUpdateLogAdminisExists.xml index 18fee9ed8..7074c565c 100644 --- a/modules/document/queries/getUpdateLogAdminisExists.xml +++ b/modules/document/queries/getUpdateLogAdminisExists.xml @@ -3,9 +3,10 @@ - + + diff --git a/modules/document/schemas/document_update_log.xml b/modules/document/schemas/document_update_log.xml index 05cf6d863..fee66b4bd 100644 --- a/modules/document/schemas/document_update_log.xml +++ b/modules/document/schemas/document_update_log.xml @@ -15,5 +15,5 @@ - +
From aab6b2ad625f04aa8e5c2ab8183fb168b4dcc07f Mon Sep 17 00:00:00 2001 From: BJRambo Date: Mon, 14 Mar 2016 11:12:56 +0900 Subject: [PATCH 084/205] Language improvement --- modules/board/lang/ko.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/board/lang/ko.php b/modules/board/lang/ko.php index 215d099e3..23e90484a 100644 --- a/modules/board/lang/ko.php +++ b/modules/board/lang/ko.php @@ -64,6 +64,6 @@ $lang->msg_no_update_id = '업데이트 고유 번호는 필수입니다.'; $lang->msg_no_update_log = '업데이트 로그가 존재하지 않습니다.'; $lang->cmd_modify_by_update_log = '이 로그로 게시물을 수정하기'; $lang->msg_admin_update_log = '관리자가 수정한 적이 있는 게시물입니다. 관리자에게 문의하세요.'; -$lang->msg_update_log_revert = '정말로 이 게시물로 되돌리시겠습니까?'; +$lang->msg_update_log_revert = '정말로 이 버전으로 되돌리시겠습니까?'; $lang->write_admin = '관리자작성'; -$lang->revert_reason_update = '작성글을 현재글로 되돌림'; +$lang->revert_reason_update = '이 버전으로 되돌림'; From 13a67f3496000964b2031af86f050d4e2927b186 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 14 Mar 2016 11:51:32 +0900 Subject: [PATCH 085/205] Implement URL::modifyURL() --- common/framework/url.php | 43 ++++++++++++++++++++++++-------- tests/unit/framework/URLTest.php | 22 ++++++++++++++++ 2 files changed, 55 insertions(+), 10 deletions(-) diff --git a/common/framework/url.php b/common/framework/url.php index 1829fbe5c..8fc370bdb 100644 --- a/common/framework/url.php +++ b/common/framework/url.php @@ -21,19 +21,15 @@ class URL $proto = \RX_SSL ? 'https://' : 'http://'; $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost'; $local = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/'; + $url = $proto . $host . $local; if (count($changes)) { - if (($qpos = strpos($local, '?')) !== false) - { - $querystring = substr($local, $qpos + 1); - $local = substr($local, 0, $qpos); - parse_str($querystring, $args); - $changes = array_merge($args, $changes); - } - $changes = array_filter($changes, function($val) { return $val !== null; }); - $local = $local . '?' . http_build_query($changes); + return self::modifyURL($url, $changes); + } + else + { + return self::getCanonicalURL($url); } - return self::getCanonicalURL($proto . $host) . $local; } /** @@ -102,6 +98,33 @@ class URL return false; } + /** + * Modify a URL. + * + * If $changes are given, they will be appended to the current URL as a query string. + * To delete an existing query string, set its value to null. + * + * @param string $url + * @param array $changes + * @return string + */ + public static function modifyURL($url, array $changes = array()) + { + $url = parse_url(self::getCanonicalURL($url)); + $prefix = sprintf('%s://%s%s%s', $url['scheme'], $url['host'], ($url['port'] ? (':' . $url['port']) : ''), $url['path']); + parse_str($url['query'], $args); + $changes = array_merge($args, $changes); + $changes = array_filter($changes, function($val) { return $val !== null; }); + if (count($changes)) + { + return $prefix . '?' . http_build_query($changes); + } + else + { + return $prefix; + } + } + /** * Encode UTF-8 domain into IDNA (punycode) * diff --git a/tests/unit/framework/URLTest.php b/tests/unit/framework/URLTest.php index 93410ed58..d36dd5fe6 100644 --- a/tests/unit/framework/URLTest.php +++ b/tests/unit/framework/URLTest.php @@ -18,6 +18,9 @@ class URLTest extends \Codeception\TestCase\Test // Removing item from the query string $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php?xe=sucks', Rhymix\Framework\URL::getCurrentURL(array('foo' => null))); + // Removing all items from the query string + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php', Rhymix\Framework\URL::getCurrentURL(array('foo' => null, 'xe' => null))); + // Adding and removing parameters at the same time $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php?xe=sucks&l=ko', Rhymix\Framework\URL::getCurrentURL(array('l' => 'ko', 'foo' => null))); } @@ -55,6 +58,25 @@ class URLTest extends \Codeception\TestCase\Test } } + public function testModifyURL() + { + $protocol = \RX_SSL ? 'https://' : 'http://'; + $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; + $url = $protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php?foo=bar'; + + // Conversion to absolute + $this->assertEquals($url, Rhymix\Framework\URL::modifyURL('./index.php?foo=bar')); + + // Adding items to the query string + $this->assertEquals($url . '&var=1&arr%5B0%5D=2&arr%5B1%5D=3', Rhymix\Framework\URL::modifyURL($url, array('var' => '1', 'arr' => array(2, 3)))); + + // Removing item from the query string + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php', Rhymix\Framework\URL::modifyURL($url, array('foo' => null))); + + // Adding and removing parameters at the same time + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php?l=ko', Rhymix\Framework\URL::modifyURL($url, array('l' => 'ko', 'foo' => null))); + } + public function testIsInternalURL() { // This function is checked in Security::checkCSRF() From 19ad1d1ac45c83776f22729e646d7fb348cd4b8e Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 14 Mar 2016 13:16:02 +0900 Subject: [PATCH 086/205] Use encryption and URL functions to improve SSO --- classes/context/Context.class.php | 57 +++++++++++++++++-------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/classes/context/Context.class.php b/classes/context/Context.class.php index 44ec47d6d..94fc3d848 100644 --- a/classes/context/Context.class.php +++ b/classes/context/Context.class.php @@ -712,61 +712,66 @@ class Context $current_site = self::getRequestUri(); // Step 1: if the current site is not the default site, send SSO validation request to the default site - if($default_url !== $current_site && !self::get('SSOID') && $_COOKIE['sso'] !== md5($current_site)) + if($default_url !== $current_site && !self::get('sso_response') && $_COOKIE['sso'] !== md5($current_site)) { // Set sso cookie to prevent multiple simultaneous SSO validation requests setcookie('sso', md5($current_site), 0, '/'); // Redirect to the default site - $redirect_url = sprintf('%s?return_url=%s', $default_url, urlencode(base64_encode($current_site))); + $sso_request = Rhymix\Framework\Security::encrypt(Rhymix\Framework\URL::getCurrentURL()); + $redirect_url = $default_url . '?sso_request=' . urlencode($sso_request); header('Location:' . $redirect_url); - return FALSE; + return false; } // Step 2: receive and process SSO validation request at the default site - if($default_url === $current_site && self::get('return_url')) + if($default_url === $current_site && self::get('sso_request')) { // Get the URL of the origin site - $url = base64_decode(self::get('return_url')); - $url_info = parse_url($url); + $sso_request = Rhymix\Framework\Security::decrypt(self::get('sso_request')); + if (!$sso_request || !preg_match('!^https?://!', $sso_request)) + { + self::displayErrorPage('SSO Error', 'Invalid SSO Request', 400); + return false; + } // Check that the origin site is a valid site in this XE installation (to prevent open redirect vuln) if(!getModel('module')->getSiteInfoByDomain(rtrim($url, '/'))->site_srl) { - htmlHeader(); - echo self::getLang("msg_invalid_request"); - htmlFooter(); - return FALSE; + self::displayErrorPage('SSO Error', 'Invalid SSO Request', 400); + return false; } // Redirect back to the origin site - $url_info['query'] .= ($url_info['query'] ? '&' : '') . 'SSOID=' . session_id(); - $redirect_url = sprintf('%s://%s%s%s%s', $url_info['scheme'], $url_info['host'], $url_info['port'] ? (':' . $url_info['port']) : '', $url_info['path'], ($url_info['query'] ? ('?' . $url_info['query']) : '')); - header('Location:' . $redirect_url); - return FALSE; + $sso_response = Rhymix\Framework\Security::encrypt(session_id()); + header('Location: ' . Rhymix\Framework\URL::modifyURL($sso_request, array('sso_response' => $sso_response))); + return false; } // Step 3: back at the origin site, set session ID to be the same as the default site - if($default_url !== $current_site && self::get('SSOID')) + if($default_url !== $current_site && self::get('sso_response')) { - // Check that the session ID was given by the default site (to prevent session fixation CSRF) + // Check SSO response + $sso_response = Rhymix\Framework\Security::decrypt(self::get('sso_response')); + if ($sso_response === false) + { + self::displayErrorPage('SSO Error', 'Invalid SSO Response', 400); + return false; + } + + // Check that the response was given by the default site (to prevent session fixation CSRF) if(isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $default_url) !== 0) { - htmlHeader(); - echo self::getLang("msg_invalid_request"); - htmlFooter(); - return FALSE; + self::displayErrorPage('SSO Error', 'Invalid SSO Response', 400); + return false; } // Set session ID - setcookie(session_name(), self::get('SSOID')); + setcookie(session_name(), $sso_response); // Finally, redirect to the originally requested URL - $url_info = parse_url(self::getRequestUrl()); - $url_info['query'] = preg_replace('/(^|\b)SSOID=([^&?]+)/', '', $url_info['query']); - $redirect_url = sprintf('%s://%s%s%s%s', $url_info['scheme'], $url_info['host'], $url_info['port'] ? (':' . $url_info['port']) : '', $url_info['path'], ($url_info['query'] ? ('?' . $url_info['query']) : '')); - header('Location:' . $redirect_url); - return FALSE; + header('Location: ' . Rhymix\Framework\URL::getCurrentURL(array('sso_response' => null))); + return false; } // If none of the conditions above apply, proceed normally From 804fd0515bcb667147753edb17deb4da31b547e0 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 14 Mar 2016 15:32:39 +0900 Subject: [PATCH 087/205] Also apply embed filter to
'; - $content = Rhymix\Framework\Security\MediaFilter::removeEmbeddedMedia($content, $security_msg); + $content = Rhymix\Framework\Filters\MediaFilter::removeEmbeddedMedia($content, $security_msg); } return; diff --git a/modules/admin/admin.admin.controller.php b/modules/admin/admin.admin.controller.php index fd8ace3c4..deebc7ed0 100644 --- a/modules/admin/admin.admin.controller.php +++ b/modules/admin/admin.admin.controller.php @@ -561,7 +561,7 @@ class adminAdminController extends admin return $item !== ''; }); $iframe_whitelist = array_unique(array_map(function($item) { - return Rhymix\Framework\Security\MediaFilter::formatPrefix($item); + return Rhymix\Framework\Filters\MediaFilter::formatPrefix($item); }, $iframe_whitelist)); natcasesort($iframe_whitelist); Rhymix\Framework\Config::set('mediafilter.iframe', array_values($iframe_whitelist)); @@ -572,7 +572,7 @@ class adminAdminController extends admin return $item !== ''; }); $object_whitelist = array_unique(array_map(function($item) { - return Rhymix\Framework\Security\MediaFilter::formatPrefix($item); + return Rhymix\Framework\Filters\MediaFilter::formatPrefix($item); }, $object_whitelist)); natcasesort($object_whitelist); Rhymix\Framework\Config::set('mediafilter.object', array_values($object_whitelist)); @@ -587,7 +587,7 @@ class adminAdminController extends admin $allowed_ip = array_unique(array_filter($allowed_ip, function($item) { return $item !== ''; })); - if (!Rhymix\Framework\Security\IpFilter::validateRanges($allowed_ip)) { + if (!Rhymix\Framework\Filters\IpFilter::validateRanges($allowed_ip)) { return new Object(-1, 'msg_invalid_ip'); } @@ -595,7 +595,7 @@ class adminAdminController extends admin $denied_ip = array_unique(array_filter($denied_ip, function($item) { return $item !== ''; })); - if (!Rhymix\Framework\Security\IpFilter::validateRanges($denied_ip)) { + if (!Rhymix\Framework\Filters\IpFilter::validateRanges($denied_ip)) { return new Object(-1, 'msg_invalid_ip'); } @@ -745,7 +745,7 @@ class adminAdminController extends admin $allowed_ip = array_unique(array_filter($allowed_ip, function($item) { return $item !== ''; })); - if (!Rhymix\Framework\Security\IpFilter::validate($allowed_ip)) { + if (!Rhymix\Framework\Filters\IpFilter::validate($allowed_ip)) { return new Object(-1, 'msg_invalid_ip'); } Rhymix\Framework\Config::set('debug.allow', array_values($allowed_ip)); @@ -771,17 +771,17 @@ class adminAdminController extends admin if ($vars->sitelock_locked === 'Y') { - if (!Rhymix\Framework\Security\IpFilter::inRanges('127.0.0.1', $allowed_ip)) + if (!Rhymix\Framework\Filters\IpFilter::inRanges('127.0.0.1', $allowed_ip)) { array_unshift($allowed_ip, '127.0.0.1'); } - if (!Rhymix\Framework\Security\IpFilter::inRanges(RX_CLIENT_IP, $allowed_ip)) + if (!Rhymix\Framework\Filters\IpFilter::inRanges(RX_CLIENT_IP, $allowed_ip)) { array_unshift($allowed_ip, RX_CLIENT_IP); } } - if (!Rhymix\Framework\Security\IpFilter::validateRanges($allowed_ip)) + if (!Rhymix\Framework\Filters\IpFilter::validateRanges($allowed_ip)) { return new Object(-1, 'msg_invalid_ip'); } diff --git a/modules/admin/admin.admin.view.php b/modules/admin/admin.admin.view.php index a74801f38..18b23eab1 100644 --- a/modules/admin/admin.admin.view.php +++ b/modules/admin/admin.admin.view.php @@ -418,8 +418,8 @@ class adminAdminView extends admin function dispAdminConfigSecurity() { // Load embed filter. - context::set('mediafilter_iframe', implode(PHP_EOL, Rhymix\Framework\Security\MediaFilter::getIframeWhitelist())); - context::set('mediafilter_object', implode(PHP_EOL, Rhymix\Framework\Security\MediaFilter::getObjectWhitelist())); + context::set('mediafilter_iframe', implode(PHP_EOL, Rhymix\Framework\Filters\MediaFilter::getIframeWhitelist())); + context::set('mediafilter_object', implode(PHP_EOL, Rhymix\Framework\Filters\MediaFilter::getObjectWhitelist())); // Admin IP access control $allowed_ip = Rhymix\Framework\Config::get('admin.allow'); @@ -518,11 +518,11 @@ class adminAdminView extends admin Context::set('sitelock_message', escape(Rhymix\Framework\Config::get('lock.message'))); $allowed_ip = Rhymix\Framework\Config::get('lock.allow') ?: array(); - if (!Rhymix\Framework\Security\IpFilter::inRanges('127.0.0.1', $allowed_ip)) + if (!Rhymix\Framework\Filters\IpFilter::inRanges('127.0.0.1', $allowed_ip)) { array_unshift($allowed_ip, '127.0.0.1'); } - if (!Rhymix\Framework\Security\IpFilter::inRanges(RX_CLIENT_IP, $allowed_ip)) + if (!Rhymix\Framework\Filters\IpFilter::inRanges(RX_CLIENT_IP, $allowed_ip)) { array_unshift($allowed_ip, RX_CLIENT_IP); } diff --git a/modules/file/file.controller.php b/modules/file/file.controller.php index 12d618456..76cbc63df 100644 --- a/modules/file/file.controller.php +++ b/modules/file/file.controller.php @@ -732,7 +732,7 @@ class fileController extends file } // Sanitize filename - $file_info['name'] = Rhymix\Framework\Security\FilenameFilter::clean($file_info['name']); + $file_info['name'] = Rhymix\Framework\Filters\FilenameFilter::clean($file_info['name']); // Set upload path by checking if the attachement is an image or other kinds of file if(preg_match("/\.(jpe?g|gif|png|wm[va]|mpe?g|avi|swf|flv|mp[1-4]|as[fx]|wav|midi?|moo?v|qt|r[am]{1,2}|m4v)$/i", $file_info['name'])) diff --git a/modules/member/member.admin.model.php b/modules/member/member.admin.model.php index 8e9682bb6..e9930e5db 100644 --- a/modules/member/member.admin.model.php +++ b/modules/member/member.admin.model.php @@ -298,12 +298,12 @@ class memberAdminModel extends member { if ($allow_list = ($allow_list === null) ? config('admin.allow') : $allow_list) { - return Rhymix\Framework\Security\IpFilter::inRanges(RX_CLIENT_IP, $allow_list); + return Rhymix\Framework\Filters\IpFilter::inRanges(RX_CLIENT_IP, $allow_list); } if ($deny_list = ($deny_list === null) ? config('admin.deny') : $deny_list) { - return !Rhymix\Framework\Security\IpFilter::inRanges(RX_CLIENT_IP, $deny_list); + return !Rhymix\Framework\Filters\IpFilter::inRanges(RX_CLIENT_IP, $deny_list); } return true; diff --git a/tests/unit/framework/security/FilenameFilterTest.php b/tests/unit/framework/filters/FilenameFilterTest.php similarity index 93% rename from tests/unit/framework/security/FilenameFilterTest.php rename to tests/unit/framework/filters/FilenameFilterTest.php index 935ffd473..7fe095d35 100644 --- a/tests/unit/framework/security/FilenameFilterTest.php +++ b/tests/unit/framework/filters/FilenameFilterTest.php @@ -35,7 +35,7 @@ class FilenameFilterTest extends \Codeception\TestCase\Test foreach ($tests as $from => $to) { - $result = Rhymix\Framework\Security\FilenameFilter::clean($from); + $result = Rhymix\Framework\Filters\FilenameFilter::clean($from); $this->assertEquals($to, $result); } } diff --git a/tests/unit/framework/security/HTMLFilterTest.php b/tests/unit/framework/filters/HTMLFilterTest.php similarity index 84% rename from tests/unit/framework/security/HTMLFilterTest.php rename to tests/unit/framework/filters/HTMLFilterTest.php index 1b32a675c..75aae9cd8 100644 --- a/tests/unit/framework/security/HTMLFilterTest.php +++ b/tests/unit/framework/filters/HTMLFilterTest.php @@ -61,7 +61,7 @@ class HTMLFilterTest extends \Codeception\TestCase\Test foreach ($tests as $test) { - $this->assertEquals($test[1], Rhymix\Framework\Security\HTMLFilter::clean($test[0])); + $this->assertEquals($test[1], Rhymix\Framework\Filters\HTMLFilter::clean($test[0])); } } @@ -69,41 +69,41 @@ class HTMLFilterTest extends \Codeception\TestCase\Test { $source = '
'; $target = '
'; - $this->assertEquals($target, Rhymix\Framework\Security\HTMLFilter::clean($source)); + $this->assertEquals($target, Rhymix\Framework\Filters\HTMLFilter::clean($source)); $source = ''; $target = ''; - $this->assertEquals($target, Rhymix\Framework\Security\HTMLFilter::clean($source)); + $this->assertEquals($target, Rhymix\Framework\Filters\HTMLFilter::clean($source)); $source = '
456
789
'; $target = '
456
789
'; - $this->assertEquals($target, Rhymix\Framework\Security\HTMLFilter::clean($source)); + $this->assertEquals($target, Rhymix\Framework\Filters\HTMLFilter::clean($source)); } public function testHTMLFilterCSS3() { $source = '
'; $target = '
'; - $this->assertEquals($target, Rhymix\Framework\Security\HTMLFilter::clean($source)); + $this->assertEquals($target, Rhymix\Framework\Filters\HTMLFilter::clean($source)); $source = '
'; $target = '
'; - $this->assertEquals($target, Rhymix\Framework\Security\HTMLFilter::clean($source)); + $this->assertEquals($target, Rhymix\Framework\Filters\HTMLFilter::clean($source)); $source = '
'; $target = '
'; - $this->assertEquals($target, Rhymix\Framework\Security\HTMLFilter::clean($source)); + $this->assertEquals($target, Rhymix\Framework\Filters\HTMLFilter::clean($source)); } public function testHTMLFilterEmbeddedMedia() { $source = ''; $target = ''; - $this->assertEquals($target, Rhymix\Framework\Security\HTMLFilter::clean($source)); + $this->assertEquals($target, Rhymix\Framework\Filters\HTMLFilter::clean($source)); $source = ''; $target = ''; - $this->assertEquals($target, Rhymix\Framework\Security\HTMLFilter::clean($source)); + $this->assertEquals($target, Rhymix\Framework\Filters\HTMLFilter::clean($source)); $source = '' . '' . @@ -123,33 +123,33 @@ class HTMLFilterTest extends \Codeception\TestCase\Test '' . '' . ''; - $this->assertEquals($target, Rhymix\Framework\Security\HTMLFilter::clean($source)); + $this->assertEquals($target, Rhymix\Framework\Filters\HTMLFilter::clean($source)); $source = ''; $target = ''; - $this->assertEquals($target, Rhymix\Framework\Security\HTMLFilter::clean($source)); + $this->assertEquals($target, Rhymix\Framework\Filters\HTMLFilter::clean($source)); $source = ''; $target = ''; - $this->assertEquals($target, Rhymix\Framework\Security\HTMLFilter::clean($source)); + $this->assertEquals($target, Rhymix\Framework\Filters\HTMLFilter::clean($source)); $source = ''; $target = ''; - $this->assertEquals($target, Rhymix\Framework\Security\HTMLFilter::clean($source)); + $this->assertEquals($target, Rhymix\Framework\Filters\HTMLFilter::clean($source)); $source = ''; $target = ''; - $this->assertEquals($target, Rhymix\Framework\Security\HTMLFilter::clean($source)); + $this->assertEquals($target, Rhymix\Framework\Filters\HTMLFilter::clean($source)); } public function testHTMLFilterEditorComponent() { $source = 'My Picture'; $target = 'My Picture'; - $this->assertEquals($target, Rhymix\Framework\Security\HTMLFilter::clean($source)); + $this->assertEquals($target, Rhymix\Framework\Filters\HTMLFilter::clean($source)); $source = ''; $target = ''; - $this->assertEquals($target, Rhymix\Framework\Security\HTMLFilter::clean($source)); + $this->assertEquals($target, Rhymix\Framework\Filters\HTMLFilter::clean($source)); } } diff --git a/tests/unit/framework/filters/IpFilterTest.php b/tests/unit/framework/filters/IpFilterTest.php new file mode 100644 index 000000000..5da4b2f97 --- /dev/null +++ b/tests/unit/framework/filters/IpFilterTest.php @@ -0,0 +1,77 @@ +assertTrue(Rhymix\Framework\Filters\IpFilter::inRange('10.0.127.191', '10.0.127.191')); + $this->assertFalse(Rhymix\Framework\Filters\IpFilter::inRange('10.1.131.177', '10.1.131.178')); + $this->assertTrue(Rhymix\Framework\Filters\IpFilter::inRange('127.0.0.1', '127.0.0.0/8')); + $this->assertFalse(Rhymix\Framework\Filters\IpFilter::inRange('172.34.0.0', '172.16.0.0/12')); + $this->assertTrue(Rhymix\Framework\Filters\IpFilter::inRange('192.168.18.214', '192.168.16.0/22')); + $this->assertFalse(Rhymix\Framework\Filters\IpFilter::inRange('192.168.18.214', '192.168.16.0/23')); + } + + public function testIPv6CIDR() + { + $this->assertTrue(Rhymix\Framework\Filters\IpFilter::inRange('::1', '::1/128')); + $this->assertFalse(Rhymix\Framework\Filters\IpFilter::inRange('::1', '::2')); + $this->assertTrue(Rhymix\Framework\Filters\IpFilter::inRange('2400:cb00::1234', '2400:cb00::/32')); + $this->assertFalse(Rhymix\Framework\Filters\IpFilter::inRange('2405:8100::1234', '2400:cb00::/32')); + } + + public function testIPv4Wildcard() + { + $this->assertTrue(Rhymix\Framework\Filters\IpFilter::inRange('192.168.134.241', '192.168.134.*')); + $this->assertTrue(Rhymix\Framework\Filters\IpFilter::inRange('192.168.134.241', '192.168.*.*')); + $this->assertFalse(Rhymix\Framework\Filters\IpFilter::inRange('192.168.134.241', '192.168.136.*')); + $this->assertFalse(Rhymix\Framework\Filters\IpFilter::inRange('192.168.134.241', '192.172.*.*')); + } + + public function testIPv4Hyphen() + { + $this->assertTrue(Rhymix\Framework\Filters\IpFilter::inRange('192.168.134.241', '192.168.134.0-192.168.134.255')); + $this->assertTrue(Rhymix\Framework\Filters\IpFilter::inRange('192.168.134.241', '192.168.128.16-192.168.145.0')); + $this->assertFalse(Rhymix\Framework\Filters\IpFilter::inRange('192.168.134.241', '192.168.134.242-192.168.244.7')); + $this->assertFalse(Rhymix\Framework\Filters\IpFilter::inRange('192.168.134.241', '192.168.100.255-192.168.133.19')); + } + + public function testValidator() + { + $this->assertTrue(Rhymix\Framework\Filters\IpFilter::validateRange('192.168.0.1')); + $this->assertTrue(Rhymix\Framework\Filters\IpFilter::validateRange('192.168.0.0/16')); + $this->assertTrue(Rhymix\Framework\Filters\IpFilter::validateRange('192.168.*.*')); + $this->assertTrue(Rhymix\Framework\Filters\IpFilter::validateRange('192.168.*')); + $this->assertTrue(Rhymix\Framework\Filters\IpFilter::validateRange('192.168.0.0-192.168.255.255')); + $this->assertTrue(Rhymix\Framework\Filters\IpFilter::validateRange('2400:cb00::/32')); + $this->assertFalse(Rhymix\Framework\Filters\IpFilter::validateRange('192.168.0.0~192.168.255.255')); + } + + public function testLegacy() + { + $this->assertTrue(\IpFilter::filter(array('192.168.134.241'), '192.168.134.241')); + $this->assertTrue(\IpFilter::filter(array('192.168.134.0-192.168.134.255'), '192.168.134.241')); + $this->assertTrue(\IpFilter::filter(array('127.0.0.1', '192.168.134.241'), '192.168.134.241')); + $this->assertTrue(\IpFilter::filter(array('192.168.134.*'), '192.168.134.241')); + $this->assertTrue(\IpFilter::filter(array('192.168.*'), '192.168.134.241')); + $this->assertFalse(\IpFilter::filter(array('127.0.0.1'), '192.168.134.241')); + } + + public function testCloudFlareRealIP() + { + $_SERVER['HTTP_CF_CONNECTING_IP'] = '192.168.134.241'; + + $_SERVER['REMOTE_ADDR'] = '192.168.10.1'; + $this->assertFalse(Rhymix\Framework\Filters\IpFilter::getCloudFlareRealIP()); + $this->assertEquals('192.168.10.1', $_SERVER['REMOTE_ADDR']); + + $_SERVER['REMOTE_ADDR'] = '108.162.192.121'; + $this->assertEquals('192.168.134.241', Rhymix\Framework\Filters\IpFilter::getCloudFlareRealIP()); + $this->assertEquals('192.168.134.241', $_SERVER['REMOTE_ADDR']); + + unset($_SERVER['HTTP_CF_CONNECTING_IP']); + $_SERVER['REMOTE_ADDR'] = '192.168.10.1'; + $this->assertFalse(Rhymix\Framework\Filters\IpFilter::getCloudFlareRealIP()); + $this->assertEquals('192.168.10.1', $_SERVER['REMOTE_ADDR']); + } +} diff --git a/tests/unit/framework/filters/MediaFilterTest.php b/tests/unit/framework/filters/MediaFilterTest.php new file mode 100644 index 000000000..1e865a240 --- /dev/null +++ b/tests/unit/framework/filters/MediaFilterTest.php @@ -0,0 +1,58 @@ +assertTrue(in_array('www.youtube.com/', Rhymix\Framework\Filters\MediaFilter::getIframeWhitelist())); + $this->assertFalse(in_array('random-website.com/', Rhymix\Framework\Filters\MediaFilter::getIframeWhitelist())); + + // iframe whitelist as regex. + $this->assertTrue(strpos(Rhymix\Framework\Filters\MediaFilter::getIframeWhitelistRegex(), '|www\.youtube\.com/') !== false); + $this->assertFalse(strpos(Rhymix\Framework\Filters\MediaFilter::getIframeWhitelistRegex(), 'www.youtube.com/') !== false); + + // Match individual URL against iframe whitelist. + $this->assertTrue(Rhymix\Framework\Filters\MediaFilter::matchIframeWhitelist('https://www.youtube.com/v')); + $this->assertFalse(Rhymix\Framework\Filters\MediaFilter::matchIframeWhitelist('http://www-youtube.com/v')); + + // object whitelist as array. + $this->assertTrue(in_array('www.youtube.com/', Rhymix\Framework\Filters\MediaFilter::getObjectWhitelist())); + $this->assertFalse(in_array('random-website.com/', Rhymix\Framework\Filters\MediaFilter::getObjectWhitelist())); + + // object whitelist as regex. + $this->assertTrue(strpos(Rhymix\Framework\Filters\MediaFilter::getObjectWhitelistRegex(), '|www\.youtube\.com/') !== false); + $this->assertFalse(strpos(Rhymix\Framework\Filters\MediaFilter::getObjectWhitelistRegex(), 'www.youtube.com/') !== false); + + // Match individual URL against object whitelist. + $this->assertTrue(Rhymix\Framework\Filters\MediaFilter::matchObjectWhitelist('https://www.youtube.com/v')); + $this->assertFalse(Rhymix\Framework\Filters\MediaFilter::matchObjectWhitelist('http://www-youtube.com/v')); + } + + public function testAddPrefix() + { + $this->assertFalse(Rhymix\Framework\Filters\MediaFilter::matchIframeWhitelist('http://some.custom.website.com/video.mp4')); + Rhymix\Framework\Filters\MediaFilter::addIframePrefix('*.custom.website.com/'); + $this->assertTrue(Rhymix\Framework\Filters\MediaFilter::matchIframeWhitelist('http://some.custom.website.com/video.mp4')); + + $this->assertFalse(Rhymix\Framework\Filters\MediaFilter::matchObjectWhitelist('http://some.custom.website.com/video.mp4')); + Rhymix\Framework\Filters\MediaFilter::addObjectPrefix('*.custom.website.com/'); + $this->assertTrue(Rhymix\Framework\Filters\MediaFilter::matchObjectWhitelist('http://some.custom.website.com/video.mp4')); + } + + public function testRemoveEmbeddedMedia() + { + $tests = array( + '
' => '
', + '
' => '
', + '
' => '
', + '
' => '
', + '
' => '
', + ); + + foreach ($tests as $from => $to) + { + $this->assertEquals($to, Rhymix\Framework\Filters\MediaFilter::removeEmbeddedMedia($from)); + } + } +} diff --git a/tests/unit/framework/security/IpFilterTest.php b/tests/unit/framework/security/IpFilterTest.php deleted file mode 100644 index 235939253..000000000 --- a/tests/unit/framework/security/IpFilterTest.php +++ /dev/null @@ -1,77 +0,0 @@ -assertTrue(Rhymix\Framework\Security\IpFilter::inRange('10.0.127.191', '10.0.127.191')); - $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('10.1.131.177', '10.1.131.178')); - $this->assertTrue(Rhymix\Framework\Security\IpFilter::inRange('127.0.0.1', '127.0.0.0/8')); - $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('172.34.0.0', '172.16.0.0/12')); - $this->assertTrue(Rhymix\Framework\Security\IpFilter::inRange('192.168.18.214', '192.168.16.0/22')); - $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('192.168.18.214', '192.168.16.0/23')); - } - - public function testIPv6CIDR() - { - $this->assertTrue(Rhymix\Framework\Security\IpFilter::inRange('::1', '::1/128')); - $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('::1', '::2')); - $this->assertTrue(Rhymix\Framework\Security\IpFilter::inRange('2400:cb00::1234', '2400:cb00::/32')); - $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('2405:8100::1234', '2400:cb00::/32')); - } - - public function testIPv4Wildcard() - { - $this->assertTrue(Rhymix\Framework\Security\IpFilter::inRange('192.168.134.241', '192.168.134.*')); - $this->assertTrue(Rhymix\Framework\Security\IpFilter::inRange('192.168.134.241', '192.168.*.*')); - $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('192.168.134.241', '192.168.136.*')); - $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('192.168.134.241', '192.172.*.*')); - } - - public function testIPv4Hyphen() - { - $this->assertTrue(Rhymix\Framework\Security\IpFilter::inRange('192.168.134.241', '192.168.134.0-192.168.134.255')); - $this->assertTrue(Rhymix\Framework\Security\IpFilter::inRange('192.168.134.241', '192.168.128.16-192.168.145.0')); - $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('192.168.134.241', '192.168.134.242-192.168.244.7')); - $this->assertFalse(Rhymix\Framework\Security\IpFilter::inRange('192.168.134.241', '192.168.100.255-192.168.133.19')); - } - - public function testValidator() - { - $this->assertTrue(Rhymix\Framework\Security\IpFilter::validateRange('192.168.0.1')); - $this->assertTrue(Rhymix\Framework\Security\IpFilter::validateRange('192.168.0.0/16')); - $this->assertTrue(Rhymix\Framework\Security\IpFilter::validateRange('192.168.*.*')); - $this->assertTrue(Rhymix\Framework\Security\IpFilter::validateRange('192.168.*')); - $this->assertTrue(Rhymix\Framework\Security\IpFilter::validateRange('192.168.0.0-192.168.255.255')); - $this->assertTrue(Rhymix\Framework\Security\IpFilter::validateRange('2400:cb00::/32')); - $this->assertFalse(Rhymix\Framework\Security\IpFilter::validateRange('192.168.0.0~192.168.255.255')); - } - - public function testLegacy() - { - $this->assertTrue(\IpFilter::filter(array('192.168.134.241'), '192.168.134.241')); - $this->assertTrue(\IpFilter::filter(array('192.168.134.0-192.168.134.255'), '192.168.134.241')); - $this->assertTrue(\IpFilter::filter(array('127.0.0.1', '192.168.134.241'), '192.168.134.241')); - $this->assertTrue(\IpFilter::filter(array('192.168.134.*'), '192.168.134.241')); - $this->assertTrue(\IpFilter::filter(array('192.168.*'), '192.168.134.241')); - $this->assertFalse(\IpFilter::filter(array('127.0.0.1'), '192.168.134.241')); - } - - public function testCloudFlareRealIP() - { - $_SERVER['HTTP_CF_CONNECTING_IP'] = '192.168.134.241'; - - $_SERVER['REMOTE_ADDR'] = '192.168.10.1'; - $this->assertFalse(Rhymix\Framework\Security\IpFilter::getCloudFlareRealIP()); - $this->assertEquals('192.168.10.1', $_SERVER['REMOTE_ADDR']); - - $_SERVER['REMOTE_ADDR'] = '108.162.192.121'; - $this->assertEquals('192.168.134.241', Rhymix\Framework\Security\IpFilter::getCloudFlareRealIP()); - $this->assertEquals('192.168.134.241', $_SERVER['REMOTE_ADDR']); - - unset($_SERVER['HTTP_CF_CONNECTING_IP']); - $_SERVER['REMOTE_ADDR'] = '192.168.10.1'; - $this->assertFalse(Rhymix\Framework\Security\IpFilter::getCloudFlareRealIP()); - $this->assertEquals('192.168.10.1', $_SERVER['REMOTE_ADDR']); - } -} diff --git a/tests/unit/framework/security/MediaFilterTest.php b/tests/unit/framework/security/MediaFilterTest.php deleted file mode 100644 index 404bc0a4c..000000000 --- a/tests/unit/framework/security/MediaFilterTest.php +++ /dev/null @@ -1,58 +0,0 @@ -assertTrue(in_array('www.youtube.com/', Rhymix\Framework\Security\MediaFilter::getIframeWhitelist())); - $this->assertFalse(in_array('random-website.com/', Rhymix\Framework\Security\MediaFilter::getIframeWhitelist())); - - // iframe whitelist as regex. - $this->assertTrue(strpos(Rhymix\Framework\Security\MediaFilter::getIframeWhitelistRegex(), '|www\.youtube\.com/') !== false); - $this->assertFalse(strpos(Rhymix\Framework\Security\MediaFilter::getIframeWhitelistRegex(), 'www.youtube.com/') !== false); - - // Match individual URL against iframe whitelist. - $this->assertTrue(Rhymix\Framework\Security\MediaFilter::matchIframeWhitelist('https://www.youtube.com/v')); - $this->assertFalse(Rhymix\Framework\Security\MediaFilter::matchIframeWhitelist('http://www-youtube.com/v')); - - // object whitelist as array. - $this->assertTrue(in_array('www.youtube.com/', Rhymix\Framework\Security\MediaFilter::getObjectWhitelist())); - $this->assertFalse(in_array('random-website.com/', Rhymix\Framework\Security\MediaFilter::getObjectWhitelist())); - - // object whitelist as regex. - $this->assertTrue(strpos(Rhymix\Framework\Security\MediaFilter::getObjectWhitelistRegex(), '|www\.youtube\.com/') !== false); - $this->assertFalse(strpos(Rhymix\Framework\Security\MediaFilter::getObjectWhitelistRegex(), 'www.youtube.com/') !== false); - - // Match individual URL against object whitelist. - $this->assertTrue(Rhymix\Framework\Security\MediaFilter::matchObjectWhitelist('https://www.youtube.com/v')); - $this->assertFalse(Rhymix\Framework\Security\MediaFilter::matchObjectWhitelist('http://www-youtube.com/v')); - } - - public function testAddPrefix() - { - $this->assertFalse(Rhymix\Framework\Security\MediaFilter::matchIframeWhitelist('http://some.custom.website.com/video.mp4')); - Rhymix\Framework\Security\MediaFilter::addIframePrefix('*.custom.website.com/'); - $this->assertTrue(Rhymix\Framework\Security\MediaFilter::matchIframeWhitelist('http://some.custom.website.com/video.mp4')); - - $this->assertFalse(Rhymix\Framework\Security\MediaFilter::matchObjectWhitelist('http://some.custom.website.com/video.mp4')); - Rhymix\Framework\Security\MediaFilter::addObjectPrefix('*.custom.website.com/'); - $this->assertTrue(Rhymix\Framework\Security\MediaFilter::matchObjectWhitelist('http://some.custom.website.com/video.mp4')); - } - - public function testRemoveEmbeddedMedia() - { - $tests = array( - '
' => '
', - '
' => '
', - '
' => '
', - '
' => '
', - '
' => '
', - ); - - foreach ($tests as $from => $to) - { - $this->assertEquals($to, Rhymix\Framework\Security\MediaFilter::removeEmbeddedMedia($from)); - } - } -} From 3e728dc6c24e223568c7a42e152270495524014f Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Mon, 14 Mar 2016 22:13:19 +0900 Subject: [PATCH 093/205] Exclude more attributes from widget and editor component detection --- common/framework/filters/htmlfilter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/framework/filters/htmlfilter.php b/common/framework/filters/htmlfilter.php index 64356a8e8..4009d940f 100644 --- a/common/framework/filters/htmlfilter.php +++ b/common/framework/filters/htmlfilter.php @@ -449,11 +449,11 @@ class HTMLFilter $attrs = array(); $html = preg_replace_callback('!([a-zA-Z0-9_-]+)="([^"]+)"!', function($attr) use($tag, &$attrs) { $attrkey = strtolower($attr[1]); - if ($tag === 'img' && preg_match('/^(?:width|height|alt)$/', $attrkey)) + if ($tag === 'img' && preg_match('/^(?:width|height|src|alt|ismap|usemap)$/', $attrkey)) { return $attr[0]; } - if (preg_match('/^(?:on|data-|(?:src|style|class|editor_component)$)/', $attrkey)) + if (preg_match('/^(?:on|data-|(?:accesskey|class|contextmenu|contenteditable|dir|draggable|dropzone|editor_component|hidden|id|lang|name|style|tabindex|title)$)/', $attrkey)) { return $attr[0]; } From 93b045fa17d8496523075c29c2a0e8fc7c938822 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 15 Mar 2016 09:59:38 +0900 Subject: [PATCH 094/205] Fix incorrect use of global constant in Debug class --- common/framework/debug.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/framework/debug.php b/common/framework/debug.php index 0fd10d846..d606f1723 100644 --- a/common/framework/debug.php +++ b/common/framework/debug.php @@ -445,7 +445,7 @@ class Debug return $cache = true; case 'ip': - if (Filters\IpFilter::inRanges(RX_CLIENT_IP, Config::get('debug.allow'))) + if (Filters\IpFilter::inRanges(\RX_CLIENT_IP, Config::get('debug.allow'))) { return $cache = true; } From f4b35e3717212698150e0d7bc8c9cec3906382db Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 15 Mar 2016 10:06:33 +0900 Subject: [PATCH 095/205] Fix typo --- modules/admin/admin.admin.controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/admin/admin.admin.controller.php b/modules/admin/admin.admin.controller.php index deebc7ed0..20a4bab37 100644 --- a/modules/admin/admin.admin.controller.php +++ b/modules/admin/admin.admin.controller.php @@ -745,7 +745,7 @@ class adminAdminController extends admin $allowed_ip = array_unique(array_filter($allowed_ip, function($item) { return $item !== ''; })); - if (!Rhymix\Framework\Filters\IpFilter::validate($allowed_ip)) { + if (!Rhymix\Framework\Filters\IpFilter::validateRanges($allowed_ip)) { return new Object(-1, 'msg_invalid_ip'); } Rhymix\Framework\Config::set('debug.allow', array_values($allowed_ip)); From a6a1b25f2d7c2d6875d285cae88e78b3ec4c8df0 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 15 Mar 2016 10:21:40 +0900 Subject: [PATCH 096/205] Always allow debugging from localhost --- common/framework/debug.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/common/framework/debug.php b/common/framework/debug.php index d606f1723..bf2227904 100644 --- a/common/framework/debug.php +++ b/common/framework/debug.php @@ -449,6 +449,14 @@ class Debug { return $cache = true; } + if (\RX_CLIENT_IP === '127.0.0.1' || \RX_CLIENT_IP === '::1') + { + return $cache = true; + } + if (\RX_CLIENT_IP === $_SERVER['SERVER_ADDR'] || \RX_CLIENT_IP === $_SERVER['LOCAL_ADDR']) + { + return $cache = true; + } return $cache = false; case 'admin': From 7298adf8555a9256a6cefa3b16cafc1bdb7e0f93 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 15 Mar 2016 10:57:57 +0900 Subject: [PATCH 097/205] Include visitor IP address in debug output --- common/tpl/debug_comment.html | 1 + 1 file changed, 1 insertion(+) diff --git a/common/tpl/debug_comment.html b/common/tpl/debug_comment.html index ca05800df..3e9741aee 100644 --- a/common/tpl/debug_comment.html +++ b/common/tpl/debug_comment.html @@ -6,6 +6,7 @@ Request / Response ================== Request URL: url . "\n"; ?> +Request IP Address: Request Method: request->method . "\n" ?> Request Body Size: request->size . "\n" ?> Response Method: response->method . "\n"; ?> From 468fcc88deeff3782def9dff872b2a511291c35b Mon Sep 17 00:00:00 2001 From: BJRambo Date: Tue, 15 Mar 2016 13:26:09 +0900 Subject: [PATCH 098/205] rename query id --- modules/document/queries/getUpdateLogAdminisExists.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/document/queries/getUpdateLogAdminisExists.xml b/modules/document/queries/getUpdateLogAdminisExists.xml index 7074c565c..5c494fc5d 100644 --- a/modules/document/queries/getUpdateLogAdminisExists.xml +++ b/modules/document/queries/getUpdateLogAdminisExists.xml @@ -1,4 +1,4 @@ - + From 9d2fe0270bc5322441fd75e5ce9249bf8b277dfb Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 15 Mar 2016 13:48:07 +0900 Subject: [PATCH 099/205] Add URL-to-path and clean path conversion --- common/framework/filters/filenamefilter.php | 20 ++++++ common/framework/url.php | 69 +++++++++++++++++-- tests/unit/framework/URLTest.php | 35 ++++++++++ .../framework/filters/FilenameFilterTest.php | 24 ++++++- 4 files changed, 140 insertions(+), 8 deletions(-) diff --git a/common/framework/filters/filenamefilter.php b/common/framework/filters/filenamefilter.php index 42f5a729c..a498f2a98 100644 --- a/common/framework/filters/filenamefilter.php +++ b/common/framework/filters/filenamefilter.php @@ -46,4 +46,24 @@ class FilenameFilter return $filename; } + + /** + * Clean a path to remove ./, ../, trailing slashes, etc. + * + * @param string $path + * @return string + */ + public static function cleanPath($path) + { + $path = str_replace('\\', '/', $path); + $path = preg_replace('@[\?#].+$@', '', $path); + $path = preg_replace('@/{2,}@', '/', $path); + $path = preg_replace('@/\.{3,}/@', '/', $path); + $path = preg_replace('@/(\./)+@', '/', $path); + while (preg_match('@/[^/]+/\.\.(?:/|$)@', $path, $matches)) + { + $path = str_replace($matches[0], '/', $path); + } + return rtrim($path, '/'); + } } diff --git a/common/framework/url.php b/common/framework/url.php index 8fc370bdb..f810c0082 100644 --- a/common/framework/url.php +++ b/common/framework/url.php @@ -18,10 +18,7 @@ class URL */ public static function getCurrentURL(array $changes = array()) { - $proto = \RX_SSL ? 'https://' : 'http://'; - $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost'; - $local = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/'; - $url = $proto . $host . $local; + $url = self::getCurrentDomainURL(isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/'); if (count($changes)) { return self::modifyURL($url, $changes); @@ -32,6 +29,19 @@ class URL } } + /** + * Get the current domain. + * + * @param string $path + * @return string + */ + public static function getCurrentDomainURL($path = '/') + { + $proto = \RX_SSL ? 'https://' : 'http://'; + $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost'; + return $proto . $host . '/' . ltrim($path, '/'); + } + /** * Convert a URL to its canonical format. * @@ -42,9 +52,7 @@ class URL { if (preg_match('#^\.?/([^/]|$)#', $url) || !preg_match('#^(https?:|/)#', $url)) { - $proto = \RX_SSL ? 'https://' : 'http://'; - $host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost'; - $url = $proto . $host . \RX_BASEURL . ltrim($url, './'); + $url = self::getCurrentDomainURL(\RX_BASEURL . ltrim($url, './')); } return preg_replace_callback('#^(https?:|)//([^/]+)#i', function($matches) { if ($matches[1] === '') $matches[1] = \RX_SSL ? 'https:' : 'http:'; @@ -125,6 +133,53 @@ class URL } } + /** + * Convert a server-side path to a URL. + * + * This method returns false if the path cannot be converted to a URL, + * e.g. if the path is outside of the document root. + * + * @param string $path + * @return string|false + */ + public static function fromServerPath($path) + { + $cleanpath = Filters\FilenameFilter::cleanPath($path); + if (substr($path, -1) === '/') + { + $cleanpath .= '/'; + } + $root = Filters\FilenameFilter::cleanPath($_SERVER['DOCUMENT_ROOT']); + if ($cleanpath === $root) + { + return self::getCurrentDomainURL('/'); + } + if (starts_with($root . '/', $cleanpath)) + { + return self::getCurrentDomainURL(substr($cleanpath, strlen($root))); + } + return false; + } + + /** + * Convert a URL to a server-side path. + * + * This method returns false if the URL cannot be converted to a server-side path, + * e.g. if the URL belongs to an external domain. + * + * @param string $url + * @return string + */ + public static function toServerPath($url) + { + $url = self::getCanonicalURL($url); + if (!self::isInternalURL($url)) + { + return false; + } + return Filters\FilenameFilter::cleanPath($_SERVER['DOCUMENT_ROOT'] . parse_url($url, \PHP_URL_PATH)); + } + /** * Encode UTF-8 domain into IDNA (punycode) * diff --git a/tests/unit/framework/URLTest.php b/tests/unit/framework/URLTest.php index d36dd5fe6..2faaac28f 100644 --- a/tests/unit/framework/URLTest.php +++ b/tests/unit/framework/URLTest.php @@ -25,6 +25,17 @@ class URLTest extends \Codeception\TestCase\Test $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php?xe=sucks&l=ko', Rhymix\Framework\URL::getCurrentURL(array('l' => 'ko', 'foo' => null))); } + public function testGetCurrentDomainURL() + { + $protocol = \RX_SSL ? 'https://' : 'http://'; + $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; + + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/', Rhymix\Framework\URL::getCurrentDomainURL()); + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/', Rhymix\Framework\URL::getCurrentDomainURL('/')); + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/foo/bar', Rhymix\Framework\URL::getCurrentDomainURL('/foo/bar')); + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php?foo=bar', Rhymix\Framework\URL::getCurrentDomainURL('index.php?foo=bar')); + } + public function testGetCanonicalURL() { $protocol = \RX_SSL ? 'https://' : 'http://'; @@ -82,6 +93,30 @@ class URLTest extends \Codeception\TestCase\Test // This function is checked in Security::checkCSRF() } + public function testURLFromServerPath() + { + $protocol = \RX_SSL ? 'https://' : 'http://'; + $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; + + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL, Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR)); + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . 'index.php')); + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'foo/bar', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . '/foo/bar')); + $this->assertEquals(false, Rhymix\Framework\URL::fromServerPath('C:/Windows')); + } + + public function testURLToServerPath() + { + $protocol = \RX_SSL ? 'https://' : 'http://'; + $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; + + $this->assertEquals(\RX_BASEDIR . 'index.php', Rhymix\Framework\URL::toServerPath($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php')); + $this->assertEquals(\RX_BASEDIR . 'foo/bar', Rhymix\Framework\URL::toServerPath($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . '/foo/bar?arg=baz')); + $this->assertEquals(\RX_BASEDIR . 'foo/bar', Rhymix\Framework\URL::toServerPath('./foo/bar')); + $this->assertEquals(\RX_BASEDIR . 'foo/bar', Rhymix\Framework\URL::toServerPath('foo/bar/../bar')); + $this->assertEquals(false, Rhymix\Framework\URL::toServerPath('http://other.domain.com/')); + $this->assertEquals(false, Rhymix\Framework\URL::toServerPath('//other.domain.com/')); + } + public function testEncodeIdna() { $this->assertEquals('xn--9i1bl3b186bf9e.xn--3e0b707e', Rhymix\Framework\URL::encodeIdna('퓨니코드.한국')); diff --git a/tests/unit/framework/filters/FilenameFilterTest.php b/tests/unit/framework/filters/FilenameFilterTest.php index 7fe095d35..7d1bf117a 100644 --- a/tests/unit/framework/filters/FilenameFilterTest.php +++ b/tests/unit/framework/filters/FilenameFilterTest.php @@ -1,5 +1,7 @@ $to) { - $result = Rhymix\Framework\Filters\FilenameFilter::clean($from); + $result = FilenameFilter::clean($from); $this->assertEquals($to, $result); } } + + public function testFilenameFilterCleanPath() + { + // Remove extra dots and slashes. + $this->assertEquals('/usr/share/foo/bar.jpg', FilenameFilter::cleanPath('/usr/share/foo//./baz/../bar.jpg')); + $this->assertEquals('/usr/share/foo/bar.jpg', FilenameFilter::cleanPath('/usr/share/foo/././baz/../../foo/bar.jpg')); + $this->assertEquals('/usr/share', FilenameFilter::cleanPath('/usr/share/foo/..')); + $this->assertEquals('/usr/share', FilenameFilter::cleanPath('/usr/share/foo/bar/../baz/../../')); + + // Test Windows paths. + $this->assertEquals('C:/Windows/Notepad.exe', FilenameFilter::cleanPath('C:\\Windows\\System32\\..\\Notepad.exe')); + + // Do not remove .. if there is no parent directory. + $this->assertEquals('C:/../foobar', FilenameFilter::cleanPath('C:\\..\foobar\\')); + $this->assertEquals('/../foobar', FilenameFilter::cleanPath('/../foobar/')); + + // Remove query strings and URL fragments. + $this->assertEquals('index.php', FilenameFilter::cleanPath('index.php?foo=bar')); + $this->assertEquals('index.php', FilenameFilter::cleanPath('index.php#baz')); + } } From f983335833057b2fcdb4f3593764fd39dd88741a Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 15 Mar 2016 13:54:33 +0900 Subject: [PATCH 100/205] Add function aliases for URL and path conversion --- common/functions.php | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/common/functions.php b/common/functions.php index 154d60455..6840cdcaa 100644 --- a/common/functions.php +++ b/common/functions.php @@ -133,6 +133,19 @@ function class_basename($class) return basename(str_replace('\\', '/', is_object($class) ? get_class($class) : $class)); } +/** + * Clean a path to remove ./, ../, trailing slashes, etc. + * + * This function is an alias to Rhymix\Framework\Filters\FilenameFilter::cleanPath(). + * + * @param string $path + * @return string + */ +function clean_path($path) +{ + return Rhymix\Framework\Filters\FilenameFilter::cleanPath($path); +} + /** * This function is a shortcut to htmlspecialchars(). * @@ -337,6 +350,34 @@ function base64_decode_urlsafe($str) return @base64_decode(str_pad(strtr($str, '-_', '+/'), ceil(strlen($str) / 4) * 4, '=', STR_PAD_RIGHT)); } +/** + * Convert a server-side path to a URL. + * + * This function is an alias to Rhymix\Framework\URL::fromServerPath(). + * It returns false if the path cannot be converted. + * + * @param string $path + * @return string|false + */ +function path2url($path) +{ + return Rhymix\Framework\URL::fromServerPath($path); +} + +/** + * Convert a URL to a server-side path. + * + * This function is an alias to Rhymix\Framework\URL::toServerPath(). + * It returns false if the URL cannot be converted. + * + * @param string $url + * @return string|false + */ +function url2path($url) +{ + return Rhymix\Framework\URL::toServerPath($url); +} + /** * Convert hexadecimal color codes to an array of R, G, B values. * This function can handle both 6-digit and 3-digit notations, optionally prefixed with '#'. From 139e11df13f0beec2d9fd1ba37a7a8822278ca45 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 15 Mar 2016 14:14:50 +0900 Subject: [PATCH 101/205] Also clean relative paths in cleanPath() --- common/framework/filters/filenamefilter.php | 4 ++++ common/framework/storage.php | 11 +++++++++++ 2 files changed, 15 insertions(+) create mode 100644 common/framework/storage.php diff --git a/common/framework/filters/filenamefilter.php b/common/framework/filters/filenamefilter.php index a498f2a98..becd5e3f7 100644 --- a/common/framework/filters/filenamefilter.php +++ b/common/framework/filters/filenamefilter.php @@ -55,6 +55,10 @@ class FilenameFilter */ public static function cleanPath($path) { + if (!preg_match('@^(?:[a-z]:[\\\\/]|\\\\|/)@i', $path)) + { + $path = \RX_BASEDIR . $path; + } $path = str_replace('\\', '/', $path); $path = preg_replace('@[\?#].+$@', '', $path); $path = preg_replace('@/{2,}@', '/', $path); diff --git a/common/framework/storage.php b/common/framework/storage.php new file mode 100644 index 000000000..4fee32b08 --- /dev/null +++ b/common/framework/storage.php @@ -0,0 +1,11 @@ + Date: Tue, 15 Mar 2016 14:15:03 +0900 Subject: [PATCH 102/205] Update unit tests --- tests/unit/framework/URLTest.php | 7 ++++--- tests/unit/framework/filters/FilenameFilterTest.php | 8 ++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/unit/framework/URLTest.php b/tests/unit/framework/URLTest.php index 2faaac28f..f3d5befb3 100644 --- a/tests/unit/framework/URLTest.php +++ b/tests/unit/framework/URLTest.php @@ -98,9 +98,10 @@ class URLTest extends \Codeception\TestCase\Test $protocol = \RX_SSL ? 'https://' : 'http://'; $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL, Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR)); - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . 'index.php')); - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'foo/bar', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . '/foo/bar')); + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR)); + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . 'index.php')); + $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/foo/bar', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . '/foo/bar')); + $this->assertEquals(false, Rhymix\Framework\URL::fromServerPath(dirname(dirname(\RX_BASEDIR)))); $this->assertEquals(false, Rhymix\Framework\URL::fromServerPath('C:/Windows')); } diff --git a/tests/unit/framework/filters/FilenameFilterTest.php b/tests/unit/framework/filters/FilenameFilterTest.php index 7d1bf117a..562bac0cc 100644 --- a/tests/unit/framework/filters/FilenameFilterTest.php +++ b/tests/unit/framework/filters/FilenameFilterTest.php @@ -50,6 +50,10 @@ class FilenameFilterTest extends \Codeception\TestCase\Test $this->assertEquals('/usr/share', FilenameFilter::cleanPath('/usr/share/foo/..')); $this->assertEquals('/usr/share', FilenameFilter::cleanPath('/usr/share/foo/bar/../baz/../../')); + // Test internal paths. + $this->assertEquals(\RX_BASEDIR . 'common/js/debug.js', FilenameFilter::cleanPath('common/js/debug.js')); + $this->assertEquals(\RX_BASEDIR . 'common/js/debug.js', FilenameFilter::cleanPath('./common/js/debug.js')); + // Test Windows paths. $this->assertEquals('C:/Windows/Notepad.exe', FilenameFilter::cleanPath('C:\\Windows\\System32\\..\\Notepad.exe')); @@ -58,7 +62,7 @@ class FilenameFilterTest extends \Codeception\TestCase\Test $this->assertEquals('/../foobar', FilenameFilter::cleanPath('/../foobar/')); // Remove query strings and URL fragments. - $this->assertEquals('index.php', FilenameFilter::cleanPath('index.php?foo=bar')); - $this->assertEquals('index.php', FilenameFilter::cleanPath('index.php#baz')); + $this->assertEquals(\RX_BASEDIR . 'index.php', FilenameFilter::cleanPath('index.php?foo=bar')); + $this->assertEquals(\RX_BASEDIR . 'index.php', FilenameFilter::cleanPath('index.php#baz')); } } From ff0df0a14b1346945d89d219a7cbab92ddfc1900 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 15 Mar 2016 14:41:21 +0900 Subject: [PATCH 103/205] Ensure proper handling of network shares and URLs in cleanPath() --- common/framework/filters/filenamefilter.php | 19 +++++++++++++++++-- .../framework/filters/FilenameFilterTest.php | 5 +++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/common/framework/filters/filenamefilter.php b/common/framework/filters/filenamefilter.php index becd5e3f7..dc5fee4eb 100644 --- a/common/framework/filters/filenamefilter.php +++ b/common/framework/filters/filenamefilter.php @@ -55,19 +55,34 @@ class FilenameFilter */ public static function cleanPath($path) { - if (!preg_match('@^(?:[a-z]:[\\\\/]|\\\\|/)@i', $path)) + // Convert relative paths to absolute paths. + if (!preg_match('@^(?:/|[a-z]:[\\\\/]|\\\\|https?:)@i', $path)) { $path = \RX_BASEDIR . $path; } + + // Convert backslashes to forward slashes. $path = str_replace('\\', '/', $path); + + // Remove querystrings and URL fragments. $path = preg_replace('@[\?#].+$@', '', $path); - $path = preg_replace('@/{2,}@', '/', $path); + + // Remove duplicate slashes, except at the beginning of a URL. + $path = preg_replace('@(?assertEquals('C:/Windows/Notepad.exe', FilenameFilter::cleanPath('C:\\Windows\\System32\\..\\Notepad.exe')); + $this->assertEquals('//vboxsrv/hello/world', FilenameFilter::cleanPath('\\\\vboxsrv\\hello\\world')); + + // Test absolute URLs. + $this->assertEquals('https://www.rhymix.org/foo/bar', FilenameFilter::cleanPath('https://www.rhymix.org/foo/.//bar')); + $this->assertEquals('//www.rhymix.org/foo/bar', FilenameFilter::cleanPath('//www.rhymix.org/foo/.//bar')); // Do not remove .. if there is no parent directory. $this->assertEquals('C:/../foobar', FilenameFilter::cleanPath('C:\\..\foobar\\')); From b088348be2c0899d8d463d992e6ffe9c9ed98cc7 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Tue, 15 Mar 2016 18:05:48 +0900 Subject: [PATCH 104/205] More explicitly exclude a series of double dots --- common/framework/filters/filenamefilter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/framework/filters/filenamefilter.php b/common/framework/filters/filenamefilter.php index dc5fee4eb..a43fb8ba2 100644 --- a/common/framework/filters/filenamefilter.php +++ b/common/framework/filters/filenamefilter.php @@ -77,7 +77,7 @@ class FilenameFilter $path = preg_replace('@/(\./)+@', '/', $path); // Remove double dots and the preceding directory. - while (preg_match('@/[^/]+/\.\.(?:/|$)@', $path, $matches)) + while (preg_match('@/(?!\.\.)[^/]+/\.\.(?:/|$)@', $path, $matches)) { $path = str_replace($matches[0], '/', $path); } From ca7a7b64eaf98a74c12a06990116d220a98567b9 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 16 Mar 2016 09:13:31 +0900 Subject: [PATCH 105/205] Use a more realistic server environment in unit tests --- tests/_bootstrap.php | 15 +++- tests/unit/_bootstrap.php | 1 - tests/unit/classes/ContextTest.php | 3 - tests/unit/classes/FileHandlerTest.php | 1 - .../unit/classes/FrontEndFileHandlerTest.php | 53 +++++++-------- tests/unit/classes/OldSecurityTest.php | 1 - tests/unit/classes/TemplateHandlerTest.php | 35 +++++----- tests/unit/framework/SecurityTest.php | 2 - tests/unit/framework/URLTest.php | 68 +++++++------------ 9 files changed, 81 insertions(+), 98 deletions(-) diff --git a/tests/_bootstrap.php b/tests/_bootstrap.php index 529a86e6c..8017ed216 100644 --- a/tests/_bootstrap.php +++ b/tests/_bootstrap.php @@ -1,6 +1,17 @@ loadFile(array('./common/js/common.js', 'body')); $handler->loadFile(array('./common/js/common.js', 'head')); $handler->loadFile(array('./common/js/xml_js_filter.js', 'body')); - $expected[] = array('file' => '/xe/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); $this->assertEquals($handler->getJsFileList(), $expected); }); @@ -39,8 +38,8 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler = new FrontEndFileHandler(); $handler->loadFile(array('./common/css/xe.css')); $handler->loadFile(array('./common/css/mobile.css')); - $expected[] = array('file' => '/xe/common/css/xe.css' . $this->_filemtime('common/css/xe.css'), 'media' => 'all', 'targetie' => null); - $expected[] = array('file' => '/xe/common/css/mobile.css' . $this->_filemtime('common/css/mobile.css'), 'media' => 'all', 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/css/xe.css' . $this->_filemtime('common/css/xe.css'), 'media' => 'all', 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/css/mobile.css' . $this->_filemtime('common/css/mobile.css'), 'media' => 'all', 'targetie' => null); $this->assertEquals($handler->getCssFileList(), $expected); }); @@ -54,10 +53,10 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler->loadFile(array('./common/js/common.js', 'head', '', -100000)); $handler->loadFile(array('./common/js/xml_handler.js', 'head', '', -100000)); $handler->loadFile(array('./common/js/xml_js_filter.js', 'head', '', -100000)); - $expected[] = array('file' => '/xe/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/xml_handler.js' . $this->_filemtime('common/js/xml_handler.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/xml_js_filter.js' . $this->_filemtime('common/js/xml_js_filter.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/xml_handler.js' . $this->_filemtime('common/js/xml_handler.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/xml_js_filter.js' . $this->_filemtime('common/js/xml_js_filter.js'), 'targetie' => null); $this->assertEquals($handler->getJsFileList(), $expected); }); @@ -67,10 +66,10 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler->loadFile(array('./common/js/js_app.js', 'head', '', -100000)); $handler->loadFile(array('./common/js/common.js', 'head', '', -100000)); $handler->loadFile(array('./common/js/xml_js_filter.js', 'head', '', -100000)); - $expected[] = array('file' => '/xe/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/xml_js_filter.js' . $this->_filemtime('common/js/xml_js_filter.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/xml_handler.js' . $this->_filemtime('common/js/xml_handler.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/xml_js_filter.js' . $this->_filemtime('common/js/xml_js_filter.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/xml_handler.js' . $this->_filemtime('common/js/xml_handler.js'), 'targetie' => null); $this->assertEquals($handler->getJsFileList(), $expected); }); @@ -81,9 +80,9 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler->loadFile(array('./common/js/xml_handler.js', 'head', '', -100000)); $handler->loadFile(array('./common/js/xml_js_filter.js', 'head', '', -100000)); $handler->unloadFile('./common/js/js_app.js', '', 'all'); - $expected[] = array('file' => '/xe/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/xml_handler.js' . $this->_filemtime('common/js/xml_handler.js'), 'targetie' => null); - $expected[] = array('file' => '/xe/common/js/xml_js_filter.js' . $this->_filemtime('common/js/xml_js_filter.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/common.js' . $this->_filemtime('common/js/common.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/xml_handler.js' . $this->_filemtime('common/js/xml_handler.js'), 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/js/xml_js_filter.js' . $this->_filemtime('common/js/xml_js_filter.js'), 'targetie' => null); $this->assertEquals($handler->getJsFileList(), $expected); }); @@ -92,9 +91,9 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler->loadFile(array('./common/js/js_app.js', 'head', 'ie6')); $handler->loadFile(array('./common/js/js_app.js', 'head', 'ie7')); $handler->loadFile(array('./common/js/js_app.js', 'head', 'ie8')); - $expected[] = array('file' => '/xe/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => 'ie6'); - $expected[] = array('file' => '/xe/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => 'ie7'); - $expected[] = array('file' => '/xe/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => 'ie8'); + $expected[] = array('file' => '/rhymix/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => 'ie6'); + $expected[] = array('file' => '/rhymix/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => 'ie7'); + $expected[] = array('file' => '/rhymix/common/js/js_app.js' . $this->_filemtime('common/js/js_app.js'), 'targetie' => 'ie8'); $this->assertEquals($handler->getJsFileList(), $expected); }); @@ -127,9 +126,9 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler->loadFile(array('./common/css/common.css', null, 'ie7')); $handler->loadFile(array('./common/css/common.css', null, 'ie8')); - $expected[] = array('file' => '/xe/common/css/common.css', 'media'=>'all', 'targetie' => 'ie6'); - $expected[] = array('file' => '/xe/common/css/common.css','media'=>'all', 'targetie' => 'ie7'); - $expected[] = array('file' => '/xe/common/css/common.css', 'media'=>'all', 'targetie' => 'ie8'); + $expected[] = array('file' => '/rhymix/common/css/common.css', 'media'=>'all', 'targetie' => 'ie6'); + $expected[] = array('file' => '/rhymix/common/css/common.css','media'=>'all', 'targetie' => 'ie7'); + $expected[] = array('file' => '/rhymix/common/css/common.css', 'media'=>'all', 'targetie' => 'ie8'); $this->assertEquals($handler->getCssFileList(), $expected); }); @@ -139,9 +138,9 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler->loadFile(array('./common/css/common.css', 'screen')); $handler->loadFile(array('./common/css/common.css', 'handled')); - $expected[] = array('file' => '/xe/common/css/common.css', 'media'=>'all', 'targetie' => null); - $expected[] = array('file' => '/xe/common/css/common.css','media'=>'screen', 'targetie' => null); - $expected[] = array('file' => '/xe/common/css/common.css', 'media'=>'handled', 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/css/common.css', 'media'=>'all', 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/css/common.css','media'=>'screen', 'targetie' => null); + $expected[] = array('file' => '/rhymix/common/css/common.css', 'media'=>'handled', 'targetie' => null); $this->assertEquals($handler->getCssFileList(), $expected); }); @@ -151,8 +150,8 @@ class FrontEndFileHandlerTest extends \Codeception\TestCase\Test $handler = new FrontEndFileHandler(); $handler->loadFile(array('./common/css/xe.css')); $handler->loadFile(array('./common/css/mobile.css')); - $expected[] = array('file' => '/xe/files/cache/minify/common.css.xe.min.css', 'media' => 'all', 'targetie' => null); - $expected[] = array('file' => '/xe/files/cache/minify/common.css.mobile.min.css', 'media' => 'all', 'targetie' => null); + $expected[] = array('file' => '/rhymix/files/cache/minify/common.css.xe.min.css', 'media' => 'all', 'targetie' => null); + $expected[] = array('file' => '/rhymix/files/cache/minify/common.css.mobile.min.css', 'media' => 'all', 'targetie' => null); $result = $handler->getCssFileList(); $result[0]['file'] = preg_replace('/\?\d+$/', '', $result[0]['file']); $result[1]['file'] = preg_replace('/\?\d+$/', '', $result[1]['file']); diff --git a/tests/unit/classes/OldSecurityTest.php b/tests/unit/classes/OldSecurityTest.php index f699b8d1b..85ed5e564 100644 --- a/tests/unit/classes/OldSecurityTest.php +++ b/tests/unit/classes/OldSecurityTest.php @@ -1,5 +1,4 @@ ', - '?>' + '?>' ), // relative path2 array( '', - '?>' + '?>' ), // error case array( @@ -213,7 +210,7 @@ class TemplateHandlerTest extends \Codeception\TestCase\Test // issue 584 array( 'mobile', - PHP_EOL . 'if($__Context->oBodex->display_extra_images[\'mobile\'] && $__Context->arr_extra && $__Context->arr_extra->bodex->mobile){ ?>mobile' + PHP_EOL . 'if($__Context->oBodex->display_extra_images[\'mobile\'] && $__Context->arr_extra && $__Context->arr_extra->bodex->mobile){ ?>mobile' ), // issue 831 array( @@ -222,8 +219,8 @@ class TemplateHandlerTest extends \Codeception\TestCase\Test ), // issue 746 array( - '', - '?>' + '', + '?>' ), // issue 696 array( @@ -233,47 +230,47 @@ class TemplateHandlerTest extends \Codeception\TestCase\Test // https://github.com/xpressengine/xe-core/issues/1510 array( '', - PHP_EOL . 'if($__Context->foo->bar){ ?>' + PHP_EOL . 'if($__Context->foo->bar){ ?>' ), // https://github.com/xpressengine/xe-core/issues/1510 array( 'a!@#$%^&*()_-=[]{}?/', - PHP_EOL . 'if($__Context->foo->bar > 100){ ?>a!@#$%^&*()_-=[]{}?/' + PHP_EOL . 'if($__Context->foo->bar > 100){ ?>a!@#$%^&*()_-=[]{}?/' ), // https://github.com/xpressengine/xe-core/issues/1510 array( '', - PHP_EOL . 'if($__Context->foo->bar){ ?>' + PHP_EOL . 'if($__Context->foo->bar){ ?>' ), // https://github.com/xpressengine/xe-core/issues/1510 array( '', - PHP_EOL . 'if(!$__Context->module_info->title){ ?>' + PHP_EOL . 'if(!$__Context->module_info->title){ ?>' ), // https://github.com/xpressengine/xe-core/issues/1510 array( '', - PHP_EOL . 'if($__Context->mi->title){ ?>mi->use){ ?> class="tmp_class" src="/xe/tests/unit/classes/img/common/blank.gif" />' + PHP_EOL . 'if($__Context->mi->title){ ?>mi->use){ ?> class="tmp_class" src="/rhymix/tests/unit/classes/img/common/blank.gif" />' ), array( ' alt', - '?> foo->bar){ ?>alt' + '?> foo->bar){ ?>alt' ), array( '' . "\n" . ' alt', - '?>' . PHP_EOL . ' foo->bar){ ?>alt' + '?>' . PHP_EOL . ' foo->bar){ ?>alt' ), array( 'asf ', '?>asf ' ), array( - '', - '?>' + '', + '?>' ), array( - 'asdf src="../img/img.gif" asdf src asdf src="../img/img.gif" asdf', - '?>asdf src="../img/img.gif" asdf src asdf src="../img/img.gif" asdf' + 'asdf src="../img/img.gif" asdf src asdf src="../img/img.gif" asdf', + '?>asdf src="../img/img.gif" asdf src asdf src="../img/img.gif" asdf' ), array( 'asdf src="../img/img.gif" asdf', diff --git a/tests/unit/framework/SecurityTest.php b/tests/unit/framework/SecurityTest.php index b28d99ada..f0db07f0d 100644 --- a/tests/unit/framework/SecurityTest.php +++ b/tests/unit/framework/SecurityTest.php @@ -100,11 +100,9 @@ class SecurityTest extends \Codeception\TestCase\Test $_SERVER['REQUEST_METHOD'] = 'POST'; $this->assertTrue(Rhymix\Framework\Security::checkCSRF()); - $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; $_SERVER['HTTP_REFERER'] = 'http://www.foobar.com/'; $this->assertFalse(Rhymix\Framework\Security::checkCSRF()); - $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; $this->assertTrue(Rhymix\Framework\Security::checkCSRF('http://www.rhymix.org/')); } diff --git a/tests/unit/framework/URLTest.php b/tests/unit/framework/URLTest.php index f3d5befb3..ed60020ee 100644 --- a/tests/unit/framework/URLTest.php +++ b/tests/unit/framework/URLTest.php @@ -4,48 +4,42 @@ class URLTest extends \Codeception\TestCase\Test { public function testGetCurrentURL() { - $protocol = \RX_SSL ? 'https://' : 'http://'; - $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; - $_SERVER['REQUEST_URI'] = '/index.php?foo=bar&xe=sucks'; - $full_url = $protocol . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; + $old_request_uri = $_SERVER['REQUEST_URI']; + $_SERVER['REQUEST_URI'] = '/rhymix/index.php?foo=bar&xe=sucks'; // Getting the current URL - $this->assertEquals($full_url, Rhymix\Framework\URL::getCurrentURL()); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php?foo=bar&xe=sucks', Rhymix\Framework\URL::getCurrentURL()); // Adding items to the query string - $this->assertEquals($full_url . '&var=1&arr%5B0%5D=2&arr%5B1%5D=3', Rhymix\Framework\URL::getCurrentURL(array('var' => '1', 'arr' => array(2, 3)))); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php?foo=bar&xe=sucks&var=1&arr%5B0%5D=2&arr%5B1%5D=3', Rhymix\Framework\URL::getCurrentURL(array('var' => '1', 'arr' => array(2, 3)))); // Removing item from the query string - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php?xe=sucks', Rhymix\Framework\URL::getCurrentURL(array('foo' => null))); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php?xe=sucks', Rhymix\Framework\URL::getCurrentURL(array('foo' => null))); // Removing all items from the query string - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php', Rhymix\Framework\URL::getCurrentURL(array('foo' => null, 'xe' => null))); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php', Rhymix\Framework\URL::getCurrentURL(array('foo' => null, 'xe' => null))); // Adding and removing parameters at the same time - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php?xe=sucks&l=ko', Rhymix\Framework\URL::getCurrentURL(array('l' => 'ko', 'foo' => null))); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php?xe=sucks&l=ko', Rhymix\Framework\URL::getCurrentURL(array('l' => 'ko', 'foo' => null))); + + $_SERVER['REQUEST_URI'] = $old_request_uri; } public function testGetCurrentDomainURL() { - $protocol = \RX_SSL ? 'https://' : 'http://'; - $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; - - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/', Rhymix\Framework\URL::getCurrentDomainURL()); - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/', Rhymix\Framework\URL::getCurrentDomainURL('/')); - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/foo/bar', Rhymix\Framework\URL::getCurrentDomainURL('/foo/bar')); - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php?foo=bar', Rhymix\Framework\URL::getCurrentDomainURL('index.php?foo=bar')); + $this->assertEquals('https://www.rhymix.org/', Rhymix\Framework\URL::getCurrentDomainURL()); + $this->assertEquals('https://www.rhymix.org/', Rhymix\Framework\URL::getCurrentDomainURL('/')); + $this->assertEquals('https://www.rhymix.org/foo/bar', Rhymix\Framework\URL::getCurrentDomainURL('/foo/bar')); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php?foo=bar', Rhymix\Framework\URL::getCurrentDomainURL('rhymix/index.php?foo=bar')); } public function testGetCanonicalURL() { - $protocol = \RX_SSL ? 'https://' : 'http://'; - $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; - $tests = array( - 'foo/bar' => $protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'foo/bar', - './foo/bar' => $protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'foo/bar', - '/foo/bar' => $protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'foo/bar', - '//www.example.com/foo' => $protocol . 'www.example.com/foo', + 'foo/bar' => 'https://www.rhymix.org/rhymix/foo/bar', + './foo/bar' => 'https://www.rhymix.org/rhymix/foo/bar', + '/foo/bar' => 'https://www.rhymix.org/rhymix/foo/bar', + '//www.example.com/foo' => 'https://www.example.com/foo', 'http://xn--cg4bkiv2oina.com/' => 'http://삼성전자.com/', ); @@ -71,21 +65,17 @@ class URLTest extends \Codeception\TestCase\Test public function testModifyURL() { - $protocol = \RX_SSL ? 'https://' : 'http://'; - $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; - $url = $protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php?foo=bar'; - // Conversion to absolute - $this->assertEquals($url, Rhymix\Framework\URL::modifyURL('./index.php?foo=bar')); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php?foo=bar', $url = Rhymix\Framework\URL::modifyURL('./index.php?foo=bar')); // Adding items to the query string - $this->assertEquals($url . '&var=1&arr%5B0%5D=2&arr%5B1%5D=3', Rhymix\Framework\URL::modifyURL($url, array('var' => '1', 'arr' => array(2, 3)))); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php?foo=bar&var=1&arr%5B0%5D=2&arr%5B1%5D=3', Rhymix\Framework\URL::modifyURL($url, array('var' => '1', 'arr' => array(2, 3)))); // Removing item from the query string - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php', Rhymix\Framework\URL::modifyURL($url, array('foo' => null))); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php', Rhymix\Framework\URL::modifyURL($url, array('foo' => null))); // Adding and removing parameters at the same time - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php?l=ko', Rhymix\Framework\URL::modifyURL($url, array('l' => 'ko', 'foo' => null))); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php?l=ko', Rhymix\Framework\URL::modifyURL($url, array('l' => 'ko', 'foo' => null))); } public function testIsInternalURL() @@ -95,23 +85,17 @@ class URLTest extends \Codeception\TestCase\Test public function testURLFromServerPath() { - $protocol = \RX_SSL ? 'https://' : 'http://'; - $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; - - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR)); - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/index.php', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . 'index.php')); - $this->assertEquals($protocol . $_SERVER['HTTP_HOST'] . '/foo/bar', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . '/foo/bar')); + $this->assertEquals('https://www.rhymix.org/rhymix/', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR)); + $this->assertEquals('https://www.rhymix.org/rhymix/index.php', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . 'index.php')); + $this->assertEquals('https://www.rhymix.org/rhymix/foo/bar', Rhymix\Framework\URL::fromServerPath(\RX_BASEDIR . '/foo/bar')); $this->assertEquals(false, Rhymix\Framework\URL::fromServerPath(dirname(dirname(\RX_BASEDIR)))); $this->assertEquals(false, Rhymix\Framework\URL::fromServerPath('C:/Windows')); } public function testURLToServerPath() { - $protocol = \RX_SSL ? 'https://' : 'http://'; - $_SERVER['HTTP_HOST'] = 'www.rhymix.org'; - - $this->assertEquals(\RX_BASEDIR . 'index.php', Rhymix\Framework\URL::toServerPath($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . 'index.php')); - $this->assertEquals(\RX_BASEDIR . 'foo/bar', Rhymix\Framework\URL::toServerPath($protocol . $_SERVER['HTTP_HOST'] . \RX_BASEURL . '/foo/bar?arg=baz')); + $this->assertEquals(\RX_BASEDIR . 'index.php', Rhymix\Framework\URL::toServerPath('http://www.rhymix.org/rhymix/index.php')); + $this->assertEquals(\RX_BASEDIR . 'foo/bar', Rhymix\Framework\URL::toServerPath('http://www.rhymix.org/rhymix/foo/bar?arg=baz')); $this->assertEquals(\RX_BASEDIR . 'foo/bar', Rhymix\Framework\URL::toServerPath('./foo/bar')); $this->assertEquals(\RX_BASEDIR . 'foo/bar', Rhymix\Framework\URL::toServerPath('foo/bar/../bar')); $this->assertEquals(false, Rhymix\Framework\URL::toServerPath('http://other.domain.com/')); From f9ea115c19de7d52a7e34ab079a223c979147301 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 16 Mar 2016 10:09:48 +0900 Subject: [PATCH 106/205] Fix some minor bugs in global functions and add unit tests --- common/functions.php | 9 +- tests/unit/functions/FunctionsTest.php | 152 +++++++++++++++++++++++++ 2 files changed, 158 insertions(+), 3 deletions(-) create mode 100644 tests/unit/functions/FunctionsTest.php diff --git a/common/functions.php b/common/functions.php index 154d60455..5f9214123 100644 --- a/common/functions.php +++ b/common/functions.php @@ -238,7 +238,7 @@ function starts_with($needle, $haystack, $case_sensitive = true) } else { - !strncasecmp($needle, $haystack, strlen($needle)); + return !strncasecmp($needle, $haystack, strlen($needle)); } } @@ -452,8 +452,11 @@ if (!function_exists('hex2bin')) */ function tobool($input) { - if (preg_match('/^(1|[ty].*|on|oui|si|vrai|aye)$/i', $input)) return true; - if (preg_match('/^(0|[fn].*|off)$/i', $input)) return false; + if (is_scalar($input)) + { + if (preg_match('/^(1|[ty].*|on|ok.*oui|si|vrai|aye)$/i', $input)) return true; + if (preg_match('/^(0|[fn].*|off)$/i', $input)) return false; + } return (bool)$input; } diff --git a/tests/unit/functions/FunctionsTest.php b/tests/unit/functions/FunctionsTest.php new file mode 100644 index 000000000..e89a73977 --- /dev/null +++ b/tests/unit/functions/FunctionsTest.php @@ -0,0 +1,152 @@ + 'xe', 'bar' => 'rhymix', 'key' => array('value1', 'value2', array('bar' => 'value3')), 'last' => 'bears'); + $flattened1 = array('foo' => 'xe', 'bar' => 'value3', 0 => 'value1', 1 => 'value2', 'last' => 'bears'); + $flattened2 = array(0 => 'xe', 1 => 'rhymix', 2 => 'value1', 3 => 'value2', 4 => 'value3', 5 => 'bears'); + + $this->assertEquals('foo', array_first_key($array)); + $this->assertEquals('xe', array_first($array)); + + $this->assertEquals('last', array_last_key($array)); + $this->assertEquals('bears', array_last($array)); + + $this->assertEquals($flattened1, array_flatten($array)); + $this->assertEquals($flattened2, array_flatten($array, false)); + } + + public function testClassBasename() + { + $this->assertEquals('FunctionsTest', class_basename($this)); + $this->assertEquals('FunctionsTest', class_basename(get_class($this))); + } + + public function testEscapeFunctions() + { + $this->assertEquals('<foo>&amp;</foo>', escape('&')); + $this->assertEquals('<foo>&</foo>', escape('&', false)); + + $this->assertEquals('expressionalertXSS', escape_css('expression:alert("XSS")')); + $this->assertEquals('#123456', escape_css('#123456')); + + $this->assertEquals('hello\\\\world', escape_js('hello\\world')); + $this->assertEquals('\u003Cbr \/\u003E', escape_js('
')); + + $this->assertEquals('hello\\\\world', escape_sqstr('hello\\world')); + $this->assertEquals('hello"world\\\'quotes', escape_sqstr('hello"world\'quotes')); + + $this->assertEquals('hello\\\\\\$world in \\"quotes\\"', escape_dqstr('hello\\$world in "quotes"')); + $this->assertEquals('\\${array[\'key\']}', escape_dqstr('${array[\'key\']}')); + } + + public function testExplodeWithEscape() + { + $this->assertEquals(array('foo', 'bar'), explode_with_escape(',', 'foo,bar')); + $this->assertEquals(array('foo', 'bar'), explode_with_escape(',', 'foo , bar')); + $this->assertEquals(array('foo', 'bar', 'baz,rhymix'), explode_with_escape(',', 'foo,bar,baz,rhymix', 3)); + $this->assertEquals(array('foo', 'bar', 'baz , rhymix'), explode_with_escape(',', 'foo,bar,baz , rhymix', 3)); + + $this->assertEquals(array('foo', 'bar,baz'), explode_with_escape(',', 'foo,bar\\,baz')); + $this->assertEquals(array('foo', 'bar\\', 'baz'), explode_with_escape(',', 'foo,bar\\ , baz')); + $this->assertEquals(array('foo', 'bar,baz', 'rhymix'), explode_with_escape(',', 'foo,bar\\,baz,rhymix')); + $this->assertEquals(array('foo', 'bar,baz'), explode_with_escape(',', 'foo,bar!,baz', null, '!')); + } + + public function testStartsEndsContains() + { + $this->assertTrue(starts_with('foo', 'foobar')); + $this->assertFalse(starts_with('FOO', 'foobar')); + $this->assertTrue(starts_with('FOO', 'foobar', false)); + $this->assertFalse(starts_with('bar', 'foobar')); + + $this->assertTrue(ends_with('bar', 'foobar')); + $this->assertFalse(ends_with('BAR', 'foobar')); + $this->assertTrue(ends_with('BAR', 'foobar', false)); + $this->assertFalse(ends_with('foo', 'foobar')); + + $this->assertTrue(contains('foo', 'foo bar baz rhymix rocks')); + $this->assertFalse(contains('barbaz', 'foo bar baz rhymix rocks')); + $this->assertTrue(contains('RHYMIX', 'foo bar baz rhymix rocks', false)); + $this->assertFalse(contains('ROCKS', 'foo bar baz rhymix rocks')); + } + + public function testRangeFunctions() + { + $this->assertTrue(is_between(5, 1, 10)); + $this->assertTrue(is_between(1, 1, 10)); + $this->assertTrue(is_between(10, 1, 10)); + $this->assertTrue(is_between(7, 1, 10, true)); + $this->assertFalse(is_between(1, 1, 10, true)); + $this->assertFalse(is_between(10, 1, 10, true)); + + $this->assertEquals(10, force_range(14, 1, 10)); + $this->assertEquals(3, force_range(3, 1, 10)); + $this->assertEquals(1, force_range(-4, 1, 10)); + } + + public function testUrlSafeBase64() + { + $this->assertEquals('Umh5bWl4IF5-', base64_encode_urlsafe('Rhymix ^~')); + $this->assertEquals('Rhymix ^~', base64_decode_urlsafe('Umh5bWl4IF5-')); + } + + public function testHex2Rgb2Hex() + { + $this->assertEquals(array(128, 128, 128), hex2rgb('808080')); + $this->assertEquals(array(60, 71, 244), hex2rgb('#3c47f4')); + $this->assertEquals(array(119, 119, 119), hex2rgb('#777')); + $this->assertEquals(array(51, 102, 153), hex2rgb('369')); + + $this->assertEquals('#808080', rgb2hex(array(128, 128, 128))); + $this->assertEquals('#3c47f4', rgb2hex(array(60, 71, 244))); + $this->assertEquals('777777', rgb2hex(array(119, 119, 119), false)); + $this->assertEquals('#000000', rgb2hex(array())); + } + + public function testToBool() + { + $this->assertTrue(tobool('Y')); + $this->assertTrue(tobool('yes')); + $this->assertTrue(tobool('on')); + $this->assertTrue(tobool('ok')); + $this->assertTrue(tobool('okay')); + $this->assertTrue(tobool('true')); + $this->assertTrue(tobool(1)); + $this->assertTrue(tobool(-1)); + $this->assertTrue(tobool(true)); + $this->assertTrue(tobool(array(1, 2, 3))); + + $this->assertFalse(tobool('N')); + $this->assertFalse(tobool('no')); + $this->assertFalse(tobool('false')); + $this->assertFalse(tobool('off')); + $this->assertFalse(tobool('Fuck you!')); + $this->assertFalse(tobool(0)); + $this->assertFalse(tobool('')); + $this->assertFalse(tobool(false)); + $this->assertFalse(tobool(null)); + $this->assertFalse(tobool(array())); + } + + public function testUTF8Functions() + { + $this->assertTrue(utf8_check('Hello, world!')); + $this->assertTrue(utf8_check('라이믹스')); + $this->assertTrue(utf8_check('')); + $this->assertTrue(utf8_check(iconv('UTF-8', 'EUC-KR', 'One CMS to rule them all...'))); + $this->assertFalse(utf8_check(iconv('UTF-8', 'EUC-KR', '라이믹스'))); + $this->assertFalse(utf8_check(chr(129) . chr(214) . chr(181) . chr(73) . chr(97))); + + $this->assertEquals('Emoticon: 😁', utf8_mbencode("Emoticon: \xf0\x9f\x98\x81")); + $this->assertEquals('Emoticon: 😜', utf8_mbencode("Emoticon: \xf0\x9f\x98\x9c")); + $this->assertEquals('한글은 인코딩하지 않음', utf8_mbencode('한글은 인코딩하지 않음')); + + $this->assertEquals("Weird spaces are in this string", utf8_normalize_spaces("Weird\x20spaces\xe2\x80\x80are\xe2\x80\x84in\xe2\x80\x86\xe2\x80\x8bthis\x0astring")); + $this->assertEquals("Weird spaces are in this\nstring", utf8_normalize_spaces("Weird\x20spaces\xe2\x80\x80are\xe2\x80\x84in\xe2\x80\x86\xe2\x80\x8bthis\x0astring", true)); + $this->assertEquals("Trimmed", utf8_trim("\x20\xe2\x80\x80Trimmed\xe2\x80\x84\xe2\x80\x86\xe2\x80\x8b")); + $this->assertEquals("Trimmed", utf8_trim("\x20\xe2\x80\x80Trimmed\x0a\x0c\x07\x09")); + } +} From d325ef99bc5494324066e00d81e8412a8b4646a3 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 16 Mar 2016 11:36:36 +0900 Subject: [PATCH 107/205] Add unit tests for some legacy tests, too --- common/legacy.php | 2 +- tests/unit/framework/DateTimeTest.php | 17 +- tests/unit/functions/LegacyTest.php | 220 ++++++++++++++++++++++++++ 3 files changed, 236 insertions(+), 3 deletions(-) diff --git a/common/legacy.php b/common/legacy.php index 3f827fa96..3d4af5102 100644 --- a/common/legacy.php +++ b/common/legacy.php @@ -421,7 +421,7 @@ function getCurrentPageUrl($escape = true) */ function isSiteID($domain) { - return preg_match('/^([a-zA-Z0-9\_]+)$/', $domain); + return (bool)preg_match('/^([a-zA-Z0-9\_]+)$/', $domain); } /** diff --git a/tests/unit/framework/DateTimeTest.php b/tests/unit/framework/DateTimeTest.php index 737147bb3..d5adc1105 100644 --- a/tests/unit/framework/DateTimeTest.php +++ b/tests/unit/framework/DateTimeTest.php @@ -66,15 +66,28 @@ class DateTimeTest extends \Codeception\TestCase\Test { $timestamp = 1454000000; - // Test zdate() when the internal time zone is different from the default time zone. + // Test when the internal time zone is different from the default time zone. Rhymix\Framework\Config::set('locale.internal_timezone', 10800); $this->assertEquals('20160128195320', getInternalDateTime($timestamp)); - // Test zdate() when the internal time zone is the same as the default time zone. + // Test when the internal time zone is the same as the default time zone. Rhymix\Framework\Config::set('locale.internal_timezone', 32400); $this->assertEquals('20160129015320', getInternalDateTime($timestamp)); } + public function testGetDisplayDateTime() + { + $timestamp = 1454000000; + + // Test when the display time zone is different from the internal time zone. + $_SESSION['timezone'] = 'America/Los_Angeles'; + $this->assertEquals('20160128085320', getDisplayDateTime($timestamp)); + + // Test when the display time zone is the same as the internal time zone. + $_SESSION['timezone'] = 'Etc/GMT-3'; + $this->assertEquals('20160128195320', getDisplayDateTime($timestamp)); + } + public function testGetTimeGap() { $GLOBALS['lang'] = Rhymix\Framework\Lang::getInstance('en'); diff --git a/tests/unit/functions/LegacyTest.php b/tests/unit/functions/LegacyTest.php index 413b1f9cd..5bb693d56 100644 --- a/tests/unit/functions/LegacyTest.php +++ b/tests/unit/functions/LegacyTest.php @@ -2,5 +2,225 @@ class LegacyTest extends \Codeception\TestCase\Test { + public function testGetModule() + { + $this->assertTrue(getModule('board', 'controller') instanceof BoardController); + $this->assertTrue(getModule('board', 'model') instanceof BoardModel); + $this->assertTrue(getModule('board', 'view', 'admin') instanceof BoardAdminView); + $this->assertTrue(getModule('board') instanceof BoardView); + $this->assertTrue(getAdminController('board') instanceof BoardAdminController); + $this->assertTrue(getAdminModel('board') instanceof BoardAdminModel); + $this->assertTrue(getAdminView('board') instanceof BoardAdminView); + $this->assertTrue(getController('board') instanceof BoardController); + $this->assertTrue(getModel('board') instanceof BoardModel); + $this->assertTrue(getView('board') instanceof BoardView); + $this->assertTrue(getAPI('board') instanceof BoardApi); + $this->assertTrue(getMobile('board') instanceof BoardMobile); + $this->assertTrue(getWAP('board') instanceof BoardWap); + $this->assertTrue(getClass('board') instanceof Board); + } + public function testGetNextSequence() + { + if (!file_exists(\RX_BASEDIR . 'files/config/config.php')) + { + return; + } + + $this->assertGreaterThan(0, $sequence1 = getNextSequence()); + $this->assertGreaterThan($sequence1, $sequence2 = getNextSequence()); + + $this->assertTrue(checkUserSequence($sequence1)); + $this->assertTrue(checkUserSequence($sequence2)); + + $this->assertFalse(checkUserSequence(-1)); + setUserSequence(-1); + $this->assertTrue(checkUserSequence(-1)); + } + + public function testGetURL() + { + /** + * TODO: + * - getUrl() + * - getNotEncodedUrl() + * - getAutoEncodedUrl() + * - getFullUrl() + * - getNotEncodedFullUrl() + * - getSiteUrl() + * - getNotEncodedSiteUrl() + * - getFullSiteUrl() + * - getCurrentPageUrl() + * - getScriptPath() + * - getRequestUriByServerEnviroment() + */ + } + + public function testIsSiteID() + { + $this->assertTrue(isSiteID('rhymix_RHYMIX_1234')); + $this->assertFalse(isSiteID('www.rhymix.org')); + } + + public function testCutStr() + { + $this->assertEquals('안녕하세요? 라이믹스...', cut_str('안녕하세요? 라이믹스입니다. 제목이 너무 길어서 잘립니다.', 20)); + $this->assertEquals('Hello? This is Rhymix...', cut_str('Hello? This is Rhymix. This title is very long.', 20)); + } + + public function testTimeFunctions() + { + $this->assertEquals(0, get_time_zone_offset('00:00')); + $this->assertEquals(32400, get_time_zone_offset('+09:00')); + $this->assertEquals(32400, get_time_zone_offset('+0900')); + $this->assertEquals(32400, get_time_zone_offset('0900')); + $this->assertEquals(-18000, get_time_zone_offset('-05:00')); + $this->assertEquals(-18000, get_time_zone_offset('-0500')); + + $this->assertEquals('Jan', getMonthName(1)); + $this->assertEquals('Sep', getMonthName(9, true)); + $this->assertEquals('September', getMonthName(9, false)); + + /** + * The following functions are tested in DateTimeTest: + * - zgap() + * - zdate() + * - ztime() + * - getInternalDateTime() + * - getDisplayDateTime() + * - getTimeGap() + */ + } + + public function testGetEncodedEmailAddress() + { + $this->assertNotEquals('devops@rhymix.org', getEncodeEmailAddress('devops@rhymix.org')); + $this->assertContains('&#X', getEncodeEmailAddress('devops@rhymix.org')); + } + + public function testGetMicrotime() + { + $microtime1 = microtime(true); + $microtime2 = getMicroTime(); + $microtime3 = microtime(true); + + $this->assertEquals('double', gettype($microtime2)); + $this->assertGreaterThanOrEqual($microtime1, $microtime2); + $this->assertGreaterThanOrEqual($microtime2, $microtime3); + } + + public function testDelObjectVars() + { + $target = (object)array('foo' => 1, 'bar' => 2, 'baz' => 3, 'rhymix' => 4); + $delete = (object)array('bar' => 5, 'baz' => 6); + $result = delObjectVars($target, $delete); + + // Check if the keys were deleted from the result. + $this->assertTrue(isset($result->foo)); + $this->assertFalse(isset($result->bar)); + $this->assertFalse(isset($result->baz)); + $this->assertTrue(isset($result->rhymix)); + + // Check if the keys are intact in the original target. + $this->assertTrue(isset($target->bar)); + $this->assertTrue(isset($target->baz)); + } + + public function testGetDestroyXeVars() + { + // Test array. (Keys should be intact in the original target.) + $target = array('foo' => 1, 'bar' => 2, 'xe_validator_id' => 3); + $result = getDestroyXeVars($target); + $this->assertFalse(isset($result['xe_validator_id'])); + $this->assertTrue(isset($target['xe_validator_id'])); + + // Test object. (Keys should be deleted from the original target.) + $target = (object)array('foo' => 1, 'bar' => 2, 'xe_validator_id' => 3); + $result = getDestroyXeVars($target); + $this->assertFalse(isset($result->xe_validator_id)); + $this->assertFalse(isset($target->xe_validator_id)); + } + + public function testGetNumberingPath() + { + $this->assertEquals('001/', getNumberingPath(1)); + $this->assertEquals('012/', getNumberingPath(12)); + $this->assertEquals('123/', getNumberingPath(123)); + $this->assertEquals('234/001/', getNumberingPath(1234)); + $this->assertEquals('345/012/', getNumberingPath(12345)); + $this->assertEquals('456/123/', getNumberingPath(123456)); + $this->assertEquals('567/234/001/', getNumberingPath(1234567)); + $this->assertEquals('678/345/012/', getNumberingPath(12345678)); + $this->assertEquals('789/456/123/', getNumberingPath(123456789)); + } + + public function testMysqlPre4HashPassword() + { + $this->assertEquals('5d2e19393cc5ef67', mysql_pre4_hash_password('password')); + $this->assertEquals('25a4fb474e17c19a', mysql_pre4_hash_password('pass\'#word')); + } + + public function testJsonEncode2() + { + $data = array('foo' => 1, 'bar' => 2, 'baz' => 3, 'rhymix' => 4); + $this->assertEquals(json_encode($data), json_encode2($data)); + } + + public function TestIsCrawler() + { + $original_user_agent = $_SERVER['HTTP_USER_AGENT']; + + // Test automatic detection from User-Agent string. + $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (Windows NT 6.1; rv:45.0) Gecko/20100101 Firefox/45.0'; + $this->assertFalse(isCrawler()); + $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'; + $this->assertTrue(isCrawler()); + + // Test manual detection. + $this->assertTrue(isCrawler('Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)')); + $this->assertTrue(isCrawler('Yeti/1.0 (NHN Corp.; http://help.naver.com/robots/)')); + $this->assertFalse(isCrawler('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36')); + $this->assertFalse(isCrawler('Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25')); + + $_SERVER['HTTP_USER_AGENT'] = $original_user_agent; + } + + public function testMiscUTF8Functions() + { + $this->assertEquals('<img>', url_decode('%3Cimg%3E')); + $this->assertEquals('한글 % English', utf8RawUrlDecode('%uD55C%uAE00%20%25%20English')); + $this->assertEquals('뷁', _code2utf(48577)); + + $this->assertTrue(detectUTF8('라이믹스')); + $this->assertTrue(detectUTF8(urlencode('라이믹스'))); + $this->assertTrue(detectUTF8('%87%a9%43%cd%ef', false, false)); + $this->assertFalse(detectUTF8(iconv('UTF-8', 'EUC-KR', '라이믹스'))); + $this->assertFalse(detectUTF8(chr(129) . chr(214) . chr(181) . chr(73) . chr(97))); + $this->assertFalse(detectUTF8('%87%a9%43%cd%ef')); + $this->assertEquals(mb_convert_encoding('라이믹스', 'UTF-8', 'CP949'), detectUTF8('라이믹스', true)); + $this->assertEquals('라이믹스', detectUTF8(iconv('UTF-8', 'EUC-KR', '라이믹스'), true)); + } + + public function testMiscSecurityFunctions() + { + /** + * TODO: + * - stripEmbedTagForAdmin() + * - checkCSRF() + */ + } + + public function testRecurciveExposureCheck() + { + /** + * TODO + */ + } + + public function testChangeValueInUrl() + { + /** + * TODO + */ + } } From b890f9f42e6fc58833b37814d9a353a6c702bd14 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 16 Mar 2016 14:14:41 +0900 Subject: [PATCH 108/205] Consistently update the new message flag file --- .../communication.controller.php | 60 +++++++++---------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/modules/communication/communication.controller.php b/modules/communication/communication.controller.php index 562b14a35..56c161bc4 100644 --- a/modules/communication/communication.controller.php +++ b/modules/communication/communication.controller.php @@ -258,13 +258,7 @@ class communicationController extends communication $oDB->commit(); // create a flag that message is sent (in file format) - $flag_path = './files/member_extra_info/new_message_flags/' . getNumberingPath($receiver_srl); - FileHandler::makeDir($flag_path); - $flag_file = sprintf('%s%s', $flag_path, $receiver_srl); - - $oCommunicationModel = getModel('communication'); - $new_message_count = $oCommunicationModel->getNewMessageCount($receiver_srl); - FileHandler::writeFile($flag_file, $new_message_count); + $this->updateFlagFile($receiver_srl); return new Object(0, 'success_sended'); } @@ -305,7 +299,7 @@ class communicationController extends communication { return $output; } - + $this->updateFlagFile($logged_info->member_srl); $this->setMessage('success_registed'); } @@ -365,7 +359,7 @@ class communicationController extends communication { return $output; } - + $this->updateFlagFile($member_srl); $this->setMessage('success_deleted'); } @@ -443,7 +437,7 @@ class communicationController extends communication { return $output; } - + $this->updateFlagFile($member_srl); $this->setMessage('success_deleted'); $returnUrl = Context::get('success_return_url') ? Context::get('success_return_url') : getNotEncodedUrl('', 'mid', Context::get('mid'), 'act', 'dispCommunicationMessages', 'message_type', Context::get('message_type')); @@ -778,26 +772,30 @@ class communicationController extends communication $args->related_srl = $message_srl; $output = executeQuery('communication.setMessageReaded', $args); - // Renew a flag - $logged_info = Context::get('logged_info'); - $flag_path = './files/member_extra_info/new_message_flags/' . getNumberingPath($logged_info->member_srl); - $flag_file = sprintf('%s%s', $flag_path, $logged_info->member_srl); - if(file_exists($flag_file)) - { - $oCommunicationModel = getModel('communication'); - $new_message_count = $oCommunicationModel->getNewMessageCount(); - if($new_message_count > 0) - { - FileHandler::writeFile($flag_file, $new_message_count); - } - else - { - FileHandler::removeFile($flag_file); - } - } - + // Update flag file + $this->updateFlagFile($logged_info->member_srl); return $output; } + + /** + * Update flag file + * @param int $member_srl + * @return void + */ + function updateFlagFile($member_srl) + { + $flag_path = \RX_BASEDIR . 'files/member_extra_info/new_message_flags/' . getNumberingPath($member_srl); + $flag_file = $flag_path . $member_srl; + $new_message_count = getModel('communication')->getNewMessageCount($member_srl); + if($new_message_count > 0) + { + FileHandler::writeFile($flag_file, $new_message_count); + } + else + { + FileHandler::removeFile($flag_file); + } + } function triggerModuleHandlerBefore($obj) { @@ -833,14 +831,14 @@ class communicationController extends communication if($config->enable_message == 'Y' && $obj->act != 'dispCommunicationNewMessage') { $flag_path = './files/member_extra_info/new_message_flags/' . getNumberingPath($logged_info->member_srl); - $flag_file = sprintf('%s%s', $flag_path, $logged_info->member_srl); + $flag_file = $flag_path . $logged_info->member_srl; if(file_exists($flag_file)) { // Pop-up to display messages if a flag on new message is set - $new_message_count = (int) trim(FileHandler::readFile($flag_file)); + $new_message_count = (int)trim(FileHandler::readFile($flag_file)); if($new_message_count > 0) { - $text = preg_replace('@\r?\n@', '\\n', addslashes(lang('alert_new_message_arrived'))); + $text = escape_js(lang('alert_new_message_arrived')); Context::addHtmlFooter(""); Context::loadFile(array('./modules/communication/tpl/js/member_communication.js'), true); } From 5ead23ae7931455e7e571d5bad3aac9d7eb238a0 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 16 Mar 2016 14:18:37 +0900 Subject: [PATCH 109/205] Update new message flag file when reading message --- modules/communication/communication.view.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/communication/communication.view.php b/modules/communication/communication.view.php index ff3c10dc2..b76264cb6 100644 --- a/modules/communication/communication.view.php +++ b/modules/communication/communication.view.php @@ -116,6 +116,9 @@ class communicationView extends communication $columnList = array('message_srl', 'readed', 'title', 'member.member_srl', 'member.nick_name', 'message.regdate', 'readed_date'); $output = $oCommunicationModel->getMessages($message_type, $columnList); + // Update flag file + getController('communication')->updateFlagFile($logged_info->member_srl); + // set a template file Context::set('total_count', $output->total_count); Context::set('total_page', $output->total_page); From 54795421fe736b0a9885cd8025bd20b447fb2ca0 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 16 Mar 2016 14:20:51 +0900 Subject: [PATCH 110/205] Fix styles of new message notification --- .../tpl/js/member_communication.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/modules/communication/tpl/js/member_communication.js b/modules/communication/tpl/js/member_communication.js index ae6f8c013..a69263a43 100644 --- a/modules/communication/tpl/js/member_communication.js +++ b/modules/communication/tpl/js/member_communication.js @@ -3,17 +3,20 @@ window.xeNotifyMessage = function(text, count){ $bar = $('div.message.info'); if(!$bar.length) { - $bar = $('
') - .hide() - .css({ - 'position' : 'absolute', - 'z-index' : '100', - }) - .prependTo(document.body); + $bar = jQuery('
').hide().css({ + 'position' : 'absolute', + 'opacity' : 0.7, + 'z-index' : 10000, + }).appendTo(document.body); } text = text.replace('%d', count); - $bar.html('

'+text+'

').height(); + var link = jQuery(''); + link.attr("href", current_url.setQuery('module','').setQuery('act','dispCommunicationNewMessage')); + //link.attr("onclick", "popopen(this.href, 'popup');xeNotifyMessageClose(); return false;"); + link.text(text); + var para = jQuery('

'); + para.append(link).appendTo($bar); $bar.show().animate({top:0}); }; From 4b2bced0eb3ad48ebd5dfecd005ebad9f1358ea2 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 16 Mar 2016 14:39:53 +0900 Subject: [PATCH 111/205] Fix URL in new message notification --- modules/communication/tpl/js/member_communication.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/communication/tpl/js/member_communication.js b/modules/communication/tpl/js/member_communication.js index a69263a43..661a431ab 100644 --- a/modules/communication/tpl/js/member_communication.js +++ b/modules/communication/tpl/js/member_communication.js @@ -12,7 +12,7 @@ text = text.replace('%d', count); var link = jQuery(''); - link.attr("href", current_url.setQuery('module','').setQuery('act','dispCommunicationNewMessage')); + link.attr("href", current_url.setQuery('module','').setQuery('act','dispCommunicationMessages')); //link.attr("onclick", "popopen(this.href, 'popup');xeNotifyMessageClose(); return false;"); link.text(text); var para = jQuery('

'); From 488704acf01f1000efef5e2b2dfd76fae1265f2a Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 16 Mar 2016 14:48:54 +0900 Subject: [PATCH 112/205] Move trigger to moduleObject.proc.after for better consistency --- modules/communication/communication.class.php | 19 ++++++++++++++++++- .../communication.controller.php | 5 +++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/modules/communication/communication.class.php b/modules/communication/communication.class.php index a4b2518d3..fd8600e27 100644 --- a/modules/communication/communication.class.php +++ b/modules/communication/communication.class.php @@ -9,9 +9,12 @@ class communication extends ModuleObject { private $triggers = array( - array('moduleHandler.init', 'communication', 'controller', 'triggerModuleHandlerBefore', 'before'), + array('moduleObject.proc', 'communication', 'controller', 'triggerModuleProcAfter', 'after'), array('member.getMemberMenu', 'communication', 'controller', 'triggerMemberMenu', 'before') ); + private $delete_triggers = array( + array('moduleHandler.init', 'communication', 'controller', 'triggerModuleHandlerBefore', 'before'), + ); /** * Implement if additional tasks are necessary when installing @@ -47,6 +50,13 @@ class communication extends ModuleObject return TRUE; } } + foreach($this->delete_triggers as $trigger) + { + if($oModuleModel->getTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4])) + { + return TRUE; + } + } if(!is_dir("./files/member_extra_info/new_message_flags")) { @@ -72,6 +82,13 @@ class communication extends ModuleObject $oModuleController->insertTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4]); } } + foreach($this->delete_triggers as $trigger) + { + if($oModuleModel->getTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4])) + { + $oModuleController->deleteTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4]); + } + } if(!is_dir("./files/member_extra_info/new_message_flags")) { diff --git a/modules/communication/communication.controller.php b/modules/communication/communication.controller.php index 56c161bc4..9b04e270d 100644 --- a/modules/communication/communication.controller.php +++ b/modules/communication/communication.controller.php @@ -798,6 +798,11 @@ class communicationController extends communication } function triggerModuleHandlerBefore($obj) + { + return $this->triggerModuleProcAfter($obj); + } + + function triggerModuleProcAfter($obj) { if(!Context::get('is_logged') || $obj->module == 'member') { From 5a3893be5e9894b5bd6914814c30bcfef37bb51b Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 16 Mar 2016 14:51:58 +0900 Subject: [PATCH 113/205] Do not display notification if we are already on the message view --- modules/communication/communication.controller.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/communication/communication.controller.php b/modules/communication/communication.controller.php index 9b04e270d..74392742d 100644 --- a/modules/communication/communication.controller.php +++ b/modules/communication/communication.controller.php @@ -808,6 +808,10 @@ class communicationController extends communication { return new Object(); } + if (starts_with('dispCommunication', Context::get('act'))) + { + return new Object(); + } $logged_info = Context::get('logged_info'); $oCommunicationModel = getModel('communication'); From 94859b10460c3ceaca61219e42696dc8b53d0298 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 16 Mar 2016 15:25:32 +0900 Subject: [PATCH 114/205] Improve performance of FilenameFilter::cleanPath() --- common/framework/filters/filenamefilter.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/common/framework/filters/filenamefilter.php b/common/framework/filters/filenamefilter.php index a43fb8ba2..dd99954c9 100644 --- a/common/framework/filters/filenamefilter.php +++ b/common/framework/filters/filenamefilter.php @@ -65,16 +65,16 @@ class FilenameFilter $path = str_replace('\\', '/', $path); // Remove querystrings and URL fragments. - $path = preg_replace('@[\?#].+$@', '', $path); + if (($querystring = strpbrk($path, '?#')) !== false) + { + $path = substr($path, 0, -1 * strlen($querystring)); + } - // Remove duplicate slashes, except at the beginning of a URL. - $path = preg_replace('@(? Date: Wed, 16 Mar 2016 16:31:27 +0900 Subject: [PATCH 115/205] Do not display HTML error page if the current SAPI is CLI --- common/framework/debug.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/framework/debug.php b/common/framework/debug.php index bf2227904..566f0a923 100644 --- a/common/framework/debug.php +++ b/common/framework/debug.php @@ -397,6 +397,12 @@ class Debug */ public static function displayErrorScreen($message) { + // Do not display error screen in CLI. + if (php_sapi_name() === 'cli') + { + return; + } + // Disable output buffering. while (ob_get_level()) { From e9ca45eac789264c40b7a831ca10c5b182819578 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 16 Mar 2016 19:10:53 +0900 Subject: [PATCH 116/205] Fix disappearing message & friend menu --- modules/communication/communication.controller.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/communication/communication.controller.php b/modules/communication/communication.controller.php index 74392742d..a287e8657 100644 --- a/modules/communication/communication.controller.php +++ b/modules/communication/communication.controller.php @@ -808,11 +808,6 @@ class communicationController extends communication { return new Object(); } - if (starts_with('dispCommunication', Context::get('act'))) - { - return new Object(); - } - $logged_info = Context::get('logged_info'); $oCommunicationModel = getModel('communication'); $config = $oCommunicationModel->getConfig(); @@ -837,6 +832,11 @@ class communicationController extends communication $oMemberController->addMemberMenu('dispCommunicationFriend', 'cmd_view_friend'); } + if (starts_with('dispCommunication', Context::get('act'))) + { + return new Object(); + } + if($config->enable_message == 'Y' && $obj->act != 'dispCommunicationNewMessage') { $flag_path = './files/member_extra_info/new_message_flags/' . getNumberingPath($logged_info->member_srl); From 9fc1926dc361af29c422dcb0be87f93732315f64 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 16 Mar 2016 19:19:53 +0900 Subject: [PATCH 117/205] Fix disappearing message & friend menu in other places, too --- modules/communication/communication.class.php | 18 +-------- .../communication.controller.php | 37 +++++++------------ 2 files changed, 15 insertions(+), 40 deletions(-) diff --git a/modules/communication/communication.class.php b/modules/communication/communication.class.php index fd8600e27..35f51b2a8 100644 --- a/modules/communication/communication.class.php +++ b/modules/communication/communication.class.php @@ -9,12 +9,10 @@ class communication extends ModuleObject { private $triggers = array( + array('moduleHandler.init', 'communication', 'controller', 'triggerModuleHandlerBefore', 'before'), array('moduleObject.proc', 'communication', 'controller', 'triggerModuleProcAfter', 'after'), array('member.getMemberMenu', 'communication', 'controller', 'triggerMemberMenu', 'before') ); - private $delete_triggers = array( - array('moduleHandler.init', 'communication', 'controller', 'triggerModuleHandlerBefore', 'before'), - ); /** * Implement if additional tasks are necessary when installing @@ -50,13 +48,6 @@ class communication extends ModuleObject return TRUE; } } - foreach($this->delete_triggers as $trigger) - { - if($oModuleModel->getTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4])) - { - return TRUE; - } - } if(!is_dir("./files/member_extra_info/new_message_flags")) { @@ -82,13 +73,6 @@ class communication extends ModuleObject $oModuleController->insertTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4]); } } - foreach($this->delete_triggers as $trigger) - { - if($oModuleModel->getTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4])) - { - $oModuleController->deleteTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4]); - } - } if(!is_dir("./files/member_extra_info/new_message_flags")) { diff --git a/modules/communication/communication.controller.php b/modules/communication/communication.controller.php index a287e8657..3845e8b20 100644 --- a/modules/communication/communication.controller.php +++ b/modules/communication/communication.controller.php @@ -799,29 +799,10 @@ class communicationController extends communication function triggerModuleHandlerBefore($obj) { - return $this->triggerModuleProcAfter($obj); - } - - function triggerModuleProcAfter($obj) - { - if(!Context::get('is_logged') || $obj->module == 'member') - { - return new Object(); - } - $logged_info = Context::get('logged_info'); - $oCommunicationModel = getModel('communication'); - $config = $oCommunicationModel->getConfig(); - if($obj->module == 'admin') - { - return new Object(); - } - if($config->enable_message == 'N' && $config->enable_friend == 'N') - { - return new Object(); - } - // Add menus on the member login information + $config = getModel('communication')->getConfig(); $oMemberController = getController('member'); + if($config->enable_message == 'Y') { $oMemberController->addMemberMenu('dispCommunicationMessages', 'cmd_view_message_box'); @@ -831,13 +812,23 @@ class communicationController extends communication { $oMemberController->addMemberMenu('dispCommunicationFriend', 'cmd_view_friend'); } + } + + function triggerModuleProcAfter($obj) + { + if (!Context::get('is_logged') || $obj->module == 'member' || $obj->module == 'admin') + { + return new Object(); + } - if (starts_with('dispCommunication', Context::get('act'))) + $config = getModel('communication')->getConfig(); + if ($config->enable_message == 'N' || starts_with('dispCommunication', Context::get('act'))) { return new Object(); } - if($config->enable_message == 'Y' && $obj->act != 'dispCommunicationNewMessage') + $logged_info = Context::get('logged_info'); + if ($config->enable_message == 'Y') { $flag_path = './files/member_extra_info/new_message_flags/' . getNumberingPath($logged_info->member_srl); $flag_file = $flag_path . $logged_info->member_srl; From 25cf1856a663141098f4415285c74c6b285c6a84 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Wed, 16 Mar 2016 19:22:53 +0900 Subject: [PATCH 118/205] Remove duplicate checking of new message flag file --- modules/communication/communication.view.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/modules/communication/communication.view.php b/modules/communication/communication.view.php index b76264cb6..28e043767 100644 --- a/modules/communication/communication.view.php +++ b/modules/communication/communication.view.php @@ -115,9 +115,6 @@ class communicationView extends communication // Extract a list $columnList = array('message_srl', 'readed', 'title', 'member.member_srl', 'member.nick_name', 'message.regdate', 'readed_date'); $output = $oCommunicationModel->getMessages($message_type, $columnList); - - // Update flag file - getController('communication')->updateFlagFile($logged_info->member_srl); // set a template file Context::set('total_count', $output->total_count); From 826aa8aa3659f0906c230c1893711b13a3674e3d Mon Sep 17 00:00:00 2001 From: BJRambo Date: Thu, 17 Mar 2016 06:05:05 +0900 Subject: [PATCH 119/205] add ncenterlite module from xe-public. --- modules/ncenterlite/LICENSE | 84 ++ modules/ncenterlite/LICENSE-ko | 97 ++ modules/ncenterlite/conf/info.xml | 14 + modules/ncenterlite/conf/module.xml | 30 + modules/ncenterlite/lang/lang.xml | 458 +++++++ .../m.skins/default/NotifyList.html | 24 + modules/ncenterlite/m.skins/default/img/p.png | Bin 0 -> 7463 bytes .../ncenterlite/m.skins/default/ncenter.html | 124 ++ .../m.skins/default/ncenterlite.black.css | 15 + .../default/ncenterlite.blacknoprofile.css | 17 + .../m.skins/default/ncenterlite.css | 185 +++ .../m.skins/default/ncenterlite.gray.css | 16 + .../default/ncenterlite.graynoprofile.css | 18 + .../m.skins/default/ncenterlite.html | 122 ++ .../m.skins/default/ncenterlite.mobile.css | 12 + .../m.skins/default/ncenterlite.white.css | 16 + .../default/ncenterlite.whitenoprofile.css | 18 + .../ncenterlite/m.skins/default/notify.css | 97 ++ modules/ncenterlite/m.skins/default/skin.xml | 32 + .../m.skins/default/userconfig.html | 66 ++ .../ncenterlite.admin.controller.php | 152 +++ .../ncenterlite/ncenterlite.admin.model.php | 105 ++ .../ncenterlite/ncenterlite.admin.view.php | 90 ++ modules/ncenterlite/ncenterlite.class.php | 213 ++++ .../ncenterlite/ncenterlite.controller.php | 1050 +++++++++++++++++ modules/ncenterlite/ncenterlite.mobile.php | 38 + modules/ncenterlite/ncenterlite.model.php | 376 ++++++ modules/ncenterlite/ncenterlite.view.php | 71 ++ modules/ncenterlite/queries/deleteNotify.xml | 10 + .../ncenterlite/queries/deleteNotifyAll.xml | 8 + .../queries/deleteNotifyByMemberSrl.xml | 10 + .../ncenterlite/queries/deleteNotifyBySrl.xml | 10 + .../queries/deleteNotifyByTargetSrl.xml | 10 + .../queries/getAdminNotifyList.xml | 11 + .../ncenterlite/queries/getAllUserConfig.xml | 11 + .../queries/getCountNewMessage.xml | 13 + .../ncenterlite/queries/getDispNotifyList.xml | 20 + .../ncenterlite/queries/getMemberAdmins.xml | 12 + .../ncenterlite/queries/getMemberSrlById.xml | 13 + .../queries/getMemberSrlByNickName.xml | 13 + .../ncenterlite/queries/getMemberTotals.xml | 12 + modules/ncenterlite/queries/getNotifyList.xml | 21 + .../ncenterlite/queries/getNotifyNewCount.xml | 14 + modules/ncenterlite/queries/getNotifyType.xml | 13 + .../ncenterlite/queries/getNotifyTypeByID.xml | 13 + .../ncenterlite/queries/getSocialxeCount.xml | 13 + modules/ncenterlite/queries/getUserConfig.xml | 13 + modules/ncenterlite/queries/insertNotify.xml | 24 + .../ncenterlite/queries/insertNotifyType.xml | 11 + .../ncenterlite/queries/insertUserConfig.xml | 12 + .../queries/updateNotifyReaded.xml | 12 + .../queries/updateNotifyReadedAll.xml | 11 + .../queries/updateNotifyReadedBySrl.xml | 13 + .../queries/updateNotifyReadedByTargetSrl.xml | 12 + .../queries/updateNotifyReadedByType.xml | 12 + .../ncenterlite/queries/updateUserConfig.xml | 14 + modules/ncenterlite/ruleset/insertConfig.xml | 13 + .../schemas/ncenterlite_notify.xml | 24 + .../schemas/ncenterlite_notify_type.xml | 6 + .../schemas/ncenterlite_user_set.xml | 7 + .../ncenterlite/skins/default/NotifyList.html | 46 + modules/ncenterlite/skins/default/img/p.png | Bin 0 -> 7463 bytes .../skins/default/ncenterlite.black.css | 15 + .../default/ncenterlite.blacknoprofile.css | 17 + .../ncenterlite/skins/default/ncenterlite.css | 185 +++ .../skins/default/ncenterlite.gray.css | 16 + .../default/ncenterlite.graynoprofile.css | 18 + .../skins/default/ncenterlite.html | 125 ++ .../skins/default/ncenterlite.mobile.css | 12 + .../skins/default/ncenterlite.white.css | 16 + .../default/ncenterlite.whitenoprofile.css | 18 + modules/ncenterlite/skins/default/skin.xml | 40 + .../ncenterlite/skins/default/userconfig.html | 68 ++ .../skins/default_bottom/NotifyList.html | 46 + .../skins/default_bottom/img/p.png | Bin 0 -> 7463 bytes .../default_bottom/ncenterlite.black.css | 15 + .../ncenterlite.blacknoprofile.css | 17 + .../skins/default_bottom/ncenterlite.css | 177 +++ .../skins/default_bottom/ncenterlite.gray.css | 16 + .../ncenterlite.graynoprofile.css | 18 + .../skins/default_bottom/ncenterlite.html | 124 ++ .../default_bottom/ncenterlite.mobile.css | 12 + .../default_bottom/ncenterlite.white.css | 16 + .../ncenterlite.whitenoprofile.css | 18 + .../ncenterlite/skins/default_bottom/skin.xml | 40 + .../skins/default_bottom/userconfig.html | 68 ++ modules/ncenterlite/tpl/Config.html | 272 +++++ modules/ncenterlite/tpl/css/ncenter_admin.css | 12 + modules/ncenterlite/tpl/header.html | 14 + modules/ncenterlite/tpl/js/ncenter_admin.js | 106 ++ modules/ncenterlite/tpl/js/ncenterlite.js | 57 + modules/ncenterlite/tpl/ncenter_list.html | 67 ++ 92 files changed, 5622 insertions(+) create mode 100644 modules/ncenterlite/LICENSE create mode 100644 modules/ncenterlite/LICENSE-ko create mode 100644 modules/ncenterlite/conf/info.xml create mode 100644 modules/ncenterlite/conf/module.xml create mode 100644 modules/ncenterlite/lang/lang.xml create mode 100644 modules/ncenterlite/m.skins/default/NotifyList.html create mode 100644 modules/ncenterlite/m.skins/default/img/p.png create mode 100644 modules/ncenterlite/m.skins/default/ncenter.html create mode 100644 modules/ncenterlite/m.skins/default/ncenterlite.black.css create mode 100644 modules/ncenterlite/m.skins/default/ncenterlite.blacknoprofile.css create mode 100644 modules/ncenterlite/m.skins/default/ncenterlite.css create mode 100644 modules/ncenterlite/m.skins/default/ncenterlite.gray.css create mode 100644 modules/ncenterlite/m.skins/default/ncenterlite.graynoprofile.css create mode 100644 modules/ncenterlite/m.skins/default/ncenterlite.html create mode 100644 modules/ncenterlite/m.skins/default/ncenterlite.mobile.css create mode 100644 modules/ncenterlite/m.skins/default/ncenterlite.white.css create mode 100644 modules/ncenterlite/m.skins/default/ncenterlite.whitenoprofile.css create mode 100644 modules/ncenterlite/m.skins/default/notify.css create mode 100644 modules/ncenterlite/m.skins/default/skin.xml create mode 100644 modules/ncenterlite/m.skins/default/userconfig.html create mode 100644 modules/ncenterlite/ncenterlite.admin.controller.php create mode 100644 modules/ncenterlite/ncenterlite.admin.model.php create mode 100644 modules/ncenterlite/ncenterlite.admin.view.php create mode 100644 modules/ncenterlite/ncenterlite.class.php create mode 100644 modules/ncenterlite/ncenterlite.controller.php create mode 100644 modules/ncenterlite/ncenterlite.mobile.php create mode 100644 modules/ncenterlite/ncenterlite.model.php create mode 100644 modules/ncenterlite/ncenterlite.view.php create mode 100644 modules/ncenterlite/queries/deleteNotify.xml create mode 100644 modules/ncenterlite/queries/deleteNotifyAll.xml create mode 100644 modules/ncenterlite/queries/deleteNotifyByMemberSrl.xml create mode 100644 modules/ncenterlite/queries/deleteNotifyBySrl.xml create mode 100644 modules/ncenterlite/queries/deleteNotifyByTargetSrl.xml create mode 100644 modules/ncenterlite/queries/getAdminNotifyList.xml create mode 100644 modules/ncenterlite/queries/getAllUserConfig.xml create mode 100644 modules/ncenterlite/queries/getCountNewMessage.xml create mode 100644 modules/ncenterlite/queries/getDispNotifyList.xml create mode 100644 modules/ncenterlite/queries/getMemberAdmins.xml create mode 100644 modules/ncenterlite/queries/getMemberSrlById.xml create mode 100644 modules/ncenterlite/queries/getMemberSrlByNickName.xml create mode 100644 modules/ncenterlite/queries/getMemberTotals.xml create mode 100644 modules/ncenterlite/queries/getNotifyList.xml create mode 100644 modules/ncenterlite/queries/getNotifyNewCount.xml create mode 100644 modules/ncenterlite/queries/getNotifyType.xml create mode 100644 modules/ncenterlite/queries/getNotifyTypeByID.xml create mode 100644 modules/ncenterlite/queries/getSocialxeCount.xml create mode 100644 modules/ncenterlite/queries/getUserConfig.xml create mode 100644 modules/ncenterlite/queries/insertNotify.xml create mode 100644 modules/ncenterlite/queries/insertNotifyType.xml create mode 100644 modules/ncenterlite/queries/insertUserConfig.xml create mode 100644 modules/ncenterlite/queries/updateNotifyReaded.xml create mode 100644 modules/ncenterlite/queries/updateNotifyReadedAll.xml create mode 100644 modules/ncenterlite/queries/updateNotifyReadedBySrl.xml create mode 100644 modules/ncenterlite/queries/updateNotifyReadedByTargetSrl.xml create mode 100644 modules/ncenterlite/queries/updateNotifyReadedByType.xml create mode 100644 modules/ncenterlite/queries/updateUserConfig.xml create mode 100644 modules/ncenterlite/ruleset/insertConfig.xml create mode 100644 modules/ncenterlite/schemas/ncenterlite_notify.xml create mode 100644 modules/ncenterlite/schemas/ncenterlite_notify_type.xml create mode 100644 modules/ncenterlite/schemas/ncenterlite_user_set.xml create mode 100644 modules/ncenterlite/skins/default/NotifyList.html create mode 100644 modules/ncenterlite/skins/default/img/p.png create mode 100644 modules/ncenterlite/skins/default/ncenterlite.black.css create mode 100644 modules/ncenterlite/skins/default/ncenterlite.blacknoprofile.css create mode 100644 modules/ncenterlite/skins/default/ncenterlite.css create mode 100644 modules/ncenterlite/skins/default/ncenterlite.gray.css create mode 100644 modules/ncenterlite/skins/default/ncenterlite.graynoprofile.css create mode 100644 modules/ncenterlite/skins/default/ncenterlite.html create mode 100644 modules/ncenterlite/skins/default/ncenterlite.mobile.css create mode 100644 modules/ncenterlite/skins/default/ncenterlite.white.css create mode 100644 modules/ncenterlite/skins/default/ncenterlite.whitenoprofile.css create mode 100644 modules/ncenterlite/skins/default/skin.xml create mode 100644 modules/ncenterlite/skins/default/userconfig.html create mode 100644 modules/ncenterlite/skins/default_bottom/NotifyList.html create mode 100644 modules/ncenterlite/skins/default_bottom/img/p.png create mode 100644 modules/ncenterlite/skins/default_bottom/ncenterlite.black.css create mode 100644 modules/ncenterlite/skins/default_bottom/ncenterlite.blacknoprofile.css create mode 100644 modules/ncenterlite/skins/default_bottom/ncenterlite.css create mode 100644 modules/ncenterlite/skins/default_bottom/ncenterlite.gray.css create mode 100644 modules/ncenterlite/skins/default_bottom/ncenterlite.graynoprofile.css create mode 100644 modules/ncenterlite/skins/default_bottom/ncenterlite.html create mode 100644 modules/ncenterlite/skins/default_bottom/ncenterlite.mobile.css create mode 100644 modules/ncenterlite/skins/default_bottom/ncenterlite.white.css create mode 100644 modules/ncenterlite/skins/default_bottom/ncenterlite.whitenoprofile.css create mode 100644 modules/ncenterlite/skins/default_bottom/skin.xml create mode 100644 modules/ncenterlite/skins/default_bottom/userconfig.html create mode 100644 modules/ncenterlite/tpl/Config.html create mode 100644 modules/ncenterlite/tpl/css/ncenter_admin.css create mode 100644 modules/ncenterlite/tpl/header.html create mode 100644 modules/ncenterlite/tpl/js/ncenter_admin.js create mode 100644 modules/ncenterlite/tpl/js/ncenterlite.js create mode 100644 modules/ncenterlite/tpl/ncenter_list.html diff --git a/modules/ncenterlite/LICENSE b/modules/ncenterlite/LICENSE new file mode 100644 index 000000000..2fb57a6e4 --- /dev/null +++ b/modules/ncenterlite/LICENSE @@ -0,0 +1,84 @@ +GNU GENERAL PUBLIC LICENSE + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +Preamble + +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + +a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. +b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. +c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: + +a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, +b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, +c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) +The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. \ No newline at end of file diff --git a/modules/ncenterlite/LICENSE-ko b/modules/ncenterlite/LICENSE-ko new file mode 100644 index 000000000..f51a8569f --- /dev/null +++ b/modules/ncenterlite/LICENSE-ko @@ -0,0 +1,97 @@ +한국어 번역에 대한 안내 + +이 문서는 GNU General Public License를 한국어로 번역된 것이며 해석의 오류 또는 해석이 달라질 수 있으므로 해석에 도움을 받는 것 외 법적 효력이 없으며, 원문의 규정을 따르도록 합니다. +원문은 http://www.gnu.org/licenses/gpl-2.0.html 에서 볼 수 있습니다. + +-------------------------------------------------------------------------------- + +GNU 일반 공중 사용 허가서 + +2판, 1991년 6월 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +누구든지 본 사용 허가서를 있는 그대로 복제하고 배포할 수 +있습니다. 그러나 본문에 대한 수정은 허용되지 않습니다. + +전 문 + +소프트웨어에 적용되는 대부분의 사용 허가서(license)들은 소프트웨어에 대한 수정과 공유의 자유를 제한하려는 것을 그 목적으로 합니다. 그러나 GNU 일반 공중 사용 허가서(이하, ``GPL''이라고 칭합니다.)는 자유 소프트웨어에 대한 수정과 공유의 자유를 모든 사용자들에게 보장하기 위해서 성립된 것입니다. 자유 소프트웨어 재단이 제공하는 대부분의 소프트웨어들은 GPL에 의해서 관리되고 있으며, 몇몇 소프트웨어에는 별도의 사용 허가서인 GNU 라이브러리 일반 공중 사용 허가서(GNU Library General Public License)를 대신 적용하기도 합니다. 자유 소프트웨어란, 이를 사용하려고 하는 모든 사람에 대해서 동일한 자유와 권리가 함께 양도되는 소프트웨어를 말하며 프로그램 저작자의 의지에 따라 어떠한 종류의 프로그램에도 GPL을 적용할 수 있습니다. 따라서 여러분이 만든 프로그램에도 GPL을 적용할 수 있습니다. + +자유 소프트웨어를 언급할 때 사용되는 ``자유''라는 단어는 무료(無料)를 의미하는 금전적인 측면의 자유가 아니라 구속되지 않는다는 관점에서의 자유를 의미하며, GPL은 자유 소프트웨어를 이용한 복제와 개작, 배포와 수익 사업 등의 가능한 모든 형태의 자유를 실질적으로 보장하고 있습니다. 여기에는 원시 코드(source code)의 전부 또는 일부를 원용해서 개선된 프로그램을 만들거나 새로운 프로그램을 창작할 수 있는 자유가 포함되며, 자신에게 양도된 이러한 자유와 권리를 보다 명확하게 인식할 수 있도록 하기 위한 규정도 포함되어 있습니다. + +GPL은 GPL 안에 소프트웨어를 양도받을 사용자의 권리를 제한하는 조항과 단서를 별항으로 추가시키지 못하게 함으로써 사용자들의 자유와 권리를 실제적으로 보장하고 있습니다. 자유 소프트웨어의 개작과 배포에 관계하고 있는 사람들은 이러한 무조건적인 권리 양도 규정을 준수해야만 합니다. + +예를 들어 GPL 프로그램을 배포할 경우에는 프로그램의 유료 판매나 무료 배포에 관계없이 자신이 해당 프로그램에 대해서 가질 수 있었던 모든 권리를, 프로그램을 받게될 사람에게 그대로 양도해 주어야 합니다. 이 경우, 프로그램의 원시 코드를 함께 제공하거나 원시 코드를 구할 수 있는 방법을 확실히 알려주어야 하고 이러한 모든 사항들을 사용자들이 분명히 알 수 있도록 명시해야 합니다. + +자유 소프트웨어 재단은 다음과 같은 두 가지 단계를 통해서 사용자들을 권리를 보호합니다. (1) 소프트웨어에 저작권을 설정합니다. (2) 저작권의 양도에 관한 실정법에 의해서 유효한 법률적 효력을 갖는 GPL을 통해 소프트웨어를 복제하거나 개작 및 배포할 수 있는 권리를 사용자들에게 부여합니다. + +자유 소프트웨어를 사용하는 사람들은 반복적인 재배포 과정을 통해 소프트웨어 자체에 수정과 변형이 일어날 수도 있으며, 이는 최초의 저작자가 만든 소프트웨어가 갖고 있는 문제가 아닐 수 있다는 개연성을 인식하고 있어야 합니다. 우리는 개작과 재배포 과정에서 다른 사람에 의해 발생된 문제로 인해 프로그램 원저작자들의 신망이 훼손되는 것을 원하지 않습니다. GPL에 자유 소프트웨어에 대한 어떠한 형태의 보증도 규정하지 않는 이유는 이러한 점들이 고려되었기 때문이며, 이는 프로그램 원저작자와 자유 소프트웨어 재단의 자유로운 활동을 보장하는 현실적인 수단이기도 합니다. + +특허 제도는 자유 소프트웨어의 발전을 위협하는 요소일 수밖에 없습니다. 자유 프로그램을 재배포하는 사람들이 개별적으로 특허를 취득하게 되면, 결과적으로 그 프로그램이 독점 소프트웨어가 될 가능성이 있습니다. 자유 소프트웨어 재단은 이러한 문제에 대처하기 위해서 어떠한 특허에 대해서도 그 사용 권리를 모든 사람들(이하, ``공중(公衆)''이라고 칭합니다.)에게 자유롭게 허용하는 경우에 한해서만 자유 소프트웨어와 함께 사용할 수 있다는 것을 명확히 밝히고 있습니다. + +복제(copying)와 개작(modification) 및 배포(distribution)에 관련된 구체적인 조건과 규정은 다음과 같습니다. + +복제와 개작 및 배포에 관한 조건과 규정 + +제 0 조. 본 허가서는 GNU 일반 공중 사용 허가서의 규정에 따라 배포될 수 있다는 사항이 저작권자에 의해서 명시된 모든 컴퓨터 프로그램 저작물에 대해서 동일하게 적용됩니다. 컴퓨터 프로그램 저작물(이하, ``프로그램''이라고 칭합니다.)이란 특정한 결과를 얻기 위해서 컴퓨터 등의 정보 처리 능력을 가진 장치(이하, ``컴퓨터''라고 칭합니다.) 내에서 직접 또는 간접으로 사용되는 일련의 지시 및 명령으로 표현된 창작물을 의미하고, ``2차적 프로그램''이란 전술한 프로그램 자신 또는 저작권법의 규정에 따라 프로그램의 전부 또는 상당 부분을 원용하거나 다른 언어로의 번역을 포함할 수 있는 개작 과정을 통해서 창작된 새로운 프로그램과 이와 관련된 저작물을 의미합니다. (이후로 다른 언어로의 번역은 별다른 제한없이 개작의 범위에 포함되는 것으로 간주합니다.) ``피양도자''란 GPL의 규정에 따라 프로그램을 양도받은 사람을 의미하고, ``원(原)프로그램''이란 프로그램을 개작하거나 2차적 프로그램을 만들기 위해서 사용된 최초의 프로그램을 의미합니다. + +본 허가서는 프로그램에 대한 복제와 개작 그리고 배포 행위에 대해서만 적용됩니다. 따라서 프로그램을 실행시키는 행위에 대한 제한은 없습니다. 프로그램의 결과물(output)에는, 그것이 프로그램을 실행시켜서 생성된 것인지 아닌지의 여부에 상관없이 결과물의 내용이 원프로그램으로부터 파생된 2차적 프로그램을 구성했을 때에 한해서 본 허가서의 규정들이 적용됩니다. 2차적 프로그램의 구성 여부는 2차적 프로그램 안에서의 원프로그램의 역할을 토대로 판단합니다. + +제 1 조. 적절한 저작권 표시와 프로그램에 대한 보증이 제공되지 않는다는 사실을 각각의 복제물에 명시하는 한, 피양도자는 프로그램의 원시 코드를 자신이 양도받은 상태 그대로 어떠한 매체를 통해서도 복제하고 배포할 수 있습니다. 복제와 배포가 이루어 질 때는 본 허가서와 프로그램에 대한 보증이 제공되지 않는다는 사실에 대해서 언급되었던 모든 내용을 그대로 유지시켜야 하며, 영문판 GPL을 함께 제공해야 합니다. + +배포자는 복제물을 물리적으로 인도하는데 소요된 비용을 청구할 수 있으며, 선택 사항으로 독자적인 유료 보증을 설정할 수 있습니다. + +제 2 조. 피양도자는 자신이 양도받은 프로그램의 전부나 일부를 개작할 수 있으며, 이를 통해서 2차적 프로그램을 창작할 수 있습니다. 개작된 프로그램이나 창작된 2차적 프로그램은 다음의 사항들을 모두 만족시키는 조건에 한해서, 제1조의 규정에 따라 또다시 복제되고 배포될 수 있습니다. + +제 1 항. 파일을 개작할 때는 파일을 개작한 사실과 그 날짜를 파일 안에 명시해야 합니다. +제 2 항. 배포하거나 공표하려는 저작물의 전부 또는 일부가 양도받은 프로그램으로부터 파생된 것이라면, 저작물 전체에 대한 사용 권리를 본 허가서의 규정에 따라 공중에게 무상으로 허용해야 합니다. + +제 3 항. 개작된 프로그램의 일반적인 실행 형태가 대화형 구조로 명령어를 읽어 들이는 방식을 취하고 있을 경우에는, 적절한 저작권 표시와 프로그램에 대한 보증이 제공되지 않는다는 사실, (별도의 보증을 설정한 경우라면 해당 내용) 그리고 양도받은 프로그램을 본 규정에 따라 재배포할 수 있다는 사실과 GPL 사본을 참고할 수 있는 방법이 함께 포함된 문구가 프로그램이 대화형 구조로 평이하게 실행된 직후에 화면 또는 지면으로 출력되도록 작성되어야 합니다. (예외 규정: 양도받은 프로그램이 대화형 구조를 갖추고 있다 하더라도 통상적인 실행 환경에서 전술한 사항들이 출력되지 않는 형태였을 경우에는 이를 개작한 프로그램 또한 관련 사항들을 출력시키지 않아도 무방합니다.) + +위의 조항들은 개작된 프로그램 전체에 적용됩니다. 만약, 개작된 프로그램에 포함된 특정 부분이 원프로그램으로부터 파생된 것이 아닌 별도의 독립 저작물로 인정될 만한 상당한 이유가 있을 경우에는 해당 저작물의 개별적인 배포에는 본 허가서의 규정들이 적용되지 않습니다. 그러나 이러한 저작물이 2차적 프로그램의 일부로서 함께 배포된다면 개별적인 저작권과 배포 기준에 상관없이 저작물 모두에 본 허가서가 적용되어야 하며, 전체 저작물에 대한 사용 권리는 공중에게 무상으로 양도됩니다. + +이러한 규정은 개별적인 저작물에 대한 저작자의 권리를 침해하거나 인정하지 않으려는 것이 아니라, 원프로그램으로부터 파생된 2차적 프로그램이나 수집 저작물의 배포를 일관적으로 규제할 수 있는 권리를 행사하기 위한 것입니다. + +원프로그램이나 원프로그램으로부터 파생된 2차적 프로그램을 이들로부터 파생되지 않은 다른 저작물과 함께 단순히 저장하거나 배포할 목적으로 동일한 매체에 모아 놓은 집합물의 경우에는, 원프로그램으로부터 파생되지 않은 다른 저작물에는 본 허가서의 규정들이 적용되지 않습니다. + +제 3 조. 피양도자는 다음 중 하나의 항목을 만족시키는 조건에 한해서 제1조와 제2조의 규정에 따라 프로그램(또는 제2조에서 언급된 2차적 프로그램)을 목적 코드(object code)나 실행물(executable form)의 형태로 복제하고 배포할 수 있습니다. + +제 1 항. 목적 코드나 실행물에 상응하는 컴퓨터가 인식할 수 있는 완전한 원시 코드를 함께 제공해야 합니다. 원시 코드는 제1조와 제2조의 규정에 따라 배포될 수 있어야 하며, 소프트웨어의 교환을 위해서 일반적으로 사용되는 매체를 통해 제공되어야 합니다. +제 2 항. 배포에 필요한 최소한의 비용만을 받고 목적 코드나 실행물에 상응하는 완전한 원시 코드를 배포하겠다는, 최소한 3년간 유효한 약정서를 함께 제공해야 합니다. 이 약정서는 약정서를 갖고 있는 어떠한 사람에 대해서도 유효해야 합니다. 원시 코드는 컴퓨터가 인식할 수 있는 형태여야 하고 제1조와 제2조의 규정에 따라 배포될 수 있어야 하며, 소프트웨어의 교환을 위해서 일반적으로 사용되는 매체를 통해 제공되어야 합니다. + +제 3 항. 목적 코드나 실행물에 상응하는 원시 코드를 배포하겠다는 약정에 대해서 자신이 양도받은 정보를 함께 제공해야 합니다. (제3항은 위의 제2항에 따라 원시 코드를 배포하겠다는 약정을 프로그램의 목적 코드나 실행물과 함께 제공 받았고, 동시에 비상업적인 배포를 하고자 할 경우에 한해서만 허용됩니다.) + +저작물에 대한 원시 코드란 해당 저작물을 개작하기에 적절한 형식을 의미합니다. 실행물에 대한 완전한 원시 코드란 실행물에 포함된 모든 모듈들의 원시 코드와 이와 관련된 인터페이스 정의 파일 모두, 그리고 실행물의 컴파일과 설치를 제어하는데 사용된 스크립트 전부를 의미합니다. 그러나 특별한 예외의 하나로서, 실행물이 실행될 운영체제의 주요 부분(컴파일러나 커널 등)과 함께 (원시 코드나 바이너리의 형태로) 일반적으로 배포되는 구성 요소들은 이러한 구성 요소 자체가 실행물에 수반되지 않는 한 원시 코드의 배포 대상에서 제외되어도 무방합니다. + +목적 코드나 실행물을 지정한 장소로부터 복제해 갈 수 있게 하는 방식으로 배포할 경우, 동일한 장소로부터 원시 코드를 복제할 수 있는 동등한 접근 방법을 제공한다면 이는 원시 코드를 목적 코드와 함께 복제되도록 설정하지 않았다고 하더라도 원시 코드를 배포하는 것으로 간주됩니다. + +제 4 조. 본 허가서에 의해 명시적으로 이루어 지지 않는 한 프로그램에 대한 복제와 개작 및 하위 허가권 설정과 배포가 성립될 수 없습니다. 이와 관련된 어떠한 행위도 무효이며 본 허가서가 보장한 권리는 자동으로 소멸됩니다. 그러나 본 허가서의 규정에 따라 프로그램의 복제물이나 권리를 양도받았던 제3자는 본 허가서의 규정들을 준수하는 한, 배포자의 권리 소멸에 관계없이 사용상의 권리를 계속해서 유지할 수 있습니다. + +제 5 조. 본 허가서는 서명이나 날인이 수반되는 형식을 갖고 있지 않기 때문에 피양도자가 본 허가서의 내용을 반드시 받아들여야 할 필요는 없습니다. 그러나 프로그램이나 프로그램에 기반한 2차적 프로그램에 대한 개작 및 배포를 허용하는 것은 본 허가서에 의해서만 가능합니다. 만약 본 허가서에 동의하지 않을 경우에는 이러한 행위들이 법률적으로 금지됩니다. 따라서 프로그램(또는 프로그램에 기반한 2차적 프로그램)을 개작하거나 배포하는 행위는 이에 따른 본 허가서의 내용에 동의한다는 것을 의미하며, 복제와 개작 및 배포에 관한 본 허가서의 조건과 규정들을 모두 받아들이겠다는 의미로 간주됩니다. + +제 6 조. 피양도자에 의해서 프로그램(또는 프로그램에 기반한 2차적 프로그램)이 반복적으로 재배포될 경우, 각 단계에서의 피양도자는 본 허가서의 규정에 따른 프로그램의 복제와 개작 및 배포에 대한 권리를 최초의 양도자로부터 양도받은 것으로 자동적으로 간주됩니다. 프로그램(또는 프로그램에 기반한 2차적 프로그램)을 배포할 때는 피양도자의 권리의 행사를 제한할 수 있는 어떠한 사항도 추가할 수 없습니다. 그러나 피양도자에게, 재배포가 일어날 시점에서의 제3의 피양도자에게 본 허가서를 준수하도록 강제할 책임은 부과되지 않습니다. + +제 7 조. 법원의 판결이나 특허권 침해에 대한 주장 또는 특허 문제에 국한되지 않은 그밖의 이유들로 인해서 본 허가서의 규정에 배치되는 사항이 발생한다 하더라도 그러한 사항이 선행하거나 본 허가서의 조건과 규정들이 면제되는 것은 아닙니다. 따라서 법원의 명령이나 합의 등에 의해서 본 허가서에 위배되는 사항들이 발생한 상황이라도 양측 모두를 만족시킬 수 없다면 프로그램은 배포될 수 없습니다. 예를 들면, 특정한 특허 관련 허가가 프로그램의 복제물을 직접 또는 간접적인 방법으로 양도받은 임의의 제3자에게 해당 프로그램을 무상으로 재배포할 수 있게 허용하지 않는다면, 그러한 허가와 본 사용 허가를 동시에 만족시키면서 프로그램을 배포할 수 있는 방법은 없습니다. + +본 조항은 특정한 상황에서 본 조항의 일부가 유효하지 않거나 적용될 수 없을 경우에도 본 조항의 나머지 부분들을 적용하기 위한 의도로 만들어 졌습니다. 따라서 그 이외의 상황에서는 본 조항을 전체적으로 적용하면 됩니다. + +본 조항의 목적은 특허나 저작권 침해 등의 행위를 조장하거나 해당 권리를 인정하지 않으려는 것이 아니라, GPL을 통해서 구현되어 있는 자유 소프트웨어의 배포 체계를 통합적으로 보호하기 위한 것입니다. 많은 사람들이 배포 체계에 대한 신뢰있는 지원을 계속해 줌으로써 소프트웨어의 다양한 분야에 많은 공헌을 해 주었습니다. 소프트웨어를 어떠한 배포 체계로 배포할 것인가를 결정하는 것은 전적으로 저작자와 기증자들의 의지에 달려있는 것이지, 일반 사용자들이 강요할 수 있는 문제는 아닙니다. + +본 조항은 본 허가서의 다른 조항들에서 무엇이 중요하게 고려되어야 하는 지를 명확하게 설명하기 위한 목적으로 만들어진 것입니다. + +제 8 조. 특허나 저작권이 설정된 인터페이스로 인해서 특정 국가에서 프로그램의 배포와 사용이 함께 또는 개별적으로 제한되어 있는 경우, 본 사용 허가서를 프로그램에 적용한 최초의 저작권자는 문제가 발생하지 않는 국가에 한해서 프로그램을 배포한다는 배포상의 지역적 제한 조건을 명시적으로 설정할 수 있으며, 이러한 사항은 본 허가서의 일부로 간주됩니다. + +제 9 조. 자유 소프트웨어 재단은 때때로 본 사용 허가서의 개정판이나 신판을 공표할 수 있습니다. 새롭게 공표될 판은 당면한 문제나 현안을 처리하기 위해서 세부적인 내용에 차이가 발생할 수 있지만, 그 근본 정신에는 변함이 없을 것입니다. + +각각의 판들은 판번호를 사용해서 구별됩니다. 특정한 판번호와 그 이후 판을 따른다는 사항이 명시된 프로그램에는 해당 판이나 그 이후에 발행된 어떠한 판을 선택해서 적용해도 무방하고, 판번호를 명시하고 있지 않은 경우에는 자유 소프트웨어 재단이 공표한 어떠한 판번호의 판을 적용해도 무방합니다. + +제 10 조. 프로그램의 일부를 본 허가서와 배포 기준이 다른 자유 프로그램과 함께 결합하고자 할 경우에는 해당 프로그램의 저작자로부터 서면 승인을 받아야 합니다. 자유 소프트웨어 재단이 저작권을 갖고 있는 소프트웨어의 경우에는 자유 소프트웨어 재단의 승인을 얻어야 합니다. 우리는 이러한 요청을 수락하기 위해서 때때로 예외 기준을 만들기도 합니다. 자유 소프트웨어 재단은 일반적으로 자유 소프트웨어의 2차적 저작물들을 모두 자유로운 상태로 유지시키려는 목적과 소프트웨어의 공유와 재활용을 증진시키려는 두가지 목적을 기준으로 승인 여부를 결정할 것입니다. + + +보증의 결여 (제11조, 제12조) + +제 11 조. 본 허가서를 따르는 프로그램은 무상으로 양도되기 때문에 관련 법률이 허용하는 한도 내에서 어떠한 형태의 보증도 제공되지 않습니다. 프로그램의 저작권자와 배포자가 공동 또는 개별적으로 별도의 보증을 서면으로 제공할 때를 제외하면, 특정한 목적에 대한 프로그램의 적합성이나 상업성 여부에 대한 보증을 포함한 어떠한 형태의 보증도 명시적이나 묵시적으로 설정되지 않은 ``있는 그대로의'' 상태로 이 프로그램을 배포합니다. 프로그램과 프로그램의 실행에 따라 발생할 수 있는 모든 위험은 피양도자에게 인수되며 이에 따른 보수 및 복구를 위한 제반 경비 또한 피양도자가 모두 부담해야 합니다. + +제 12 조. 저작권자나 배포자가 프로그램의 손상 가능성을 사전에 알고 있었다 하더라도 발생된 손실이 관련 법규에 의해 보호되고 있거나 이에 대한 별도의 서면 보증이 설정된 경우가 아니라면, 저작권자나 프로그램을 원래의 상태 또는 개작한 상태로 제공한 배포자는 프로그램의 사용이나 비작동으로 인해 발생된 손실이나 프로그램 자체의 손실에 대해 책임지지 않습니다. 이러한 면책 조건은 사용자나 제3자가 프로그램을 조작함으로써 발생된 손실이나 다른 소프트웨어와 프로그램을 함께 동작시키는 것으로 인해서 발생된 데이터의 상실 및 부정확한 산출 결과에만 국한되는 것이 아닙니다. 발생된 손실의 일반성이나 특수성 뿐 아니라 원인의 우발성 및 필연성도 전혀 고려되지 않습니다. diff --git a/modules/ncenterlite/conf/info.xml b/modules/ncenterlite/conf/info.xml new file mode 100644 index 000000000..6d943f59c --- /dev/null +++ b/modules/ncenterlite/conf/info.xml @@ -0,0 +1,14 @@ + + + XE 알림센터 Lite + XE Notification Center Lite + 사이트 사용자간의 커뮤니케이션에 대한 정보를 알려주는 모듈입니다. + This module notify users of information about new documents, comments and/or messages that call them. This module will enhance communication beween site users. + 2.1.5 + 2014-12-09 + content + + XE Public + XE Public + + diff --git a/modules/ncenterlite/conf/module.xml b/modules/ncenterlite/conf/module.xml new file mode 100644 index 000000000..489517102 --- /dev/null +++ b/modules/ncenterlite/conf/module.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + XE 알림센터 Lite + XE Notification Center Lite + XE 通知センター Lite + + + diff --git a/modules/ncenterlite/lang/lang.xml b/modules/ncenterlite/lang/lang.xml new file mode 100644 index 000000000..d6a8762c3 --- /dev/null +++ b/modules/ncenterlite/lang/lang.xml @@ -0,0 +1,458 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %1$s님이 "%2$s"라고 글을 남겼습니다.]]> + %1$s wrote an article as "%2$s".]]> + %1$s wrote an article as "%2$s".]]> + + + %1$s님이 게시판 "%2$s""%3$s"라고 글을 남겼습니다.]]> + %1$s wrote an article as "%3$s" on the board %2$s.]]> + %1$s wrote an article as "%3$s" on the board %2$s.]]> + + + %1$s님이 회원님의 %2$s에 "%3$s"라고 댓글을 남겼습니다.]]> + %1$s commented as "%3$s" on your %2$s.]]> + %sさんがあなたの%sに「%s」とコメントしました。]]> + + + %1$s님이 게시판 "%2$s""%3$s"라고 댓글을 남겼습니다.]]> + %1$s commented as "%3$s" on the board %2$s.]]> + %1$s commented as "%3$s" on the board %2$s.]]> + + + %s님이 "%s" %s에서 회원님을 언급하였습니다.]]> + %s mentioned you on "%s" %s.]]> + %sさんが「%s」%sにあなたをタグ付けしました。]]> + + + %d개의 읽지 않은 메시지가 있습니다.]]> + %d new message(s).]]> + %d件の読んでないメッセージがあります。]]> + + + %s님께서 "%s"라고 메세지를 보내셨습니다.]]> + %1$s sent a message as "%2$s".]]> + %1$s sent a message as "%2$s".]]> + + + %s님! 스킨 테스트 알림을 완료했습니다.]]> + %s! Skin test notification has been done.]]> + %s! Skin test notification has been done.]]> + + + %s님이 "%s" 글을 추천하였습니다.]]> + %1$s marked the article "%2$s" with a recommendation.]]> + %1$s marked the article "%2$s" with a recommendation.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %s개의 알림이 있습니다.]]> + %s notification.]]> + %s件のお知らせがあります。]]> + + + %s개의 알림이 있습니다.]]> + %s notifications.]]> + %s件のお知らせがあります。]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 도와주세요! 사용 환경 정보 제공 동의]]> + HELP! Agreement on gathering server environment information]]> + HELP! Agreement on gathering server environment information]]> + + + '알림센터 Lite'를 자유소프트웨어로 제공하고 있습니다.

+

'알림센터 Lite'는 사용자의 버그 보고, 의견 제출을 통해 공동의 힘으로 발전하고 있습니다.
'제공 동의'를 선택하면 아래와 같은 항목이 전송되며, 알림센터 Lite의 기능을 개선하는데 참고하겠습니다. 감사합니다.

+
    +
  • 이 사이트의 도메인
  • +
  • 알림센터 Lite의 버전
  • +
  • XE 버전
  • +
  • PHP 버전
  • +
+ ]]>
+ 'Notification Center Lite' is distributed as an free software.

+

'Notification Center Lite' is developing with collective intelligence, such as users' bug reports, suggestions and/or contributions.
If you select to 'Agree on gathering', the items below will be gathered by developers of this program, and the information will be referenced to enhance functions of this program. Thank you.

+
    +
  • Domain name of this website
  • +
  • Version value of Notification Center Lite
  • +
  • XE Core version value
  • +
  • PHP version value
  • +
+ ]]>
+ 'Notification Center Lite' is distributed as an free software.

+

'Notification Center Lite' is developing with collective intelligence, such as users' bug reports, suggestions and/or contributions.
If you select to 'Agree on gathering', the items below will be gathered by developers of this program, and the information will be referenced to enhance functions of this program. Thank you.

+
    +
  • Domain name of this website
  • +
  • Version value of Notification Center Lite
  • +
  • XE Core version value
  • +
  • PHP version value
  • +
+ ]]>
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 주의! 알림센터를 레이아웃이나 위젯으로 표현할 수 있을 때에만 동작안함 선택하세요.]]> + Watch out! 알림센터를 레이아웃이나 위젯으로 표현할 수 있을 때에만 동작안함 선택하세요.]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
\ No newline at end of file diff --git a/modules/ncenterlite/m.skins/default/NotifyList.html b/modules/ncenterlite/m.skins/default/NotifyList.html new file mode 100644 index 000000000..6c111c5a6 --- /dev/null +++ b/modules/ncenterlite/m.skins/default/NotifyList.html @@ -0,0 +1,24 @@ + +
+

{$lang->ncenterlite_my_list}

+
    +
  • + {@ + $oMemberModel = getModel('member'); + $member_info = $oMemberModel->getMemberInfoByMemberSrl($val->member_srl); + } +
    + + {$val->target_nick_name} -> + {$member_info->nick_name} + +
    + {$val->text} + ({zdate($val->regdate,"Y-m-d H:i:s")}) + ({$lang->ncenterlite_read_y}) + ({$lang->ncenterlite_read_n}) +
    +
    +
  • +
+
\ No newline at end of file diff --git a/modules/ncenterlite/m.skins/default/img/p.png b/modules/ncenterlite/m.skins/default/img/p.png new file mode 100644 index 0000000000000000000000000000000000000000..44e3d8d1203e991132d400e26b13aa3e469862d9 GIT binary patch literal 7463 zcmV+?9oXWDP)5000;CX+uL$Nkc;* zP;zf(X>4Tx09b{USqC_l(c3@I^SsM=?Y(91y_3E79;v+crq@;^B&(2-LJEl#O;kc8 zC0U75q!3D_fvkKFjsO4qkL$a>b6w{=zxRIbbD#U%_kG6wyZ~_AAcchm!E^uwheVKV zjdb~(U0nI_uK*0_0VCi5j3m$SFdb`abMRlw(lCIwo=>H=Wc_!(|5)g~yu&>KfUHsM z;T7%~jN%&rAWfcOBf_?D4vLw`sDdc2+`_)UaMKp{_=S76dF*ZVP&@$u zfto}j`vO3mN9lY~p1!CY8fpM=3?W|rAt);!ig$VXk-Pxla7TG0gM&l2a2AS1J^o3K z@BfnN@mD5^bLzv)$B5xTa&>COHTCiYu4Lh+Daq_M+qJR2G4po3yA6t4${nr-o;c7oYL zJS?qH%z$D+&u~50Uov6kI6r%*-#q4C-ueb8MxzLJCWqSo;r9!Vvin1ii}Ta7{KFqW zGTrhw4T>{J1HVK}K8S?m+RSun24W-(&TL1X=!`3q0jRHnjbXSG~ix=es>t zgrB|fFZl>v1R4OpWFlNX{)Q&MbU`?8%X~TXqmcv5$9%!*7{L zh8IcS?3YcD5?}~OfDF8W2M7ThfDf1hJ)r-)J_{FBK4(#PX(kAK(65KL`L{mmg;D?2B<{-^&xW{fky z>En!XhBz@K4Y`hVA@`6+NHfw1_>fkl3F$%ZZsC6!h4kAhO$n9St|52mJ zzZ}1*G~zT$z;>HI5Pj$WcLc|6t^NN^N+eo6P)PykJF~xY{9|ch)VfL-U5qAbXVk_R z77PcF!YHB^*THC^K2SobdjHz%cPj;awrv@;J(oc64#mL{-Z2qdfvy)C7EAW`^^4%s zL3c}UNj{ShPiaX$d09Dmu(kL8TH4=ixA#M0MDfdf5G$;cVpc-5T*FYm^2KT@N@EG)iXW%860MpB=n*sk zy@1|8bI@046-Hocmx+%XreX`RRoI)@PINs_ zW0!C^91Bhar-DYi3oZb60GEX;#nt25a09qW+#(*2XU9w6HStz>5H;7SM~BoPV;7YQwdLBb4Sje?Fsh(evhio%N`mLi*?lHxW+AH_7q z8YMlY2&E>aJ!JspVag)PI?7JUSCrqVsHk>OsZrTb`BNoQ6;WNGdPwz#YK5ACTAW&s z+KoDr`Z)Ck>Ne^Z)Zd6iqA*dL=t_(r9w*iiJBZ`NA2f_K(ljPCUNnbjifI~X25COg zQqT&~>d@|`jiW83y-wRlJ5NVJCrqbDN1{uhE2g_eH$t~a&qyyvZ$%$WpGAL>{t^8w z1D-*c!GOVwA%&rWp@U(P5n&Wy)MNByOlGWP>|~r~!ZC?3nJ@(~Winl6>SbDBW?)ui zc4UrWE@r;VJi&sn2(y^51hHhZTw@t#S!LyB)nWB!O=qoR9b{c*<7Crj^JdFnt7m)0 zw#v@OZonSMp3C0IKE{FJ5a+Prh~_xM(aAB-$;7F_>B*VFd5v?F3+58%vgJC!Rl(K6 zwaCrGZNweMeTut-d!C1dN1G>rC!eQ~PqTw4;8M}-@OXGPdVOhn>DYDHd&5=FH{_luT`4vFE!RK)_tO2zucVR0pKKk;Jm zJ_%StS;AkURAN99E2$X2McQ0CNxD(`lZ>E@t4yv;m&{LD zCD|a^O4*lk405J&NpiR4zRHWsd&(Ef4=E583>6M3G%0*hlu-0mJfrwriBZWy>8Mh> z(uT5%GFiD!c}_)8#a*RDWki)p)mk-6^`RP8O;_!ZTC>`Uy0UtN`Ze{>8d4g88W%NY zcZ%%v-dVYGQgeqUN%NfM#4f&F?z_(Jn$Y6cB59Rtz10@b_R_A>p3xE03DCKu^GR1$ zm#lkJcUezeFJ7-rAJI3`&(QBRU@&knIBhU)xWmxbu-0(FNXh7cQJXQ=*vvTBc*KO; z#M7k4WWiL$G~V>S8MT?6*=e)4=HljL^V=4%g{eiJ#Vbo8%Mi;(D_~_}m1i|(En>aj z`i>3G#@eRX=AEsa?LpfvJ4U;Gb{FlI>~-z4?O!^GIz&3OJJLG3Io3EXJLx-}aGG$I zagKL>?84#V@6zOocXf2Fa$VeQu)ARQw40J!s@w1$kv%bc9_{7W8?^WCKH7bxeb?Qw z?oRF(-PcK0q;k@thlxjt$AYJx=Sj~GUfN#yUUS}>-Y2|gd^CMd_{{oh`sVr0`DytT z`px_6`JeXx5?~xq7O)&>6?h?VGsr2ZK9~^f9(*f=AtW&5ekf09Oz1$EL|AIr#D4Yt z1^XAsX5^}HD11+NQv?%w$?b_0jXV9I3$hH>W) zU=Da5Xg|n*@bJMm@jCJ65}*W+g!V%Mhf)qrCmJPIAErDUc=$<@Y|@FO@5xTdw^O)N z5>qCR7#*oirAZ~HjvUoET9$@M^H1wdS4cmV{xicXqdQYJvoLc#%QLI{nB1|VW1Gi) zj`wCOXP?Qz<%Hx6=W69vpP)YxcVaTnBCjEzFFzyyd%?bfM}>-oWhW_5MxLA~GB0X6 zC3q_5)cR?^)6a@^i|b3cO43V~OT9`5&uE{iE8{N9EL%P6fA;w~qjL@A!sUe(xQeKX zca@HnUFTKL*Hm#-Wmaum2)Qs(ZCic6My00aBG<+2TDUf{cJ7kfrT)wMmz(OO>dw`( z)Ms7+S0b;xzv_N<_?p?Z_Uo$G>uw0$D7ndaGot}&h;3MC^lO}Ga%md4WqPaqw#Mz7 zcckxJXy$J&zRP?!yM?kPrDd}Q?P;e60Mqp+~9b<`bnSH+z+P8~RlGn)=oHZx8GoXc^QRy!TZ9 z>4PDYp~ufGpA8H<3_l-n8<}|S^?de4(2FmlQKM@w6JKFpWsK2{6^?U_m%kQ!T|c2b zarce>o1V9JZ(mM&PJWmQpIVL&bXYJ2D3(gDEU&voJzh-@7|90WK;`jDN>&5Y<;H9lW5XV#+E z@#{q!VjH)An*DsW8N9g}Mk158uL1~KVILm=zFt7{78d{*`_VfE&9B_(cR|q02aG-) zA!+CXd>={1*kQGCB6to08wD?=CY2?1ATfvL7VSK}6oWTo1=A#pIBP6hD+ev76IUho zH{P9mDf~|a=ml+sjtM^&1!4-~J`yJ-yQP+8L}hK{66I?Zo+-h~aw>aNGt?T?-|Qr6 zs_t^vO4q)s^Gc7PuWaCHm}AsrJZ=h^37Q*O1XyNU-LQUc^TUqCUctf2G1%#tbDhg$ z*EhQt+?MyO?E~(VBz6xePaQ8??|nYrz9c_8|D6GRfl%N?P;2nnkmOLmFzfxQWWI1h z_~(e>$d;(;=)9PO*q}I<111MG<5d!r52+>U95zdGO7>2PIC3O4|7c~}_4Kxkp3LE_ z*T-g#f6V@xvzWVdVli(qf3*;ElDSCol+kI=;zK1xrFCao%O0N{IyYKAQ88Efy$ZR& zTrFOsd(o{n`cnSoYjp$lU#`+$Q@Cz-Bm8DgLrvrDrhB(KZnxdJ(|q%8eM?PiMO*2; zg7%D#gZBeE?H(w1v30FHoOsmN{orv&&%Gzjz4d)X{ow<8gT%q7Pm70qo+%Auhet-L zpGUpW9>tAzq4A~kYGy2VTw{Fl^|1+!i4Ske-r7zgll4;$Q!CT??Q4=wD@mPsG$*WR5(zCJ{Iez(_3a*L~N}0-)Dz{Xh zsJ&JHz7wa(xl2yVLOVz&Tlb1yzy3!pBh5t~%oG`kG@ zEQbuo6sK5cKNovf{oPt_hI?H1M(-e?Fi;@ODs5 zaB)a>Xi^w?zZ+RQoHu+e;#uV7sMKhW7=u{ZIKczl2ifD96X*`%4lO0VJ=~piHMt-q z`pBMC)1&%n=INx2!2t5kUsMcKK0M!2)qJ75`r}3Z+C7&_F2AT}x?*`X4c)y~Zb~#*MAXf`_9Zv;sF<&8n{*G*cRKX}AlCXgYpU4l<9_ckrId3JvG8V*ExE!KIzx|y>4g2*UrTmD&+M8AtBCKBMNvDW#-kHsxMS|dy2r7^jUK2ym>ln%Aeyjr zs3S4&uy2xnvUm#H5vo*>`u*r++LQG9jI2yjmf|t|vC-pKvNLi5bB#_goOqD8J8wQe zu0W?y_hi~B!s(3SsZ#bc@?~mg)y^rE%T$O~@}Fm~qQ5{{O;JOOMv~?w_sd7?F4sT3 zvVKkUy6cS-HwPM-np|#`+#YGh-W6*xYV~M4bni@iOULW`n-BQA^d9;?%Id!Qc(`Yy zSFCS$|H*;zr}9I`o_!jzdwy$_|7F3L%xjuA(NmeTu^%nJF0I&X`Ty59vaNzs1|Sd3 zGaZkibq;{sMl{d70{|-}0MVbpQ%H^7s6J(8QB~=A0~m7l;8xG}AN( zu4uj;1Jcoq@)BqPgJ^cV4lzJtkT#khhC|2De6SOmgdvz8)`h*`qiB{l2(Kanhy@ad zR3jr80!AGZjH$r9z%pRX(2T4D2jR4FiMVz=7H^8r!9OSP6M_i0DDV_c6x9@~lopid zD3_@$sj8_l)E?9wL?L22@e_?bO*5M51rXP3)HJvm9xha-5S~W!xS-5m=srguI2LkIU8lA9wGBkQB3Vy?#RJkTE>dWb7= zI4M8H{K(SL{B+5T&Mfca%-OGVpX7}dY!>mIRx2?-v+rzd`Ps_R3sN;_FUi(TTs?PV zZzI*My5>Et679U5h7ZeosQW|*mWFaiL|+WNI`jJE+rfA4b1NSiK41C@zRNDDEpM)5 ztTwF;t>4+G_!+jTvb}#m+us}*04ERz@<0n%05=c}5$U~Dm&m`*H&)x;ja-odWpv~bC|M|e8C z3;sO*D?x{lM|ek}L6Ju>Pictye4Waf>IRw#lc|S@YQ(cNR5UR(Gqf(Wy>y0jZS=e7 zTN(5ix*6>l$I#4ll{uS5oaHg=el`}i26h|vcO0>tM9xYsZLTTqTpm+i8s0I!>-@)e z_zUO?vI%|=x+|O`;v=dnCM3=vL6F2s5u|Bk7-Z??2y!d(vkD`M-AebA@2PaFzEu0F zL9p=8O%eOmeU*M4M zc-E=a`HQR4?nJjIdwKRnxc7Moc=~u1c;EEt^_}ut3t$OU2(k~33OOCx7WSUZ8g3X7 z6?q|QG=?tLA};&D^LXKe_(PM2eUs@@x>F<4aOp=gF{UxsI%Pma#M`u-YvLpjMbEjVL5S2(ZoIpbT^a?nceTFQFI#`m8q+x~~q)lUz2 zfE3sTY=94l2Zi7Y=mGB`3P=L7fXGl0)CRqWSzvuQ04U7gR4s#o` zj@7|tU`KFbxWl+H{4RVsfj|f)3{j|2lv2W!0hG_E^r`Mpt5M%0S`**VB+>HIw$ZuK zEz##NNHIKN3}GTNRWs`{zha4H4q8s8g5`vQ2Qtr};GNrOja!=*IDRL;8C?~0Os8OrCXF{(UGxU2Rxc%r`(OaoqC9a08 z4z4M!6|H?)H(4)VU)!+Xxcn3O>HPE3Cba3Wd2!4C;XVrT+s6k)tqTBld~@@gFaY=y z0Bq!LZmyMYZf;bd$;K-H+Jb)l@3MW?&;d~LdJ8=)sfqd5|3Cf*hMK?V*1e)V00009 za7bBm000XU000XU0RWnu7ytkS*-1n}R9J<*nA?uyHVlS8%Ca3h$C;h&c7gW&-=(*G zgrYzTbhneu(7cw4eOTl}@i%5kcf~QbbVI%Ry!QUBD3b^u0$_DU4881$9+mjBq&i^v+{s zYpOZ&=eDL>9T;c>vm(Wzkl&vyx_3Qk9l3j|aZ(&KM(Mf9}jgcvFM{ z8-tJz0XitjmM}tyhEtl_@^II(TGY&{5@RlwsdlN%_8Hn?q z`&Gl|A6B%pa_s2f)!n+m82J6q9j9}TEd;#|ORhX~+@?tCc)#SI3eGDJn>jyyT48N5 z0+91gfV)+TEed}7W6R;x1weYO^wyrq!D{{_`|G`D(Nui?xWw85RYg@9lm8MSRb@G^ z`SD?iF(%x!35O@adGdK*C3M8Y<=F8G_}QfkK{BUa3p3VX2- zi3nwpg7_kkYblISSu^?U>ed5bQsiC;p(+>=j4|o|w`Jf@O5;Sa$v&gXlqSv2$W?97 zDTaTiL<+p7>QW(wYJRhEs9auO=gO4i>JmRD>RJSAudjUkaxtkm^l~GmA+Dsox7k z21V!2;eAH@7wD7HM79vYrQ0n+@090r|86kROy7G>=WdK3kGu>eWe}5cIE7e4dekdl zw@13pjfbx{WPd!f+n+JUjLQGG5t8ovC^8ZUo*2>0i{q)|)V&>@FusN^y+3qxolnIq z^}eZIAVbkhoH3(kL`MoEbiLm zx$|^5vp<|EOg6&aVjjtKOU(dBpNc}*?Vs80pI_V_XB_X9zrUU6dPia8^5!v>o{K5W zRQJpeU3z9%@b4gRK71t#yy9pSJDPXpW`8Prkt4-0O1#!EMFcZMXynl~=qsC}c94um z-efgLRcY#ySrumZX#$7qV2m(tOG;~QBRNrWMqu(yF}D&M+Q4oF?$-@%Tk?{`_c4IY zvSHp;aG9s%pH`89$3INV$hY^^!B{IBCzCPC|r7f7xN;>E1U6_I6-^kX0 zOv&e&j8H0psLpu=Vcu4J+_bc_>J37ca l%5XU^6|Cd|?`v-#SNw`M^J`4Z=002ovPDHLkV1i5TjzRzc literal 0 HcmV?d00001 diff --git a/modules/ncenterlite/m.skins/default/ncenter.html b/modules/ncenterlite/m.skins/default/ncenter.html new file mode 100644 index 000000000..f43222c58 --- /dev/null +++ b/modules/ncenterlite/m.skins/default/ncenter.html @@ -0,0 +1,124 @@ + + + +
+ + diff --git a/modules/ncenterlite/m.skins/default/ncenterlite.black.css b/modules/ncenterlite/m.skins/default/ncenterlite.black.css new file mode 100644 index 000000000..6f2596f91 --- /dev/null +++ b/modules/ncenterlite/m.skins/default/ncenterlite.black.css @@ -0,0 +1,15 @@ +#nc_container { border-bottom-color:#555; background-color:#333; color:#B0B0B0; opacity:0.95; } +#nc_container a { color:#B0B0B0; } +#nc_container a:hover { color:#B0B0B0; } +#nc_container strong { color:#F3F3F3; } + +#nc_container .list { background-color:#333; filter:alpha(opacity=95); opacity:0.95; -moz-opacity:0.95; } +#nc_container .list li { border-color:#555; } +#nc_container .list li a:hover { background-color:#555; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list .more { background: #555; } +#nc_container a.readall { display:none; color:#555; color:#D83722;} + +#nc_container a.notify { color:white; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } diff --git a/modules/ncenterlite/m.skins/default/ncenterlite.blacknoprofile.css b/modules/ncenterlite/m.skins/default/ncenterlite.blacknoprofile.css new file mode 100644 index 000000000..88c94a4a4 --- /dev/null +++ b/modules/ncenterlite/m.skins/default/ncenterlite.blacknoprofile.css @@ -0,0 +1,17 @@ +#nc_container { border-bottom-color:#555; background-color:#333; color:#B0B0B0; opacity:0.95; } +#nc_container a { color:#B0B0B0; } +#nc_container a:hover { color:#B0B0B0; } +#nc_container strong { color:#F3F3F3; } + +#nc_container .list { background-color:#333; filter:alpha(opacity=95); opacity:0.95; -moz-opacity:0.95; } +#nc_container .list li { border-color:#555; } +#nc_container .list li a:hover { background-color:#555; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list .more { background: #555; } +#nc_container a.readall { display:none; color:#555; color:#D83722;} + +#nc_container a.notify { color:white; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } + +#nc_container img.nc_profile_img { display:none;} diff --git a/modules/ncenterlite/m.skins/default/ncenterlite.css b/modules/ncenterlite/m.skins/default/ncenterlite.css new file mode 100644 index 000000000..60df8e548 --- /dev/null +++ b/modules/ncenterlite/m.skins/default/ncenterlite.css @@ -0,0 +1,185 @@ +#nc_container { + z-index:99; + position:fixed; + top:0; + left:0; + margin:0; + padding:0; + width:100%; + height:30px; + border-bottom:1px solid; + font-size:12px; + line-height:15px; +} + +.ncenterlite_block { + height:28px; +} + +#nc_container a { + padding:0; + font-size:12px; + text-decoration:none; +} + +#nc_container ul.nc_memu { + display:block; + margin:0; + padding:4px; + list-style:none; + line-height:20px; +} + +#nc_container ul:after { + content:""; + display:block; + clear:both; +} + +#nc_container ul.nc_memu li { + display:inline-block; + padding:0 5px; + height:20px; + width:auto; + background:transparent!important; +} + +#nc_container ul.nc_memu li.fLeft { + float:left; +} + +#nc_container ul.nc_memu li.fRight { + float:right; +} + +#nc_container ul.nc_memu li a.notify { + display:block; + float:left; +} + +#nc_container a.close { + display:block; +} + +#nc_container .nc_profile img { + width:20px; + height:20px; + vertical-align:top; +} + +#nc_container a.notify .num { + padding:1px 2px; + border:0; + border-radius:3px; + -webkit-border-radius:3px; + -moz-border-radius:3px; + font-size:12px; + font-weight:700; + font-family:Gulim,"lucida grande",tahoma,verdana,arial,sans-serif; +} + +#nc_container .list { + display:none; + position:absolute; + top:30px; + left:0; + max-width:330px; + width:330px; + box-sizing:border-box; + -moz-box-sizing:border-box; + -webkit-box-sizing:border-box; + -o-box-sizing:border-box; +} + +#nc_container a.readall { + display:none; + float:left; + margin:0 4px; + font-size:11px; + font-weight:700; +} + +#nc_container .listscroll { + overflow-y:auto; + overflow-x:hidden; +} + +#nc_container .list ul { + overflow:hidden; + margin:-1px 0; + padding:0; + list-style:none; +} + +#nc_container .list li { + margin:-1px 0; + border:1px solid #555; + border-width:1px 0; +} + +#nc_container .list li img { + float:left; + margin-right:5px; + width:45px; + height:45px; + border:0; +} + +#nc_container .list li a { + display:block; + overflow:hidden; + padding:10px; + font-size:12px; +} + +#nc_container .list span.ago { + display:block; + font-size:10px; +} + +#nc_container .list .more { + display:block; + padding:10px; + text-align:center; +} + +#nc_container ~ .navbar.navbar-fixed-top { + top:28px; +} + +#nc_container a:hover,#nc_container .list li a:hover,#nc_container .list .more:hover { + text-decoration:none; +} + +@media only screen and max-device-width 480px { + #nc_container { + position:relative; + height:auto; + } + + #nc_container .list { + top:2px; + position:relative; + } + + #nc_container .list ul { + display:block; + position:relative; + } + + #nc_container ul.nc_memu:after { + content:""; + display:block; + visibility:hidden; + height:0; + clear:both; + } + + #nc_container .listscroll { + overflow:visible; + } + + .ncenterlite_block { + display:none; + } +} diff --git a/modules/ncenterlite/m.skins/default/ncenterlite.gray.css b/modules/ncenterlite/m.skins/default/ncenterlite.gray.css new file mode 100644 index 000000000..4d167ea21 --- /dev/null +++ b/modules/ncenterlite/m.skins/default/ncenterlite.gray.css @@ -0,0 +1,16 @@ +#nc_container { border-bottom-color:#C0C0C0; background-color:#efefef; color:#666; opacity:0.95; } +#nc_container a { color:#666; } +#nc_container a:hover { color:#666; } +#nc_container strong { color:#666; } +#nc_container a.notify strong { color:#FC2772; } +#nc_container .list { background-color:#efefef; filter:alpha(opacity=95); opacity:0.95; -moz-opacity:0.95; } +#nc_container .list li { border-color:#ddd; } +#nc_container .list li a:hover { background-color: #ddd; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list a.notify sup.num { background-color:#D83722; color:white; } +#nc_container .list .more { background: #ddd; } +#nc_container a.readall { display:none; color:#555; font-size:10px; color:#D83722;} + +#nc_container a.notify { color:#666; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } diff --git a/modules/ncenterlite/m.skins/default/ncenterlite.graynoprofile.css b/modules/ncenterlite/m.skins/default/ncenterlite.graynoprofile.css new file mode 100644 index 000000000..d8704070b --- /dev/null +++ b/modules/ncenterlite/m.skins/default/ncenterlite.graynoprofile.css @@ -0,0 +1,18 @@ +#nc_container { border-bottom-color:#C0C0C0; background-color:#efefef; color:#666; opacity:0.95; } +#nc_container a { color:#666; } +#nc_container a:hover { color:#666; } +#nc_container strong { color:#666; } +#nc_container a.notify strong { color:#FC2772; } +#nc_container .list { background-color:#efefef; filter:alpha(opacity=95); opacity:0.95; -moz-opacity:0.95; } +#nc_container .list li { border-color:#ddd; } +#nc_container .list li a:hover { background-color: #ddd; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list a.notify sup.num { background-color:#D83722; color:white; } +#nc_container .list .more { background: #ddd; } +#nc_container a.readall { display:none; color:#555; font-size:10px; color:#D83722;} + +#nc_container a.notify { color:#666; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } + +#nc_container img.nc_profile_img { display:none;} diff --git a/modules/ncenterlite/m.skins/default/ncenterlite.html b/modules/ncenterlite/m.skins/default/ncenterlite.html new file mode 100644 index 000000000..07c776b5d --- /dev/null +++ b/modules/ncenterlite/m.skins/default/ncenterlite.html @@ -0,0 +1,122 @@ + +
+ + diff --git a/modules/ncenterlite/m.skins/default/ncenterlite.mobile.css b/modules/ncenterlite/m.skins/default/ncenterlite.mobile.css new file mode 100644 index 000000000..ef3125177 --- /dev/null +++ b/modules/ncenterlite/m.skins/default/ncenterlite.mobile.css @@ -0,0 +1,12 @@ +#nc_container { height:auto; position:relative; } +#nc_container .list { top:2px; position:relative; } +#nc_container .list ul { display:block; position:relative; } +#nc_container ul.nc_memu:after { + content: ""; + display: block; + height: 0; + clear: both; + visibility: hidden; +} +#nc_container .listscroll { overflow: visible; } +.ncenterlite_block { display:none; } diff --git a/modules/ncenterlite/m.skins/default/ncenterlite.white.css b/modules/ncenterlite/m.skins/default/ncenterlite.white.css new file mode 100644 index 000000000..c3fd4444a --- /dev/null +++ b/modules/ncenterlite/m.skins/default/ncenterlite.white.css @@ -0,0 +1,16 @@ +#nc_container { border:1px solid #efefef; background-color:#fff; color:#000; opacity:0.95; } +#nc_container a { color:#666; } +#nc_container a:hover { color:#666; } +#nc_container strong { color:#000; } +#nc_container a.notify strong { color:#F60; } +#nc_container .list { border:1px solid #efefef; background-color:#fff; filter:alpha(opacity=95); opacity:0.95; -moz-opacity:0.95; } +#nc_container .list li { border-color:#eee; } +#nc_container .list li a:hover { background-color: #eee; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list a.notify .num { background-color:#D83722; color:white; } +#nc_container .list .more { background: #eee; } +#nc_container a.readall { display:none; color:#555; font-size:10px; color:#D83722;} + +#nc_container a.notify { color:#666; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } diff --git a/modules/ncenterlite/m.skins/default/ncenterlite.whitenoprofile.css b/modules/ncenterlite/m.skins/default/ncenterlite.whitenoprofile.css new file mode 100644 index 000000000..69342bd1d --- /dev/null +++ b/modules/ncenterlite/m.skins/default/ncenterlite.whitenoprofile.css @@ -0,0 +1,18 @@ +#nc_container { border:1px solid #efefef; background-color:#fff; color:#000; opacity:0.95; } +#nc_container a { color:#666; } +#nc_container a:hover { color:#666; } +#nc_container strong { color:#000; } +#nc_container a.notify strong { color:#F60; } +#nc_container .list { border:1px solid #efefef; background-color:#fff; filter:alpha(opacity=97); opacity:0.97; -moz-opacity:0.97; } +#nc_container .list li { border-color:#eee; } +#nc_container .list li a:hover { background-color: #eee; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list a.notify .num { background-color:#D83722; color:white; } +#nc_container .list .more { background: #eee; } +#nc_container a.readall { display:none; color:#555; font-size:10px; color:#D83722;} + +#nc_container a.notify { color:#666; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } + +#nc_container img.nc_profile_img { display:none;} diff --git a/modules/ncenterlite/m.skins/default/notify.css b/modules/ncenterlite/m.skins/default/notify.css new file mode 100644 index 000000000..0c6e55f3d --- /dev/null +++ b/modules/ncenterlite/m.skins/default/notify.css @@ -0,0 +1,97 @@ +@charset "utf-8"; + +.sosi-title { + position: relative; + font-size: 16px; + font-style: bold; + display: block; + margin: 14px 0 14px 14px; + width: auto; +} + + +.history-list { + margin: 0 0 22px 0px; +} + +.history-list .history-item { + position: relative; + padding: 4px 0 4px 14px; + border-top: 1px solid #ececec; + overflow: hidden; + +} + +.history-list .history-info { +padding: 0 11px 9px 0; +} + +.history-list .history-info .history-gird{ + color: #252525; + font-size: 14px; + line-height: 16px; + font-weight: bold; + margin-top: 9px; + display:block; +} + +.text-ellipsis { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.history-list .history-info .history-gird .history-succeed{ + color:#3c763d +} +.history-list .history-info .history-gird .history-failed{ + color:#a94442 +} +.history-list .history-text{ + color: #252525; + font-size: 11px; + line-height: 16px; +} +.history-list .history-text .history-ip{ + color: #8A8D99; + font-size: 12px; +} + +.history-list .history-text .history-auth{ + color: #8A8D99; + margin-left:3px; +} + + +html{color:#000;background:#fff;} +legend{color:#000;} +html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,ul,li,ol,dl,dt,dd,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;} +abbr,acronym,address,caption,cite,code,dfn,em,strong,th,var,h1,h2,h3,h4,h5,h6{font-style:normal;font-weight:normal;font-variant:normal;} + + + +body{line-height:1; color: #333; font: normal normal normal 13px/1.5 'Helvetica Neue', Helvetica, Arial, sans-serif;} +ol,ul,li{list-style:none;} + + + + + +html {margin: 0; padding: 0; border: 0; overflow: auto; background-color: #f4f4f4;} +body, .default-font {font: normal normal normal 12px/16px 'Helvetica Neue', Helvetica, Arial, sans-serif;} +body, .default-color {color: #3b3b3b;} +body, .default-background-color {background-color: #fff;} +table {font-size: inherit; font: 100%;} + +h1, h2, h3, h4, h5, h6 {margin: 0; font-weight: bold;} +h1 {font-size: 36px; line-height: 45px;} +h2 {font-size: 24px; line-height: 30px;} +h3 {font-size: 18px; line-height: 22px;} +h4 {font-size: 16px; line-height: 20px;} +h5 {font-size: 14px; line-height: 18px;} +h6 {font-size: 12px; line-height: 16px;} + +.cfix { zoom:1 } +.cfix:after { content: '.'; display:block; clear:both; visibility:hidden; line-height:0; height:0; overflow: hidden; } +strong, .bold {font-weight: bold;} +a { text-decoration:none; color:#1769FF;} diff --git a/modules/ncenterlite/m.skins/default/skin.xml b/modules/ncenterlite/m.skins/default/skin.xml new file mode 100644 index 000000000..bc4725b2d --- /dev/null +++ b/modules/ncenterlite/m.skins/default/skin.xml @@ -0,0 +1,32 @@ + + + XE 알림센터 Lite 기본스킨 + 1.1.1 + 2013-03-21 + + + XE Magazine + + + + + 검은색 + + + 회색 + + + 흰색 + + + + 검은색(no profile) + + + 회색(no profile) + + + 흰색(no profile) + + + diff --git a/modules/ncenterlite/m.skins/default/userconfig.html b/modules/ncenterlite/m.skins/default/userconfig.html new file mode 100644 index 000000000..fae62dae5 --- /dev/null +++ b/modules/ncenterlite/m.skins/default/userconfig.html @@ -0,0 +1,66 @@ + +
+ +
+

{$XE_VALIDATOR_MESSAGE}

+
+ + + + + + + +
+

{@$user_str = $member_info->nick_name} + {@$user_str = $logged_info->nick_name} + {@$title_str = Context::getLang('ncenterlite_userconfig_title')} + {sprintf($title_str, $user_str)} +

+ +

{$lang->ncenterlite_userconfig_about} ({$lang->ncenterlite_userconfig_about_warning})

+
+ +
+ + +

{$lang->ncenterlite_comment_noti_about}

+
+
+
+ +
+ + +

{$lang->ncenterlite_mention_noti_about}

+
+
+
+ +
+ + +

{$lang->ncenterlite_message_noti_about}

+
+
+ +
+
+
+ +
+
+ +
\ No newline at end of file diff --git a/modules/ncenterlite/ncenterlite.admin.controller.php b/modules/ncenterlite/ncenterlite.admin.controller.php new file mode 100644 index 000000000..91b15fa10 --- /dev/null +++ b/modules/ncenterlite/ncenterlite.admin.controller.php @@ -0,0 +1,152 @@ +use = $obj->use; + $config->display_use = $obj->display_use; + + $config->user_config_list = $obj->user_config_list; + $config->mention_format = $obj->mention_format; + $config->mention_names = $obj->mention_names; + $config->document_notify = $obj->document_notify; + $config->message_notify = $obj->message_notify; + $config->hide_module_srls = $obj->hide_module_srls; + $config->android_format = $obj->android_format; + if(!$config->mention_format && !is_array($config->mention_format)) + { + $config->mention_format = array(); + } + $config->admin_comment_module_srls = $obj->admin_comment_module_srls; + + $config->skin = $obj->skin; + $config->mskin = $obj->mskin; + $config->mcolorset = $obj->mcolorset; + $config->colorset = $obj->colorset; + $config->zindex = $obj->zindex; + $config->anonymous_name = $obj->anonymous_name; + $config->document_read = $obj->document_read; + $config->layout_srl = $obj->layout_srl; + $config->mlayout_srl = $obj->mlayout_srl; + $config->voted_format = $obj->voted_format; + + if(!$config->document_notify) + { + $config->document_notify = 'direct-comment'; + } + + $this->setMessage('success_updated'); + + $oModuleController->updateModuleConfig('ncenterlite', $config); + + if(!in_array(Context::getRequestMethod(),array('XMLRPC','JSON'))) + { + $returnUrl = Context::get('success_return_url') ? Context::get('success_return_url') : getNotEncodedUrl('', 'module', 'admin', 'act', 'dispNcenterliteAdminConfig'); + header('location: ' . $returnUrl); + return; + } + } + + /** + * @brief 스킨 테스트를 위한 더미 데이터 생성 5개 생성 + **/ + function procNcenterliteAdminInsertDummyData() + { + $oNcenterliteController = getController('ncenterlite'); + $logged_info = Context::get('logged_info'); + + for($i = 1; $i <= 5; $i++) + { + $args = new stdClass(); + $args->member_srl = $logged_info->member_srl; + $args->srl = 1; + $args->target_srl = 1; + $args->type = $this->_TYPE_TEST; + $args->target_type = $this->_TYPE_TEST; + $args->target_url = getUrl(''); + $args->target_summary = Context::getLang('ncenterlite_thisistest') . rand(); + $args->target_nick_name = $logged_info->nick_name; + $args->regdate = date('YmdHis'); + $args->notify = $oNcenterliteController->_getNotifyId($args); + $output = $oNcenterliteController->_insertNotify($args); + } + } + + /** + * @brief 모듈 푸시 테스트를 위한 더미 데이터 생성 1개 생성 + **/ + function procNcenterliteAdminInsertPushData() + { + $oNcenterliteController = getController('ncenterlite'); + $logged_info = Context::get('logged_info'); + + $args = new stdClass(); + $args->member_srl = $logged_info->member_srl; + $args->srl = 1; + $args->target_srl = 1; + $args->type = $this->_TYPE_DOCUMENT; + $args->target_type = $this->_TYPE_COMMENT; + $args->target_url = getUrl(''); + $args->target_summary = Context::getLang('ncenterlite_thisistest') . rand(); + $args->target_nick_name = $logged_info->nick_name; + $args->regdate = date('YmdHis'); + $args->notify = $oNcenterliteController->_getNotifyId($args); + $output = $oNcenterliteController->_insertNotify($args); + } + + function procNcenterliteAdminDeleteNofity() + { + $old_date = Context::get('old_date'); + $args = new stdClass; + if($old_date) + { + $args->old_date = $old_date; + } + $output = executeQuery('ncenterlite.deleteNotifyAll', $args); + if(!$output->toBool()) + { + $oDB->rollback(); + return $output; + } + + if($old_date) + { + $oNcenterliteModel = getModel('ncenterlite'); + $message = Context::getLang('ncenterlite_message_delete_notification_before'); + $message = sprintf($message, $oNcenterliteModel->getAgo($old_date) ); + $this->setMessage($message); + } + else + { + $this->setMessage('ncenterlite_message_delete_notification_all'); + } + + if(!in_array(Context::getRequestMethod(),array('XMLRPC','JSON'))) + { + $returnUrl = Context::get('success_return_url') ? Context::get('success_return_url') : getNotEncodedUrl('', 'module', 'admin', 'act', 'dispNcenterliteAdminList'); + header('location: ' .$returnUrl); + return; + } + } + + function procNcenterliteAdminEnviromentGatheringAgreement() + { + $vars = Context::getRequestVars(); + $oModuleModel = getModel('module'); + $ncenterlite_module_info = $oModuleModel->getModuleInfoXml('ncenterlite'); + $agreement_file = FileHandler::getRealPath(sprintf('%s%s.txt', './files/ncenterlite/ncenterlite-', $ncenterlite_module_info->version)); + + FileHandler::writeFile($agreement_file, $vars->is_agree); + + if(!in_array(Context::getRequestMethod(),array('XMLRPC','JSON'))) + { + $returnUrl = Context::get('success_return_url') ? Context::get('success_return_url') : getNotEncodedUrl('', 'module', 'admin', 'act', 'dispNcenterliteAdminConfig'); + header('location: ' . $returnUrl); + return; + } + } +} diff --git a/modules/ncenterlite/ncenterlite.admin.model.php b/modules/ncenterlite/ncenterlite.admin.model.php new file mode 100644 index 000000000..4e5a9ecb9 --- /dev/null +++ b/modules/ncenterlite/ncenterlite.admin.model.php @@ -0,0 +1,105 @@ +getConfig(); + + global $lang; + + $act = Context::get('act'); + $output = $oNcenterliteModel->getNcenterliteAdminList(); + + $oMemberModel = getModel('member'); + $list = $output->data; + + foreach($list as $k => $v) + { + $target_member = $v->target_nick_name; + + switch($v->type) + { + case 'D': + $type = $lang->ncenterlite_document; //$type = '글'; + break; + case 'C': + $type = $lang->ncenterlite_comment; //$type = '댓글'; + break; + // 메시지. 쪽지 + case 'E': + $type = $lang->ncenterlite_type_message; //$type = '쪽지'; + break; + } + + switch($v->target_type) + { + case 'C': + $str = sprintf($lang->ncenterlite_commented, $target_member, $type, $v->target_summary); + //$str = sprintf('%s님이 회원님의 %s에 "%s" 댓글을 남겼습니다.', $target_member, $type, $v->target_summary); + break; + case 'A': + $str = sprintf($lang->ncenterlite_commented_board, $target_member, $v->target_browser, $v->target_summary); + //$str = sprintf('%1$s님이 게시판 "%2$s""%3$s"라고 댓글을 남겼습니다.', $target_member, $type, $v->target_summary); + break; + case 'M': + $str = sprintf($lang->ncenterlite_mentioned, $target_member, $v->target_summary, $type); + //$str = sprintf('%s님이 "%s" %s에서 회원님을 언급하였습니다.', $target_member, $v->target_summary, $type); + break; + // 메시지. 쪽지 + case 'E': + if(version_compare(__XE_VERSION__, '1.7.4', '>=')) + { + $str = sprintf($lang->ncenterlite_message_mention, $target_member, $v->target_summary); + //%s님께서 "%s"라고 메세지를 보내셨습니다. + } + else + { + $str = sprintf($lang->ncenterlite_message_string, $v->target_summary); + } + break; + case 'T': + $str = sprintf($lang->ncenterlite_test_noti, $target_member); + break; + case 'P': + $str = sprintf($lang->ncenterlite_board, $target_member, $v->target_browser, $v->target_summary); + //%1$s님이 게시판 "%2$s""%3$s"라고 글을 남겼습니다. + break; + case 'S': + if($v->target_browser) + { + $str = sprintf($lang->ncenterlite_board, $target_member, $v->target_browser, $v->target_summary); + } + else + { + $str = sprintf($lang->ncenterlite_article, $target_member, $v->target_summary); + } + break; + case 'V': + $str = sprintf($lang->ncenterlite_vote, $target_member, $v->target_summary); + break; + } + + if($v->type=='U') + { + $str = $oNcenterliteModel->getNotifyTypeString($v->notify_type,unserialize($v->target_body)); + } + $v->text = $str; + $v->ago = $oNcenterliteModel->getAgo($v->regdate); + $v->url = getUrl('','act','procNcenterliteRedirect', 'notify', $v->notify, 'url', $v->target_url); + if($v->target_member_srl) + { + $profileImage = $oMemberModel->getProfileImage($v->target_member_srl); + $v->profileImage = $profileImage->src; + } + + $list[$k] = $v; + } + + $output->data = $list; + return $output; + } + +} \ No newline at end of file diff --git a/modules/ncenterlite/ncenterlite.admin.view.php b/modules/ncenterlite/ncenterlite.admin.view.php new file mode 100644 index 000000000..4c745ed0b --- /dev/null +++ b/modules/ncenterlite/ncenterlite.admin.view.php @@ -0,0 +1,90 @@ +setTemplatePath($this->module_path.'tpl'); + $this->setTemplateFile(str_replace('dispNcenterliteAdmin', '', $this->act)); + } + + function dispNcenterliteAdminConfig() + { + $oModuleModel = getModel('module'); + $oNcenterliteModel = getModel('ncenterlite'); + $oLayoutModel = getModel('layout'); + + $config = $oNcenterliteModel->getConfig(); + Context::set('config', $config); + + $layout_list = $oLayoutModel->getLayoutList(); + Context::set('layout_list', $layout_list); + + $mobile_layout_list = $oLayoutModel->getLayoutList(0, 'M'); + Context::set('mlayout_list', $mobile_layout_list); + + $skin_list = $oModuleModel->getSkins($this->module_path); + Context::set('skin_list', $skin_list); + + $mskin_list = $oModuleModel->getSkins($this->module_path, "m.skins"); + Context::set('mskin_list', $mskin_list); + + if(!$skin_list[$config->skin]) $config->skin = 'default'; + Context::set('colorset_list', $skin_list[$config->skin]->colorset); + + if(!$mskin_list[$config->mskin]) $config->mskin = 'default'; + Context::set('mcolorset_list', $mskin_list[$config->mskin]->colorset); + + $security = new Security(); + $security->encodeHTML('config..'); + $security->encodeHTML('skin_list..title'); + $security->encodeHTML('colorset_list..name','colorset_list..title'); + + $mid_list = $oModuleModel->getMidList(null, array('module_srl', 'mid', 'browser_title', 'module')); + + Context::set('mid_list', $mid_list); + + // 사용환경정보 전송 확인 + $ncenterlite_module_info = $oModuleModel->getModuleInfoXml('ncenterlite'); + $agreement_file = FileHandler::getRealPath(sprintf('%s%s.txt', './files/ncenterlite/ncenterlite-', $ncenterlite_module_info->version)); + + $agreement_ver_file = FileHandler::getRealPath(sprintf('%s%s.txt', './files/ncenterlite/ncenterlite_ver-', $ncenterlite_module_info->version)); + + if(file_exists($agreement_file)) + { + $agreement = FileHandler::readFile($agreement_file); + Context::set('_ncenterlite_env_agreement', $agreement); + $agreement_ver = FileHandler::readFile($agreement_ver_file); + if($agreement == 'Y') + { + $_ncenterlite_iframe_url = 'http://sosifam.com/index.php?mid=ncenterlite_iframe'; + if(!$agreement_ver) + { + $_host_info = urlencode($_SERVER['HTTP_HOST']) . '-NC' . $ncenterlite_module_info->version . '-PHP' . phpversion() . '-XE' . __XE_VERSION__; + } + Context::set('_ncenterlite_iframe_url', $_ncenterlite_iframe_url . '&_host='. $_host_info); + Context::set('ncenterlite_module_info', $ncenterlite_module_info); + } + FileHandler::writeFile($agreement_ver_file, 'Y'); + } + else + { + Context::set('_ncenterlite_env_agreement', 'NULL'); + } + } + + function dispNcenterliteAdminList() + { + $oNcenterliteAdminModel = getAdminModel('ncenterlite'); + + $output = $oNcenterliteAdminModel->getAdminNotifyList(); + + Context::set('total_count', $output->page_navigation->total_count); + Context::set('total_page', $output->page_navigation->total_page); + Context::set('page', $output->page); + Context::set('ncenterlite_list', $output->data); + Context::set('page_navigation', $output->page_navigation); + + $this->setTemplateFile('ncenter_list'); + } + +} diff --git a/modules/ncenterlite/ncenterlite.class.php b/modules/ncenterlite/ncenterlite.class.php new file mode 100644 index 000000000..edcc7777f --- /dev/null +++ b/modules/ncenterlite/ncenterlite.class.php @@ -0,0 +1,213 @@ +disable_notify)) + { + $module_info = Context::get('module_info'); + if(in_array($module_info->mid, $this->disable_notify)) $result = TRUE; + } + + return $result; + } + + function moduleInstall() + { + return new Object(); + } + + function checkUpdate() + { + $oModuleModel = getModel('module'); + $oDB = &DB::getInstance(); + + foreach($this->triggers as $trigger) + { + if(!$oModuleModel->getTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4])) return true; + } + + if(!$oDB->isColumnExists('ncenterlite_notify', 'readed')) + { + return true; + } + + if(!$oDB->isColumnExists('ncenterlite_notify', 'target_body')) + { + return true; + } + + if(!$oDB->isColumnExists('ncenterlite_notify', 'notify_type')) + { + return true; + } + + if(!$oDB->isColumnExists('ncenterlite_notify', 'target_browser')) + { + return true; + } + + if(!$oDB->isColumnExists('ncenterlite_notify', 'target_p_srl')) + { + return true; + } + + if(!$oDB->isIndexExists('ncenterlite_notify', 'idx_srl')) + { + return true; + } + + if(!$oDB->isIndexExists('ncenterlite_notify', 'idx_target_srl')) + { + return true; + } + + if(!$oDB->isIndexExists('ncenterlite_notify', 'idx_target_p_srl')) + { + return true; + } + + if(!$oDB->isIndexExists('ncenterlite_notify', 'idx_notify')) + { + return true; + } + + if(!$oDB->isIndexExists('ncenterlite_notify', 'idx_target_member_srl')) + { + return true; + } + + return false; + } + + function moduleUpdate() + { + $oModuleModel = getModel('module'); + $oModuleController = getController('module'); + $oDB = &DB::getInstance(); + + foreach($this->triggers as $trigger) + { + if(!$oModuleModel->getTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4])) + { + $oModuleController->insertTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4]); + } + } + + if(!$oDB->isColumnExists('ncenterlite_notify','readed')) + { + $oDB->addColumn('ncenterlite_notify', 'readed', 'char', 1, 'N', true); + $oDB->addIndex('ncenterlite_notify', 'idx_readed', array('readed')); + $oDB->addIndex('ncenterlite_notify', 'idx_member_srl', array('member_srl')); + $oDB->addIndex('ncenterlite_notify', 'idx_regdate', array('regdate')); + } + + if(!$oDB->isColumnExists('ncenterlite_notify','target_browser')) + { + $oDB->addColumn('ncenterlite_notify', 'target_browser', 'varchar', 50, true); + } + + if(!$oDB->isColumnExists('ncenterlite_notify','target_body')) + { + $oDB->addColumn('ncenterlite_notify', 'target_body', 'varchar', 255, true); + } + + if(!$oDB->isColumnExists('ncenterlite_notify','notify_type')) + { + $oDB->addColumn('ncenterlite_notify', 'notify_type', 'number', 11, 0); + } + + if(!$oDB->isColumnExists('ncenterlite_notify','target_p_srl')) + { + $oDB->addColumn('ncenterlite_notify', 'target_p_srl', 'number', 10, true); + } + + if(!$oDB->isIndexExists('ncenterlite_notify', 'idx_srl')) + { + $oDB->addIndex('ncenterlite_notify', 'idx_srl', array('srl')); + } + + if(!$oDB->isIndexExists('ncenterlite_notify', 'idx_target_srl')) + { + $oDB->addIndex('ncenterlite_notify', 'idx_target_srl', array('target_srl')); + } + + if(!$oDB->isIndexExists('ncenterlite_notify', 'idx_target_p_srl')) + { + $oDB->addIndex('ncenterlite_notify', 'idx_target_p_srl', array('target_p_srl')); + } + + if(!$oDB->isIndexExists('ncenterlite_notify', 'idx_notify')) + { + $oDB->addIndex('ncenterlite_notify', 'idx_notify', array('notify')); + } + + if(!$oDB->isIndexExists('ncenterlite_notify', 'idx_target_member_srl')) + { + $oDB->addIndex('ncenterlite_notify', 'idx_target_member_srl', array('target_member_srl')); + } + + return new Object(0, 'success_updated'); + } + + function recompileCache() + { + return new Object(); + } + + function moduleUninstall() + { + $oModuleController = getController('module'); + + foreach($this->triggers as $trigger) + { + $oModuleController->deleteTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4]); + } + return new Object(); + } +} diff --git a/modules/ncenterlite/ncenterlite.controller.php b/modules/ncenterlite/ncenterlite.controller.php new file mode 100644 index 000000000..99538bb5b --- /dev/null +++ b/modules/ncenterlite/ncenterlite.controller.php @@ -0,0 +1,1050 @@ +member_srl; + } + + if($logged_info->member_srl != $member_srl && $logged_info->is_admin != 'Y') return new Object(-1, 'ncenterlite_stop_no_permission_other_user_settings'); + + $output = $oNcenterliteModel->getMemberConfig($member_srl); + + $obj = Context::getRequestVars(); + + $args = new stdClass(); + $args->member_srl = $member_srl; + $args->comment_notify = $obj->comment_notify; + $args->mention_notify = $obj->mention_notify; + $args->message_notify = $obj->message_notify; + + if(!$output) + { + $outputs = executeQuery('ncenterlite.insertUserConfig', $args); + } + else + { + $outputs = executeQuery('ncenterlite.updateUserConfig', $args); + } + + $this->setMessage('success_updated'); + + if(!in_array(Context::getRequestMethod(),array('XMLRPC','JSON'))) + { + $returnUrl = Context::get('success_return_url') ? Context::get('success_return_url') : getNotEncodedUrl('', 'act', 'dispNcenterliteUserConfig','member_srl',$member_srl); + header('location: ' . $returnUrl); + return; + } + } + + function triggerAfterDeleteMember($obj) + { + $oNcenterliteModel = getModel('ncenterlite'); + $config = $oNcenterliteModel->getConfig(); + + $member_srl = $obj->member_srl; + if(!$member_srl) return new Object(); + + $args = new stdClass(); + $args->member_srl = $member_srl; + $output = executeQuery('ncenterlite.deleteNotifyByMemberSrl', $args); + if(!$output->toBool()) + { + return $output; + } + return new Object(); + } + + function triggerAfterInsertDocument(&$obj) + { + $oModuleModel = getModel('module'); + + if($this->_isDisable()) return; + + $oNcenterliteModel = getModel('ncenterlite'); + $config = $oNcenterliteModel->getConfig(); + if($config->use != 'Y') return new Object(); + + $content = strip_tags($obj->title . ' ' . $obj->content); + + $mention_targets = $this->_getMentionTarget($content); + if(!$mention_targets || !count($mention_targets)) return new Object(); + + $oDocumentModel = getModel('document'); + $document_srl = $obj->document_srl; + $oDocument = $oDocumentModel->getDocument($document_srl); + $module_info = $oModuleModel->getModuleInfoByDocumentSrl($document_srl); + + $is_anonymous = $this->_isAnonymous($this->_TYPE_DOCUMENT, $obj); + // 맨션 알림일경우 맨션알림 시작. + if($mention_targets) + { + // !TODO 공용 메소드로 분리 + foreach($mention_targets as $mention_member_srl) + { + $target_member_config = $oNcenterliteModel->getMemberConfig($mention_member_srl); + $notify_member_config = $target_member_config->data; + + if($notify_member_config->mention_notify == 'N') + { + continue; + } + + $args = new stdClass(); + $args->member_srl = $mention_member_srl; + $args->srl = $obj->document_srl; + $args->target_p_srl = $obj->document_srl; + $args->target_srl = $obj->document_srl; + $args->type = $this->_TYPE_DOCUMENT; + $args->target_type = $this->_TYPE_MENTION; + $args->target_url = getNotEncodedFullUrl('', 'document_srl', $obj->document_srl); + $args->target_summary = cut_str(strip_tags($obj->title), 50); + $args->target_nick_name = $obj->nick_name; + $args->target_email_address = $obj->email_address; + $args->regdate = date('YmdHis'); + $args->target_browser = $module_info->browser_title; + $args->notify = $this->_getNotifyId($args); + $output = $this->_insertNotify($args, $is_anonymous); + } + } + + return new Object(); + } + + function triggerAfterInsertComment(&$obj) + { + if($this->_isDisable()) return; + + $oNcenterliteModel = getModel('ncenterlite'); + $config = $oNcenterliteModel->getConfig(); + if($config->use != 'Y') return new Object(); + + $logged_info = Context::get('logged_info'); + $notify_member_srl = array(); + + $document_srl = $obj->document_srl; + $oModuleModel = getModel('module'); + $module_info = $oModuleModel->getModuleInfoByDocumentSrl($document_srl); + $comment_srl = $obj->comment_srl; + $parent_srl = $obj->parent_srl; + $content = $obj->content; + $regdate = $obj->regdate; + + // 익명 노티 체크 + $is_anonymous = $this->_isAnonymous($this->_TYPE_COMMENT, $obj); + + // 멘션 + $mention_targets = $this->_getMentionTarget(strip_tags($obj->content)); + // !TODO 공용 메소드로 분리 + foreach($mention_targets as $mention_member_srl) + { + $target_member_config = $oNcenterliteModel->getMemberConfig($mention_member_srl); + $notify_member_config = $target_member_config->data; + if($notify_member_config->mention_notify == 'N') + { + continue; + } + + $args = new stdClass(); + $args->member_srl = $mention_member_srl; + $args->target_p_srl = $obj->comment_srl; + $args->srl = $obj->document_srl; + $args->target_srl = $obj->comment_srl; + $args->type = $this->_TYPE_COMMENT; + $args->target_type = $this->_TYPE_MENTION; + $args->target_url = getNotEncodedFullUrl('', 'document_srl', $document_srl, '_comment_srl', $comment_srl) . '#comment_'. $comment_srl; + $args->target_summary = cut_str(strip_tags($content), 50); + $args->target_nick_name = $obj->nick_name; + $args->target_email_address = $obj->email_address; + $args->regdate = date('YmdHis'); + $args->target_browser = $module_info->browser_title; + $args->notify = $this->_getNotifyId($args); + $output = $this->_insertNotify($args, $is_anonymous); + $notify_member_srl[] = $mention_member_srl; + } + + $admin_list = $oNcenterliteModel->getMemberAdmins(); + $admins_list = $admin_list->data; + + foreach($admins_list as $admins) + { + if(is_array($config->admin_comment_module_srls) && in_array($module_info->module_srl, $config->admin_comment_module_srls)) + { + $args = new stdClass(); + $args->member_srl = $admins->member_srl; + $args->target_p_srl = $obj->comment_srl; + $args->srl = $obj->document_srl; + $args->target_srl = $obj->comment_srl; + $args->type = $this->_TYPE_COMMENT; + $args->target_type = $this->_TYPE_ADMIN_COMMENT; + $args->target_url = getNotEncodedFullUrl('', 'document_srl', $document_srl, '_comment_srl', $comment_srl) . '#comment_'. $comment_srl; + $args->target_summary = cut_str(strip_tags($content), 50); + $args->target_nick_name = $obj->nick_name; + $args->target_email_address = $obj->email_address; + $args->regdate = date('YmdHis'); + $args->target_browser = $module_info->browser_title; + $args->notify = $this->_getNotifyId($args); + $output = $this->_insertNotify($args, $is_anonymous); + } + } + // 대댓글 + if($parent_srl) + { + $oCommentModel = getModel('comment'); + $oComment = $oCommentModel->getComment($parent_srl); + $member_srl = $oComment->member_srl; + $comment_member_config = $oNcenterliteModel->getMemberConfig($member_srl); + $parent_member_config = $comment_member_config->data; + + // !TODO 공용 메소드로 분리 + if(!in_array(abs($member_srl), $notify_member_srl) && (!$logged_info || ($member_srl != 0 && abs($member_srl) != $logged_info->member_srl)) && $parent_member_config->comment_notify != 'N') + { + $args = new stdClass(); + $args->member_srl = abs($member_srl); + $args->srl = $obj->document_srl; + $args->target_p_srl = $parent_srl; + $args->target_srl = $obj->comment_srl; + $args->type = $this->_TYPE_COMMENT; + $args->target_type = $this->_TYPE_COMMENT; + $args->target_url = getNotEncodedFullUrl('', 'document_srl', $document_srl, '_comment_srl', $comment_srl) . '#comment_'. $comment_srl; + $args->target_summary = cut_str(strip_tags($content), 50); + $args->target_nick_name = $obj->nick_name; + $args->target_email_address = $obj->email_address; + $args->regdate = $regdate; + $args->target_browser = $module_info->browser_title; + $args->notify = $this->_getNotifyId($args); + $output = $this->_insertNotify($args, $is_anonymous); + $notify_member_srl[] = abs($member_srl); + } + } + // 대댓글이 아니고, 게시글의 댓글을 남길 경우 + if(!$parent_srl || ($parent_srl && $config->document_notify == 'all-comment')) + { + $oDocumentModel = getModel('document'); + $oDocument = $oDocumentModel->getDocument($document_srl); + + $member_srl = $oDocument->get('member_srl'); + $comment_member_config = $oNcenterliteModel->getMemberConfig($member_srl); + $document_comment_member_config = $comment_member_config->data; + + // !TODO 공용 메소드로 분리 + if(!in_array(abs($member_srl), $notify_member_srl) && (!$logged_info || ($member_srl != 0 && abs($member_srl) != $logged_info->member_srl)) && $document_comment_member_config->comment_notify != 'N') + { + $args = new stdClass(); + $args->member_srl = abs($member_srl); + $args->srl = $document_srl; + $args->target_p_srl = $comment_srl; + $args->target_srl = $comment_srl; + $args->type = $this->_TYPE_DOCUMENT; + $args->target_type = $this->_TYPE_COMMENT; + $args->target_url = getNotEncodedFullUrl('', 'document_srl', $document_srl, '_comment_srl', $comment_srl) . '#comment_'. $comment_srl; + $args->target_summary = cut_str(strip_tags($content), 50); + $args->target_nick_name = $obj->nick_name; + $args->target_email_address = $obj->email_address; + $args->regdate = $regdate; + $args->target_browser = $module_info->browser_title; + $args->notify = $this->_getNotifyId($args); + $output = $this->_insertNotify($args, $is_anonymous); + } + } + + return new Object(); + } + + function triggerBeforeModuleObjectProc(&$oModule) + { + if(version_compare(__XE_VERSION__, '1.7.4', '>=')) + { + return new Object(); + } + $oNcenterliteModel = getModel('ncenterlite'); + $config = $oNcenterliteModel->getConfig(); + if($config->use != 'Y') return new Object(); + + $vars = Context::getRequestVars(); + $logged_info = Context::get('logged_info'); + + $messages_member_config = $oNcenterliteModel->getMemberConfig($logged_info->member_srl); + $message_member_config = $messages_member_config->data; + + // 쪽지 체크 및 유저 쪽지 알림 채크 + if($config->message_notify == 'Y' && $message_member_config->message_notify != 'N') + { + $flag_path = './files/ncenterlite/new_message_flags/'; + + $need_update = false; + // 쪽지 알림 메시지 체크 + if(strpos(Context::getHtmlFooter(), 'xeNotifyMessage') !== FALSE) + { + $need_update = true; + } + // 메시지 플래그 파일 체크 + else if(file_exists($flag_path . $logged_info->member_srl)) + { + $need_update = true; + } + + if($oModule->act == 'procCommunicationSendMessage') + { + FileHandler::makeDir($flag_path); + $flag_file = sprintf('%s%s', $flag_path, $vars->receiver_srl); + FileHandler::writeFile($flag_file, $vars->receiver_srl); + } + else if($need_update) + { + $oMemberModel = getModel('member'); + $_sender_member_srl = trim(FileHandler::readFile($flag_path . $logged_info->member_srl)); + $sender_member_info = $oMemberModel->getMemberInfoByMemberSrl($_sender_member_srl); + FileHandler::removeFile($flag_path . $logged_info->member_srl); + + // 새 쪽지 수 + $args = new stdClass(); + $args->receiver_srl = $logged_info->member_srl; + $output = executeQuery('ncenterlite.getCountNewMessage', $args); + $message_count = $output->data->count; + + // 기존 쪽지 알림을 읽은 것으로 변경 + $cond = new stdClass(); + $cond->type = $this->_TYPE_MESSAGE; + $cond->member_srl = $logged_info->member_srl; + $output = executeQuery('ncenterlite.updateNotifyReadedByType', $cond); + + if(!$message_count) return; + + // 알림 추가 + $args = new stdClass(); + $args->member_srl = $logged_info->member_srl; + $args->srl = $sender_member_info->member_srl; + if(!$args->srl) $args->srl = 0; + $args->target_p_srl = 1; + $args->target_srl = $sender_member_info->member_srl; + if(!$args->srl) $args->target_srl = 0; + $args->type = $this->_TYPE_MESSAGE; + $args->target_type = $this->_TYPE_MESSAGE; + $args->target_url_params = $target_url_params; + $args->target_summary = $message_count; + $args->target_nick_name = $sender_member_info->nick_name; + $args->target_member_srl = $sender_member_info->member_srl; + $args->regdate = date('YmdHis'); + $args->notify = $this->_getNotifyId($args); + $args->target_url = getNotEncodedFullUrl('', 'act', 'dispCommunicationMessages'); + + $output = $this->_insertNotify($args); + } + } + } + + function triggerAfterSendMessage(&$trigger_obj) + { + $oNcenterliteModel = getModel('ncenterlite'); + $config = $oNcenterliteModel->getConfig(); + if($config->use != 'Y') return new Object(); + if($config->message_notify == 'N') + { + return new Object(); + } + $messages_member_config = $oNcenterliteModel->getMemberConfig($trigger_obj->receiver_srl); + $message_member_config = $messages_member_config->data; + + if(version_compare(__XE_VERSION__, '1.8', '>=') && $message_member_config->message_notify != 'N') + { + $args = new stdClass(); + $args->member_srl = $trigger_obj->receiver_srl; + $args->srl = $trigger_obj->related_srl; + $args->target_p_srl = '1'; + $args->target_srl = $trigger_obj->message_srl; + $args->type = $this->_TYPE_MESSAGE; + $args->target_type = $this->_TYPE_MESSAGE; + $args->target_summary = $trigger_obj->title; + $args->regdate = date('YmdHis'); + $args->notify = $this->_getNotifyId($args); + $args->target_url = getNotEncodedFullUrl('', 'act', 'dispCommunicationMessages', 'message_srl', $trigger_obj->related_srl); + $output = $this->_insertNotify($args); + } + elseif($message_member_config->message_notify != 'N') + { + $args = new stdClass(); + $args->member_srl = $trigger_obj->receiver_srl; + $args->srl = $trigger_obj->receiver_srl; + $args->target_p_srl = '1'; + $args->target_srl = $trigger_obj->sender_srl; + $args->type = $this->_TYPE_MESSAGE; + $args->target_type = $this->_TYPE_MESSAGE; + $args->target_summary = $trigger_obj->title; + $args->regdate = date('YmdHis'); + $args->notify = $this->_getNotifyId($args); + $args->target_url = getNotEncodedFullUrl('', 'act', 'dispCommunicationMessages'); + $output = $this->_insertNotify($args); + } + + } + + function triggerAfterVotedupdate(&$obj) + { + $oDocumentModel = getModel('document'); + $oDocument = $oDocumentModel->getDocument($obj->document_srl, false, false); + + $oNcenterliteModel = getModel('ncenterlite'); + $config = $oNcenterliteModel->getConfig(); + if($config->use != 'Y') return new Object(); + if($config->voted_format != 'Y') return new Object(); + if($obj->point < 0) return new Object(); + + $args = new stdClass(); + $args->member_srl = $obj->member_srl; + $args->srl = $obj->document_srl; + $args->target_p_srl = '1'; + $args->target_srl = $obj->document_srl; + $args->type = $this->_TYPE_DOCUMENT; + $args->target_type = $this->_TYPE_VOTED; + $args->target_summary = $oDocument->get('title'); + $args->regdate = date('YmdHis'); + $args->notify = $this->_getNotifyId($args); + $args->target_url = getNotEncodedFullUrl('', 'document_srl', $obj->document_srl); + $output = $this->_insertNotify($args, $is_anonymous); + } + + function triggerAfterDeleteComment(&$obj) + { + $oNcenterliteModel = getModel('ncenterlite'); + $config = $oNcenterliteModel->getConfig(); + if($config->use != 'Y') return new Object(); + + $args = new stdClass(); + $args->srl = $obj->comment_srl; + $output = executeQuery('ncenterlite.deleteNotifyBySrl', $args); + return new Object(); + } + + function triggerAfterDeleteDocument(&$obj) + { + $oNcenterliteModel = getModel('ncenterlite'); + $config = $oNcenterliteModel->getConfig(); + if($config->use != 'Y') return new Object(); + + $args = new stdClass(); + $args->srl = $obj->document_srl; + $output = executeQuery('ncenterlite.deleteNotifyBySrl', $args); + return new Object(); + } + + function triggerAfterModuleHandlerProc(&$oModule) + { + $vars = Context::getRequestVars(); + $logged_info = Context::get('logged_info'); + $args = new stdClass(); + + if($oModule->getLayoutFile() == 'popup_layout.html') Context::set('ncenterlite_is_popup', TRUE); + + $oNcenterliteModel = getModel('ncenterlite'); + $config = $oNcenterliteModel->getConfig(); + if($config->use != 'Y') return new Object(); + + $this->_hide_ncenterlite = false; + if($oModule->module == 'beluxe' && Context::get('is_modal')) + { + $this->_hide_ncenterlite = true; + } + if($oModule->module == 'bodex' && Context::get('is_iframe')) + { + $this->_hide_ncenterlite = true; + } + if($oModule->getLayoutFile() == 'popup_layout.html') + { + $this->_hide_ncenterlite = true; + } + + if($oModule->act == 'dispBoardReplyComment') + { + $comment_srl = Context::get('comment_srl'); + $logged_info = Context::get('logged_info'); + if($comment_srl && $logged_info) + { + $args->target_srl = $comment_srl; + $args->member_srl = $logged_info->member_srl; + $output_update = executeQuery('ncenterlite.updateNotifyReadedByTargetSrl', $args); + } + } + else if($oModule->act == 'dispBoardContent') + { + $comment_srl = Context::get('_comment_srl'); + $document_srl = Context::get('document_srl'); + $oDocument = Context::get('oDocument'); + $logged_info = Context::get('logged_info'); + + if($document_srl && $logged_info && $config->document_read=='Y') + { + $args->srl = $document_srl; + $args->member_srl = $logged_info->member_srl; + $outputs = executeQuery('ncenterlite.updateNotifyReadedBySrl', $args); + } + + if($comment_srl && $document_srl && $oDocument) + { + $_comment_list = $oDocument->getComments(); + if($_comment_list) + { + if(array_key_exists($comment_srl, $_comment_list)) + { + $url = getNotEncodedUrl('_comment_srl','') . '#comment_' . $comment_srl; + $need_check_socialxe = true; + } + else + { + $cpage = $oDocument->comment_page_navigation->cur_page; + if($cpage > 1) + { + $url = getNotEncodedUrl('cpage', $cpage-1) . '#comment_' . $comment_srl; + $need_check_socialxe = true; + } + else + { + $url = getNotEncodedUrl('_comment_srl', '', 'cpage', '') . '#comment_' . $comment_srl; + } + } + + if($need_check_socialxe) + { + $oDB = &DB::getInstance(); + if($oDB->isTableExists('socialxe')) + { + $args = new stdClass(); + $oModuleModel = getModel('module'); + $module_info = $oModuleModel->getModuleInfoByDocumentSrl($document_srl); + $args->module_srl = $module_info->module_srl; + $output = executeQuery('ncenterlite.getSocialxeCount', $args); + if($output->data->cnt) + { + $socialxe_comment_srl = $comment_srl; + + $args = new stdClass(); + $args->comment_srl = $comment_srl; + $oCommentModel = getModel('comment'); + $oComment = $oCommentModel->getComment($comment_srl); + $parent_srl = $oComment->get('parent_srl'); + if($parent_srl) + { + $socialxe_comment_srl = $parent_srl; + } + + $url = getNotEncodedUrl('_comment_srl', '', 'cpage', '', 'comment_srl', $socialxe_comment_srl) . '#comment_' . $comment_srl; + } + } + } + + $url = str_replace('&','&',$url); + header('location: ' . $url); + Context::close(); + exit; + } + } + } + elseif($oModule->act == 'dispCommunicationMessages') + { + $message_srl = Context::get('message_srl'); + $logged_info = Context::get('logged_info'); + if($message_srl) + { + $args = new stdClass(); + $args->target_srl = $message_srl; + $args->member_srl = $logged_info->member_srl; + executeQuery('ncenterlite.updateNotifyReadedByTargetSrl', $args); + } + } + + // 지식인 모듈의 의견 + // TODO: 코드 분리 + if($oModule->act == 'procKinInsertComment') + { + // 글, 댓글 구분 + $parent_type = ($vars->document_srl == $vars->parent_srl) ? 'DOCUMENT' : 'COMMENT'; + if($parent_type == 'DOCUMENT') + { + $oDocumentModel = getModel('document'); + $oDocument = $oDocumentModel->getDocument($vars->document_srl); + $member_srl = $oDocument->get('member_srl'); + $type = $this->_TYPE_DOCUMENT; + } + else + { + $oCommentModel = getModel('comment'); + $oComment = $oCommentModel->getComment($vars->parent_srl); + $member_srl = $oComment->get('member_srl'); + $type = $this->_TYPE_COMMENT; + } + + if($logged_info->member_srl != $member_srl) + { + $args = new stdClass(); + $args->member_srl = abs($member_srl); + $args->srl = ($parent_type == 'DOCUMENT') ? $vars->document_srl : $vars->parent_srl; + $args->type = $type; + $args->target_type = $this->_TYPE_COMMENT; + $args->target_srl = $vars->parent_srl; + $args->target_p_srl = '1'; + $args->target_url = getNotEncodedFullUrl('', 'document_srl', $vars->document_srl, '_comment_srl', $vars->parent_srl) . '#comment_'. $vars->parent_srl; + $args->target_summary = cut_str(strip_tags($vars->content), 50); + $args->target_nick_name = $logged_info->nick_name; + $args->target_email_address = $logged_info->email_address; + $args->regdate = date('YmdHis'); + $args->notify = $this->_getNotifyId($args); + $output = $this->_insertNotify($args); + } + } + else if($oModule->act == 'dispKinView' || $oModule->act == 'dispKinIndex') + { + // 글을 볼 때 알림 제거 + $oDocumentModel = getModel('document'); + $oDocument = $oDocumentModel->getDocument($vars->document_srl); + $member_srl = $oDocument->get('member_srl'); + + if($logged_info->member_srl == $member_srl) + { + $args = new stdClass; + $args->member_srl = $logged_info->member_srl; + $args->srl = $vars->document_srl; + $args->type = $this->_TYPE_DOCUMENT; + $output = executeQuery('ncenterlite.updateNotifyReadedBySrl', $args); + } + } + else if($oModule->act == 'getKinComments') + { + // 의견을 펼칠 때 알림 제거 + $args = new stdClass; + $args->member_srl = $logged_info->member_srl; + $args->target_srl = $vars->parent_srl; + $output = executeQuery('ncenterlite.updateNotifyReadedByTargetSrl', $args); + } + + return new Object(); + } + + function triggerBeforeDisplay(&$output_display) + { + $act = Context::get('act'); + // 팝업창이면 중지 + if(Context::get('ncenterlite_is_popup')) return; + + // 자신의 알림목록을 보고 있을 경우엔 알림센터창을 띄우지 않는다. + if($act == 'dispNcenterliteNotifyList') return; + + if(count($this->disable_notify_bar_act)) + { + if(in_array(Context::get('act'), $this->disable_notify_bar_act)) return; + } + + // HTML 모드가 아니면 중지 + act에 admin이 포함되어 있으면 중지 + if(Context::getResponseMethod() != 'HTML' || strpos(strtolower(Context::get('act')), 'admin') !== false) return; + + $logged_info = Context::get('logged_info'); + + // 로그인 상태가 아니면 중지 + if(!$logged_info) return; + + $module_info = Context::get('module_info'); + + if(count($this->disable_notify_bar_mid)) + { + if(in_array($module_info->mid, $this->disable_notify_bar_mid)) return; + } + + // admin 모듈이면 중지 + if($module_info->module == 'admin' ) return; + + $oNcenterliteModel = getModel('ncenterlite'); + $config = $oNcenterliteModel->getConfig(); + + // 알림센터가 비활성화 되어 있으면 중지 + if($config->use != 'Y') return new Object(); + if($config->display_use == 'N') return new Object(); + + // 노티바 제외 페이지이면 중지 + if(in_array($module_info->module_srl, $config->hide_module_srls)) return new Object(); + + Context::set('ncenterlite_config', $config); + + $oModuleModel = getModel('module'); + $ncenterlite_module_info = $oModuleModel->getModuleInfoXml('ncenterlite'); + $jsCacheRefresh = '?'.$ncenterlite_module_info->version.'.'.$ncenterlite_module_info->date.'.js'; + Context::addJsFile('./modules/ncenterlite/tpl/js/ncenterlite.js'.$jsCacheRefresh, true, '', 100000); + + + $oNcenterliteModel = getModel('ncenterlite'); + + // 알림 목록 가져오기 + $_output = $oNcenterliteModel->getMyNotifyList(); + if(!$_output->data) return; // 알림 메시지가 없어도 항상 표시하게 하려면 이 줄을 제거 또는 주석 처리하세요. + + $_latest_notify_id = array_slice($_output->data, 0, 1); + $_latest_notify_id = $_latest_notify_id[0]->notify; + Context::set('ncenterlite_latest_notify_id', $_latest_notify_id); + + if($_COOKIE['_ncenterlite_hide_id'] && $_COOKIE['_ncenterlite_hide_id'] == $_latest_notify_id) return; + setcookie('_ncenterlite_hide_id', '', 0, '/'); + + $oMemberModel = getModel('member'); + $memberConfig = $oMemberModel->getMemberConfig(); + if($memberConfig->profile_image == 'Y') + { + $profileImage = $oMemberModel->getProfileImage($logged_info->member_srl); + Context::set('profileImage', $profileImage); + } + Context::set('useProfileImage', ($memberConfig->profile_image == 'Y') ? true : false); + + Context::set('ncenterlite_list', $_output->data); + Context::set('ncenterlite_page_navigation', $_output->page_navigation); + + if(Mobile::isFromMobilePhone()) + { + $this->template_path = sprintf('%sm.skins/%s/', $this->module_path, $config->mskin); + if(!is_dir($this->template_path)||!$config->mskin) + { + $config->mskin = 'default'; + $this->template_path = sprintf('%sm.skins/%s/',$this->module_path, $config->mskin); + } + } + else + { + $this->template_path = sprintf('%sskins/%s/', $this->module_path, $config->skin); + if(!is_dir($this->template_path)||!$config->skin) + { + $config->skin = 'default'; + $this->template_path = sprintf('%sskins/%s/',$this->module_path, $config->skin); + } + } + + Context::addHtmlFooter(''); + + $this->_addFile(); + $html = $this->_getTemplate(); + $output_display = $html . $output_display; + } + + function triggerAddMemberMenu() + { + $oNcenterliteModel = getModel('ncenterlite'); + $config = $oNcenterliteModel->getConfig(); + + if($config->user_config_list == 'Y') + { + $logged_info = Context::get('logged_info'); + if(!Context::get('is_logged')) return new Object(); + $target_srl = Context::get('target_srl'); + + $oMemberController = getController('member'); + $oMemberController->addMemberMenu('dispNcenterliteNotifyList', 'ncenterlite_my_list'); + $oMemberController->addMemberMenu('dispNcenterliteUserConfig', 'ncenterlite_my_settings'); + + if($logged_info->is_admin== 'Y') + { + $url = getUrl('','act','dispNcenterliteUserConfig','member_srl',$target_srl); + $str = Context::getLang('ncenterlite_user_settings'); + $oMemberController->addMemberPopupMenu($url, $str, ''); + } + } + + return new Object(); + } + + function _addFile() + { + $oModuleModel = getModel('module'); + $module_info = $oModuleModel->getModuleInfoXml('ncenterlite'); + if(file_exists(FileHandler::getRealPath($this->template_path . 'ncenterlite.css'))) + { + Context::addCssFile($this->template_path . 'ncenterlite.css', true, 'all', '', 100); + } + + $oNcenterliteModel = getModel('ncenterlite'); + $config = $oNcenterliteModel->getConfig(); + if(!Mobile::isFromMobilePhone()) + { + if($config->colorset && file_exists(FileHandler::getRealPath($this->template_path . 'ncenterlite.' . $config->colorset . '.css'))) + { + Context::addCssFile($this->template_path . 'ncenterlite.'.$config->colorset.'.css', true, 'all', '', 100); + } + } + elseif(Mobile::isFromMobilePhone()) + { + if($config->mcolorset && file_exists(FileHandler::getRealPath($this->template_path . 'ncenterlite.' . $config->mcolorset . '.css'))) + { + Context::addCssFile($this->template_path . 'ncenterlite.'.$config->mcolorset.'.css', true, 'all', '', 100); + } + + Context::loadFile(array('./common/js/jquery.min.js', 'head', '', -100000), true); + Context::loadFile(array('./common/js/xe.min.js', 'head', '', -100000), true); + Context::addCssFile($this->template_path . 'ncenterlite.mobile.css', true, 'all', '', 100); + } + if($config->zindex) + { + Context::set('ncenterlite_zindex', ' style="z-index:' . $config->zindex . ';" '); + } + } + + function _getTemplate() + { + $oNcenterModel = getModel('ncenterlite'); + $config = $oNcenterModel->getConfig(); + + $oTemplateHandler = TemplateHandler::getInstance(); + $result = ''; + + if(Mobile::isFromMobilePhone()) + { + $path = sprintf('%sm.skins/%s/', $this->module_path, $config->mskin); + } + else + { + $path = sprintf('%sskins/%s/', $this->module_path, $config->skin); + } + $result = $oTemplateHandler->compile($path, 'ncenterlite.html'); + + return $result; + } + + function updateNotifyRead($notify, $member_srl) + { + $args = new stdClass(); + $args->member_srl = $member_srl; + $args->notify = $notify; + $output = executeQuery('ncenterlite.updateNotifyReaded', $args); + //$output = executeQuery('ncenterlite.deleteNotify', $args); + + return $output; + } + + function updateNotifyReadiByTargetSrl($target_srl, $member_srl) + { + $args = new stdClass(); + $args->member_srl = $member_srl; + $args->target_srl = $target_srl; + $output = executeQuery('ncenterlite.updateNotifyReadedByTargetSrl', $args); + //$output = executeQuery('ncenterlite.deleteNotifyByTargetSrl', $args); + + return $output; + } + + function updateNotifyReadAll($member_srl) + { + $args = new stdClass(); + $args->member_srl = $member_srl; + $output = executeQuery('ncenterlite.updateNotifyReadedAll', $args); + //$output = executeQuery('ncenterlite.deleteNotifyByMemberSrl', $args); + + return $ouptut; + } + + function procNcenterliteNotifyRead() + { + $logged_info = Context::get('logged_info'); + $target_srl = Context::get('target_srl'); + if(!$logged_info || !$target_srl) return new Object(-1, 'msg_invalid_request'); + + $output = $this->updateNotifyRead($notify, $logged_info->member_srl); + return $output; + } + + function procNcenterliteNotifyReadAll() + { + $logged_info = Context::get('logged_info'); + if(!$logged_info) return new Object(-1, 'msg_invalid_request'); + + $output = $this->updateNotifyReadAll($logged_info->member_srl); + return $output; + } + + function procNcenterliteRedirect() + { + $logged_info = Context::get('logged_info'); + $url = Context::get('url'); + $notify = Context::get('notify'); + if(!$logged_info || !$url || !$notify) return new Object(-1, 'msg_invalid_request'); + + $output = $this->updateNotifyRead($notify, $logged_info->member_srl); + if(!$output->toBool()) return $output; + + $url = str_replace('&', '&', $url); + header('Location: ' . $url, TRUE, 302); + Context::close(); + exit; + } + + /** + * @brief 익명으로 노티해야 할지 체크하여 반환 + * @return boolean + **/ + function _isAnonymous($source_type, $triggerObj) + { + // 회원번호가 음수 + if($triggerObj->member_srl < 0) return TRUE; + + $module_info = Context::get('module_info'); + + // DX 익명 체크박스 + if($module_info->module == 'beluxe' && $triggerObj->anonymous == 'Y') return TRUE; + + if($source_type == $this->_TYPE_COMMENT) + { + // DX 익명 강제 + if($module_info->module == 'beluxe' && $module_info->use_anonymous == 'Y') return TRUE; + } + + if($source_type == $this->_TYPE_DOCUMENT) + { + // DX 익명 강제 + if($module_info->module == 'beluxe' && $module_info->use_anonymous == 'Y') return TRUE; + } + + return FALSE; + } + + function _insertNotify($args, $anonymous = FALSE) + { + $oNcenterliteModel = getModel('ncenterlite'); + $config = $oNcenterliteModel->getConfig(); + // 비회원 노티 제거 + if($args->member_srl <= 0) return new Object(); + + $logged_info = Context::get('logged_info'); + + if($anonymous == TRUE) + { + // 설정에서 익명 이름이 설정되어 있으면 익명 이름을 설정함. 없을 경우 Anonymous 를 사용한다. + if(!$config->anonymous_name) + { + $anonymous_name = 'Anonymous'; + } + else + { + $anonymous_name = $config->anonymous_name; + } + // 익명 노티 시 회원정보 제거 + $args->target_member_srl = 0; + $args->target_nick_name = $anonymous_name; + $args->target_user_id = $anonymous_name; + $args->target_email_address = $anonymous_name; + } + else if($logged_info) + { + // 익명 노티가 아닐 때 로그인 세션의 회원정보 넣기 + $args->target_member_srl = $logged_info->member_srl; + $args->target_nick_name = $logged_info->nick_name; + $args->target_user_id = $logged_info->user_id; + $args->target_email_address = $logged_info->email_address; + } + else if($args->target_member_srl) + { + $oMemberModel = getModel('member'); + $member_info = $oMemberModel->getMemberInfoByMemberSrl($args->target_member_srl); + $args->target_member_srl = $member_info->member_srl; + $args->target_nick_name = $member_info->nick_name; + $args->target_user_id = $member_info->user_id; + $args->target_email_address = $member_info->email_address; + } + else + { + // 비회원 + $args->target_member_srl = 0; + $args->target_user_id = ''; + } + + $output = executeQuery('ncenterlite.insertNotify', $args); + if(!$output->toBool()) + { + return $output; + } + + if($output->toBool()) + { + $trigger_notify = ModuleHandler::triggerCall('ncenterlite._insertNotify', 'after', $args); + if(!$trigger_notify->toBool()) + { + return $trigger_notify; + } + } + + return $output; + } + + /** + * @brief 노티 ID 반환 + **/ + function _getNotifyId($args) + { + return md5(uniqid('') . $args->member_srl . $args->srl . $args->target_srl . $args->type . $args->target_type); + } + + /** + * @brief 멘션 대상 member_srl 목록 반환 + * @return array + **/ + function _getMentionTarget($content) + { + $oNcenterliteModel = getModel('ncenterlite'); + $config = $oNcenterliteModel->getConfig(); + $logged_info = Context::get('logged_info'); + + $list = array(); + + $content = strip_tags($content); + $content = str_replace(' ', ' ', $content); + + // 정규표현식 정리 + $split = array(); + if(in_array('comma', $config->mention_format)) $split[] = ','; + $regx = join('', array('/(^|\s)@([^@\s', join('', $split), ']+)/i')); + + preg_match_all($regx, $content, $matches); + + // '님'문자 이후 제거 + if(in_array('respect', $config->mention_format)) + { + foreach($matches[2] as $idx => $item) + { + $pos = strpos($item, '님'); + if($pos !== false && $pos > 0) + { + $matches[2][$idx] = trim(substr($item, 0, $pos)); + if($logged_info && $logged_info->nick_name == $matches[2][$idx]) unset($matches[2][$idx]); + } + } + } + + $nicks = array_unique($matches[2]); + + $oMemberModel = getModel('member'); + $member_config = $oMemberModel->getMemberConfig(); + + if($config->mention_names == 'id' && $member_config->identifier != 'email_address') + { + foreach($nicks as $user_id) + { + $vars = new stdClass(); + $vars->user_id = $user_id; + $output = executeQuery('ncenterlite.getMemberSrlById', $vars); + if($output->data && $output->data->member_srl) $list[] = $output->data->member_srl; + } + } + else + { + foreach($nicks as $nick_name) + { + $vars = new stdClass(); + $vars->nick_name = $nick_name; + $output = executeQuery('ncenterlite.getMemberSrlByNickName', $vars); + if($output->data && $output->data->member_srl) $list[] = $output->data->member_srl; + } + } + + return $list; + } +} diff --git a/modules/ncenterlite/ncenterlite.mobile.php b/modules/ncenterlite/ncenterlite.mobile.php new file mode 100644 index 000000000..8d8a5a7c0 --- /dev/null +++ b/modules/ncenterlite/ncenterlite.mobile.php @@ -0,0 +1,38 @@ +getConfig(); + $template_path = sprintf("%sm.skins/%s/",$this->module_path, $config->mskin); + if(!is_dir($template_path)||!$config->mskin) + { + $config->skin = 'default'; + $template_path = sprintf("%sm.skins/%s/",$this->module_path, $config->mskin); + } + $this->setTemplatePath($template_path); + + $oLayoutModel = getModel('layout'); + $layout_info = $oLayoutModel->getLayout($config->mlayout_srl); + + if($layout_info) + { + $this->module_info->mlayout_srl = $config->mlayout_srl; + $this->setLayoutPath($layout_info->path); + } + + } + + function dispNcenterliteNotifyList() + { + parent::dispNcenterliteNotifyList(); + } + + function dispNcenterliteUserConfig() + { + parent::dispNcenterliteUserConfig(); + } + +} diff --git a/modules/ncenterlite/ncenterlite.model.php b/modules/ncenterlite/ncenterlite.model.php new file mode 100644 index 000000000..bb4c48c18 --- /dev/null +++ b/modules/ncenterlite/ncenterlite.model.php @@ -0,0 +1,376 @@ +getModuleConfig('ncenterlite'); + if(!$config->use) $config->use = 'Y'; + if(!$config->display_use) $config->display_use = 'Y'; + + if(!$config->mention_names) $config->mention_names = 'nick_name'; + if(!$config->message_notify) $config->message_notify = 'Y'; + if(!$config->mention_format && !is_array($config->mention_format)) $config->mention_format = array('respect'); + if(!is_array($config->mention_format)) $config->mention_format = explode('|@|', $config->mention_format); + if(!$config->document_notify) $config->document_notify = 'direct-comment'; + if(!$config->hide_module_srls) $config->hide_module_srls = array(); + if(!is_array($config->hide_module_srls)) $config->hide_module_srls = explode('|@|', $config->hide_module_srls); + if(!$config->document_read) $config->document_read = 'N'; + if(!$config->voted_format) $config->voted_format = 'N'; + if(!$config->skin) $config->skin = 'default'; + if(!$config->colorset) $config->colorset = 'black'; + + self::$config = $config; + } + + return self::$config; + } + + function getNotifyTypebySrl($notify_srl='') + { + $args = new stdClass(); + $args->notify_type_srl = $notify_srl; + + $output = executeQuery('ncenterlite.getNotifyType',$args); + + return $output; + } + + function getNotifyTypeString($notify_srl='',$notify_args) + { + $this->notify_args = $notify_args; + + $output = $this->getNotifyTypebySrl($notify_srl); + + $this->notify_arguments = explode("|",$output->data->notify_type_args); + $string = preg_replace_callback("/%([^%]*)%/",array($this, 'replaceNotifyType'),$output->data->notify_string); + + return $string; + } + + function replaceNotifyType($match) + { + //if replace string is not at arguments, return + if(!in_array($match[1],$this->notify_arguments)) + { + return $match[0]; + } + + //if replace string is not set, return + if(!isset($this->notify_args->{$match[1]})) + { + return $match[0]; + } + + return $this->notify_args->{$match[1]}; + } + + function isNotifyTypeExistsbySrl($notify_srl='') + { + $args = new stdClass(); + $args->notify_type_srl = $notify_srl; + + $output = executeQuery('ncenterlite.getNotifyType',$args); + + return isset($output->data->notify_type_id); + } + + function insertNotifyType($args) + { + return executeQuery('ncenterlite.insertNotifyType',$args); + } + + function getMemberConfig($member_srl=null) + { + if(!$member_srl) + { + $logged_info = Context::get('logged_info'); + $member_srl = $logged_info->member_srl; + } + + $args = new stdClass(); + $args->member_srl = $member_srl; + $output = executeQuery('ncenterlite.getUserConfig', $args); + if(!$output->data) return $output->data; + + return $output; + } + + function getAllMemberConfig() + { + $output = executeQueryArray('ncenterlite.getAllUserConfig'); + + return $output; + } + + function getMyNotifyList($member_srl=null, $page=1, $readed='N') + { + global $lang; + + $act = Context::get('act'); + if($act=='dispNcenterliteNotifyList') + { + $output = $this->getMyDispNotifyList($member_srl); + } + else + { + $output = $this->_getMyNotifyList($member_srl, $page, $readed); + } + + $oMemberModel = getModel('member'); + $list = $output->data; + + foreach($list as $k => $v) + { + + $target_member = $v->target_nick_name; + + switch($v->type) + { + case 'D': + $type = $lang->ncenterlite_document; //$type = '글'; + break; + case 'C': + $type = $lang->ncenterlite_comment; //$type = '댓글'; + break; + // 메시지. 쪽지 + case 'E': + $type = $lang->ncenterlite_type_message; //$type = '쪽지'; + break; + } + + switch($v->target_type) + { + case 'C': + $str = sprintf($lang->ncenterlite_commented, $target_member, $type, $v->target_summary); + //$str = sprintf('%s님이 회원님의 %s에 "%s" 댓글을 남겼습니다.', $target_member, $type, $v->target_summary); + break; + case 'A': + $str = sprintf($lang->ncenterlite_commented_board, $target_member, $v->target_browser, $v->target_summary); + //$str = sprintf('%1$s님이 게시판 "%2$s""%3$s"라고 댓글을 남겼습니다.', $target_member, $type, $v->target_summary); + break; + case 'M': + $str = sprintf($lang->ncenterlite_mentioned, $target_member, $v->target_summary, $type); + //$str = sprintf('%s님이 "%s" %s에서 회원님을 언급하였습니다.', $target_member, $v->target_summary, $type); + break; + // 메시지. 쪽지 + case 'E': + if(version_compare(__XE_VERSION__, '1.7.4', '>=')) + { + $str = sprintf($lang->ncenterlite_message_mention,$target_member, $v->target_summary); + } + else + { + $str = sprintf($lang->ncenterlite_message_string, $v->target_summary); + } + break; + case 'T': + $str = sprintf($lang->ncenterlite_test_noti, $target_member); + break; + case 'P': + $str = sprintf($lang->ncenterlite_board, $target_member, $v->target_browser, $v->target_summary); + break; + case 'S': + if($v->target_browser) + { + $str = sprintf($lang->ncenterlite_board, $target_member, $v->target_browser, $v->target_summary); + } + else + { + $str = sprintf($lang->ncenterlite_article, $target_member, $v->target_summary); + } + break; + case 'V': + $str = sprintf($lang->ncenterlite_vote, $target_member, $v->target_summary); + break; + } + + if($v->type=='U') + { + $str = $this->getNotifyTypeString($v->notify_type,unserialize($v->target_body)); + } + + $v->text = $str; + $v->ago = $this->getAgo($v->regdate); + $v->url = getUrl('','act','procNcenterliteRedirect', 'notify', $v->notify, 'url', $v->target_url); + if($v->target_member_srl) + { + $profileImage = $oMemberModel->getProfileImage($v->target_member_srl); + $v->profileImage = $profileImage->src; + } + + $list[$k] = $v; + } + + $output->data = $list; + return $output; + } + + function getMyNotifyListTpl() + { + $logged_info = Context::get('logged_info'); + if(!$logged_info) return new Object(-1, 'msg_not_permitted'); + + $oMemberModel = getModel('member'); + $memberConfig = $oMemberModel->getMemberConfig(); + $page = Context::get('page'); + + $member_srl = $logged_info->member_srl; + $tmp = $this->getMyNotifyList($member_srl, $page); + foreach($tmp->data as $key => $obj) + { + $tmp->data[$key]->url = str_replace('&', '&', $obj->url); + } + + $list->data = $tmp->data; + $list->page = $tmp->page_navigation; + $this->add('list', $list); + $this->add('useProfileImage', $memberConfig->profile_image); + } + + function _getMyNotifyList($member_srl=null, $page=1, $readed='N') + { + if(!$member_srl) + { + $logged_info = Context::get('logged_info'); + if(!$logged_info) return array(); + + $member_srl = $logged_info->member_srl; + } + + $args = new stdClass(); + $args->member_srl = $member_srl; + $args->page = $page ? $page : 1; + if($readed) $args->readed = $readed; + $output = executeQueryArray('ncenterlite.getNotifyList', $args); + if(!$output->data) $output->data = array(); + + return $output; + } + + function getMyDispNotifyList($member_srl) + { + + $logged_info = Context::get('logged_info'); + + $member_srl = $logged_info->member_srl; + + $args = new stdClass(); + $args->page = Context::get('page'); + $args->list_count = '20'; + $args->page_count = '10'; + $args->member_srl = $member_srl; + $output = executeQueryArray('ncenterlite.getDispNotifyList', $args); + if(!$output->data) $output->data = array(); + + return $output; + } + + function getNcenterliteAdminList($member_srl) + { + $logged_info = Context::get('logged_info'); + + $member_srl = $logged_info->member_srl; + + $args = new stdClass(); + $args->page = Context::get('page'); + $args->list_count = '20'; + $args->page_count = '10'; + $output = executeQueryArray('ncenterlite.getAdminNotifyList', $args); + if(!$output->data) $output->data = array(); + + return $output; + } + + function getMemberAdmins() + { + $args = new stdClass(); + $args->is_admin = 'Y'; + $output = executeQueryArray('ncenterlite.getMemberAdmins', $args); + if(!$output->data) $output->data = array(); + + return $output; + } + + function _getNewCount($member_srl=null) + { + if(!$member_srl) + { + $logged_info = Context::get('logged_info'); + if(!$logged_info) return 0; + + $member_srl = $logged_info->member_srl; + } + + $args->member_srl = $member_srl; + $output = executeQuery('ncenterlite.getNotifyNewCount', $args); + if(!$output->data) return 0; + return $output->data->cnt; + } + + + function getColorsetList() + { + $oModuleModel = getModel('module'); + $skin = Context::get('skin'); + + $skin_info = $oModuleModel->loadSkinInfo($this->module_path, $skin); + + for($i=0, $c=count($skin_info->colorset); $i<$c; $i++) + { + $colorset = sprintf('%s|@|%s', $skin_info->colorset[$i]->name, $skin_info->colorset[$i]->title); + $colorset_list[] = $colorset; + } + + if(count($colorset_list)) $colorsets = implode("\n", $colorset_list); + $this->add('colorset_list', $colorsets); + } + + /** + * @brief 주어진 시간이 얼마 전 인지 반환 + * @param string YmdHis + * @return string + **/ + function getAgo($datetime) + { + global $lang; + $lang_type = Context::getLangType(); + + $display = $lang->ncenterlite_date; // array('Year', 'Month', 'Day', 'Hour', 'Minute', 'Second') + + $ago = $lang->ncenterlite_ago; // 'Ago' + + $date = getdate(strtotime(zdate($datetime, 'Y-m-d H:i:s'))); + + $current = getdate(); + $p = array('year', 'mon', 'mday', 'hours', 'minutes', 'seconds'); + $factor = array(0, 12, 30, 24, 60, 60); + + for($i = 0; $i < 6; $i++) + { + if($i > 0) + { + $current[$p[$i]] += $current[$p[$i - 1]] * $factor[$i]; + $date[$p[$i]] += $date[$p[$i - 1]] * $factor[$i]; + } + + if($current[$p[$i]] - $date[$p[$i]] > 1) + { + $value = $current[$p[$i]] - $date[$p[$i]]; + if($lang_type == 'en') + { + return $value . ' ' . $display[$i] . (($value != 1) ? 's' : '') . ' ' . $ago; + } + return $value . $display[$i] . ' ' . $ago; + } + } + + return zdate($datetime, 'Y-m-d'); + } +} diff --git a/modules/ncenterlite/ncenterlite.view.php b/modules/ncenterlite/ncenterlite.view.php new file mode 100644 index 000000000..13c9b4823 --- /dev/null +++ b/modules/ncenterlite/ncenterlite.view.php @@ -0,0 +1,71 @@ +getConfig(); + $template_path = sprintf("%sskins/%s/",$this->module_path, $config->skin); + if(!is_dir($template_path)||!$config->skin) + { + $config->skin = 'default'; + $template_path = sprintf("%sskins/%s/",$this->module_path, $config->skin); + } + $this->setTemplatePath($template_path); + + $oLayoutModel = getModel('layout'); + $layout_info = $oLayoutModel->getLayout($config->layout_srl); + + if($layout_info) + { + $this->module_info->layout_srl = $config->layout_srl; + $this->setLayoutPath($layout_info->path); + } + } + + function dispNcenterliteNotifyList() + { + $oNcenterliteModel = getModel('ncenterlite'); + + $output = $oNcenterliteModel->getMyNotifyList(); + + Context::set('total_count', $output->page_navigation->total_count); + Context::set('total_page', $output->page_navigation->total_page); + Context::set('page', $output->page); + Context::set('ncenterlite_list', $output->data); + Context::set('page_navigation', $output->page_navigation); + + $this->setTemplateFile('NotifyList'); + } + + function dispNcenterliteUserConfig() + { + $oMemberModel = getModel('member'); + $member_srl = Context::get('member_srl'); + $logged_info = Context::get('logged_info'); + if(!$logged_info) return new Object(-1, 'ncenterlite_stop_login_required'); + + if($logged_info->is_admin == 'Y') + { + $member_info = $oMemberModel->getMemberInfoByMemberSrl($member_srl); + } + if($logged_info->is_admin != 'Y' && $member_srl) + { + if($member_srl != $logged_info->member_srl) + { + return new Object(-1, 'ncenterlite_stop_no_permission_other_user'); + } + } + $oNcenterliteModel = getModel('ncenterlite'); + if(!$member_srl) + { + $member_srl = $logged_info->member_srl; + } + $output = $oNcenterliteModel->getMemberConfig($member_srl); + + Context::set('member_info', $member_info); + Context::set('user_config', $output->data); + $this->setTemplateFile('userconfig'); + } +} diff --git a/modules/ncenterlite/queries/deleteNotify.xml b/modules/ncenterlite/queries/deleteNotify.xml new file mode 100644 index 000000000..53f3b42a6 --- /dev/null +++ b/modules/ncenterlite/queries/deleteNotify.xml @@ -0,0 +1,10 @@ + + +
+ + + + + + + diff --git a/modules/ncenterlite/queries/deleteNotifyAll.xml b/modules/ncenterlite/queries/deleteNotifyAll.xml new file mode 100644 index 000000000..f6886172b --- /dev/null +++ b/modules/ncenterlite/queries/deleteNotifyAll.xml @@ -0,0 +1,8 @@ + + +
+ + + + + diff --git a/modules/ncenterlite/queries/deleteNotifyByMemberSrl.xml b/modules/ncenterlite/queries/deleteNotifyByMemberSrl.xml new file mode 100644 index 000000000..35492b021 --- /dev/null +++ b/modules/ncenterlite/queries/deleteNotifyByMemberSrl.xml @@ -0,0 +1,10 @@ + + +
+ + + + + + + diff --git a/modules/ncenterlite/queries/deleteNotifyBySrl.xml b/modules/ncenterlite/queries/deleteNotifyBySrl.xml new file mode 100644 index 000000000..384f992c0 --- /dev/null +++ b/modules/ncenterlite/queries/deleteNotifyBySrl.xml @@ -0,0 +1,10 @@ + + +
+ + + + + + + diff --git a/modules/ncenterlite/queries/deleteNotifyByTargetSrl.xml b/modules/ncenterlite/queries/deleteNotifyByTargetSrl.xml new file mode 100644 index 000000000..202147b77 --- /dev/null +++ b/modules/ncenterlite/queries/deleteNotifyByTargetSrl.xml @@ -0,0 +1,10 @@ + + +
+ + + + + + + diff --git a/modules/ncenterlite/queries/getAdminNotifyList.xml b/modules/ncenterlite/queries/getAdminNotifyList.xml new file mode 100644 index 000000000..d1a12d794 --- /dev/null +++ b/modules/ncenterlite/queries/getAdminNotifyList.xml @@ -0,0 +1,11 @@ + + +
+ + + + + + + + diff --git a/modules/ncenterlite/queries/getAllUserConfig.xml b/modules/ncenterlite/queries/getAllUserConfig.xml new file mode 100644 index 000000000..61bb35180 --- /dev/null +++ b/modules/ncenterlite/queries/getAllUserConfig.xml @@ -0,0 +1,11 @@ + + +
+ + + + + + + + diff --git a/modules/ncenterlite/queries/getCountNewMessage.xml b/modules/ncenterlite/queries/getCountNewMessage.xml new file mode 100644 index 000000000..ba1aa4a84 --- /dev/null +++ b/modules/ncenterlite/queries/getCountNewMessage.xml @@ -0,0 +1,13 @@ + + +
+ + + + + + + + + + diff --git a/modules/ncenterlite/queries/getDispNotifyList.xml b/modules/ncenterlite/queries/getDispNotifyList.xml new file mode 100644 index 000000000..e39b573b2 --- /dev/null +++ b/modules/ncenterlite/queries/getDispNotifyList.xml @@ -0,0 +1,20 @@ + + +
+ + + + + + + + + + + + + + + + + diff --git a/modules/ncenterlite/queries/getMemberAdmins.xml b/modules/ncenterlite/queries/getMemberAdmins.xml new file mode 100644 index 000000000..46a776b5c --- /dev/null +++ b/modules/ncenterlite/queries/getMemberAdmins.xml @@ -0,0 +1,12 @@ + + +
+ + + + + + + + + diff --git a/modules/ncenterlite/queries/getMemberSrlById.xml b/modules/ncenterlite/queries/getMemberSrlById.xml new file mode 100644 index 000000000..2fcec16c1 --- /dev/null +++ b/modules/ncenterlite/queries/getMemberSrlById.xml @@ -0,0 +1,13 @@ + + +
+ + + + + + + + + + diff --git a/modules/ncenterlite/queries/getMemberSrlByNickName.xml b/modules/ncenterlite/queries/getMemberSrlByNickName.xml new file mode 100644 index 000000000..3bb63a178 --- /dev/null +++ b/modules/ncenterlite/queries/getMemberSrlByNickName.xml @@ -0,0 +1,13 @@ + + +
+ + + + + + + + + + diff --git a/modules/ncenterlite/queries/getMemberTotals.xml b/modules/ncenterlite/queries/getMemberTotals.xml new file mode 100644 index 000000000..9036bc858 --- /dev/null +++ b/modules/ncenterlite/queries/getMemberTotals.xml @@ -0,0 +1,12 @@ + + +
+ + + + + + + + + diff --git a/modules/ncenterlite/queries/getNotifyList.xml b/modules/ncenterlite/queries/getNotifyList.xml new file mode 100644 index 000000000..fc8901dde --- /dev/null +++ b/modules/ncenterlite/queries/getNotifyList.xml @@ -0,0 +1,21 @@ + + +
+ + + + + + + + + + + + + + + + + + diff --git a/modules/ncenterlite/queries/getNotifyNewCount.xml b/modules/ncenterlite/queries/getNotifyNewCount.xml new file mode 100644 index 000000000..dd6b55f1f --- /dev/null +++ b/modules/ncenterlite/queries/getNotifyNewCount.xml @@ -0,0 +1,14 @@ + + +
+ + + + + + + + + + + diff --git a/modules/ncenterlite/queries/getNotifyType.xml b/modules/ncenterlite/queries/getNotifyType.xml new file mode 100644 index 000000000..803ce2aba --- /dev/null +++ b/modules/ncenterlite/queries/getNotifyType.xml @@ -0,0 +1,13 @@ + + +
+ + + + + + + + + + diff --git a/modules/ncenterlite/queries/getNotifyTypeByID.xml b/modules/ncenterlite/queries/getNotifyTypeByID.xml new file mode 100644 index 000000000..c2977adfe --- /dev/null +++ b/modules/ncenterlite/queries/getNotifyTypeByID.xml @@ -0,0 +1,13 @@ + + +
+ + + + + + + + + + diff --git a/modules/ncenterlite/queries/getSocialxeCount.xml b/modules/ncenterlite/queries/getSocialxeCount.xml new file mode 100644 index 000000000..33b2c96ae --- /dev/null +++ b/modules/ncenterlite/queries/getSocialxeCount.xml @@ -0,0 +1,13 @@ + + +
+ + + + + + + + + + diff --git a/modules/ncenterlite/queries/getUserConfig.xml b/modules/ncenterlite/queries/getUserConfig.xml new file mode 100644 index 000000000..2a0ce0f41 --- /dev/null +++ b/modules/ncenterlite/queries/getUserConfig.xml @@ -0,0 +1,13 @@ + + +
+ + + + + + + + + + diff --git a/modules/ncenterlite/queries/insertNotify.xml b/modules/ncenterlite/queries/insertNotify.xml new file mode 100644 index 000000000..24e96cdd4 --- /dev/null +++ b/modules/ncenterlite/queries/insertNotify.xml @@ -0,0 +1,24 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/modules/ncenterlite/queries/insertNotifyType.xml b/modules/ncenterlite/queries/insertNotifyType.xml new file mode 100644 index 000000000..5f1e1f27e --- /dev/null +++ b/modules/ncenterlite/queries/insertNotifyType.xml @@ -0,0 +1,11 @@ + + +
+ + + + + + + + diff --git a/modules/ncenterlite/queries/insertUserConfig.xml b/modules/ncenterlite/queries/insertUserConfig.xml new file mode 100644 index 000000000..c2bc581e3 --- /dev/null +++ b/modules/ncenterlite/queries/insertUserConfig.xml @@ -0,0 +1,12 @@ + + +
+ + + + + + + + + diff --git a/modules/ncenterlite/queries/updateNotifyReaded.xml b/modules/ncenterlite/queries/updateNotifyReaded.xml new file mode 100644 index 000000000..23f9f24bc --- /dev/null +++ b/modules/ncenterlite/queries/updateNotifyReaded.xml @@ -0,0 +1,12 @@ + + +
+ + + + + + + + + diff --git a/modules/ncenterlite/queries/updateNotifyReadedAll.xml b/modules/ncenterlite/queries/updateNotifyReadedAll.xml new file mode 100644 index 000000000..935cea3bf --- /dev/null +++ b/modules/ncenterlite/queries/updateNotifyReadedAll.xml @@ -0,0 +1,11 @@ + + +
+ + + + + + + + diff --git a/modules/ncenterlite/queries/updateNotifyReadedBySrl.xml b/modules/ncenterlite/queries/updateNotifyReadedBySrl.xml new file mode 100644 index 000000000..2ecae2689 --- /dev/null +++ b/modules/ncenterlite/queries/updateNotifyReadedBySrl.xml @@ -0,0 +1,13 @@ + + +
+ + + + + + + + + + diff --git a/modules/ncenterlite/queries/updateNotifyReadedByTargetSrl.xml b/modules/ncenterlite/queries/updateNotifyReadedByTargetSrl.xml new file mode 100644 index 000000000..842363152 --- /dev/null +++ b/modules/ncenterlite/queries/updateNotifyReadedByTargetSrl.xml @@ -0,0 +1,12 @@ + + +
+ + + + + + + + + diff --git a/modules/ncenterlite/queries/updateNotifyReadedByType.xml b/modules/ncenterlite/queries/updateNotifyReadedByType.xml new file mode 100644 index 000000000..6f6328df3 --- /dev/null +++ b/modules/ncenterlite/queries/updateNotifyReadedByType.xml @@ -0,0 +1,12 @@ + + +
+ + + + + + + + + diff --git a/modules/ncenterlite/queries/updateUserConfig.xml b/modules/ncenterlite/queries/updateUserConfig.xml new file mode 100644 index 000000000..7bfcb07d6 --- /dev/null +++ b/modules/ncenterlite/queries/updateUserConfig.xml @@ -0,0 +1,14 @@ + + +
+ + + + + + + + + + + diff --git a/modules/ncenterlite/ruleset/insertConfig.xml b/modules/ncenterlite/ruleset/insertConfig.xml new file mode 100644 index 000000000..1015ba521 --- /dev/null +++ b/modules/ncenterlite/ruleset/insertConfig.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/modules/ncenterlite/schemas/ncenterlite_notify.xml b/modules/ncenterlite/schemas/ncenterlite_notify.xml new file mode 100644 index 000000000..083da9708 --- /dev/null +++ b/modules/ncenterlite/schemas/ncenterlite_notify.xml @@ -0,0 +1,24 @@ +
+ + + + + + + + + + + + + + + + + + + + + + +
diff --git a/modules/ncenterlite/schemas/ncenterlite_notify_type.xml b/modules/ncenterlite/schemas/ncenterlite_notify_type.xml new file mode 100644 index 000000000..5beb4bf6e --- /dev/null +++ b/modules/ncenterlite/schemas/ncenterlite_notify_type.xml @@ -0,0 +1,6 @@ + + + + + +
diff --git a/modules/ncenterlite/schemas/ncenterlite_user_set.xml b/modules/ncenterlite/schemas/ncenterlite_user_set.xml new file mode 100644 index 000000000..0562ed1e4 --- /dev/null +++ b/modules/ncenterlite/schemas/ncenterlite_user_set.xml @@ -0,0 +1,7 @@ + + + + + + +
diff --git a/modules/ncenterlite/skins/default/NotifyList.html b/modules/ncenterlite/skins/default/NotifyList.html new file mode 100644 index 000000000..dae56b337 --- /dev/null +++ b/modules/ncenterlite/skins/default/NotifyList.html @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + {@ + $oMemberModel = getModel('member'); + $member_info = $oMemberModel->getMemberInfoByMemberSrl($val->member_srl); + } + + + + + + + + + +
{$lang->ncenterlite_sender}{$lang->ncenterlite_addressee}{$lang->ncenterlite_noti_contents}{$lang->ncenterlite_read}{$lang->date}
{$val->target_nick_name}{$member_info->nick_name} {$lang->ncenterlite_no_target}{$val->text} + {$lang->ncenterlite_read_y} + {$lang->ncenterlite_read_n} + + {zdate($val->regdate,"Y-m-d")} +
+ {zdate($val->regdate,"H:i:s")} +
+ + + + \ No newline at end of file diff --git a/modules/ncenterlite/skins/default/img/p.png b/modules/ncenterlite/skins/default/img/p.png new file mode 100644 index 0000000000000000000000000000000000000000..44e3d8d1203e991132d400e26b13aa3e469862d9 GIT binary patch literal 7463 zcmV+?9oXWDP)5000;CX+uL$Nkc;* zP;zf(X>4Tx09b{USqC_l(c3@I^SsM=?Y(91y_3E79;v+crq@;^B&(2-LJEl#O;kc8 zC0U75q!3D_fvkKFjsO4qkL$a>b6w{=zxRIbbD#U%_kG6wyZ~_AAcchm!E^uwheVKV zjdb~(U0nI_uK*0_0VCi5j3m$SFdb`abMRlw(lCIwo=>H=Wc_!(|5)g~yu&>KfUHsM z;T7%~jN%&rAWfcOBf_?D4vLw`sDdc2+`_)UaMKp{_=S76dF*ZVP&@$u zfto}j`vO3mN9lY~p1!CY8fpM=3?W|rAt);!ig$VXk-Pxla7TG0gM&l2a2AS1J^o3K z@BfnN@mD5^bLzv)$B5xTa&>COHTCiYu4Lh+Daq_M+qJR2G4po3yA6t4${nr-o;c7oYL zJS?qH%z$D+&u~50Uov6kI6r%*-#q4C-ueb8MxzLJCWqSo;r9!Vvin1ii}Ta7{KFqW zGTrhw4T>{J1HVK}K8S?m+RSun24W-(&TL1X=!`3q0jRHnjbXSG~ix=es>t zgrB|fFZl>v1R4OpWFlNX{)Q&MbU`?8%X~TXqmcv5$9%!*7{L zh8IcS?3YcD5?}~OfDF8W2M7ThfDf1hJ)r-)J_{FBK4(#PX(kAK(65KL`L{mmg;D?2B<{-^&xW{fky z>En!XhBz@K4Y`hVA@`6+NHfw1_>fkl3F$%ZZsC6!h4kAhO$n9St|52mJ zzZ}1*G~zT$z;>HI5Pj$WcLc|6t^NN^N+eo6P)PykJF~xY{9|ch)VfL-U5qAbXVk_R z77PcF!YHB^*THC^K2SobdjHz%cPj;awrv@;J(oc64#mL{-Z2qdfvy)C7EAW`^^4%s zL3c}UNj{ShPiaX$d09Dmu(kL8TH4=ixA#M0MDfdf5G$;cVpc-5T*FYm^2KT@N@EG)iXW%860MpB=n*sk zy@1|8bI@046-Hocmx+%XreX`RRoI)@PINs_ zW0!C^91Bhar-DYi3oZb60GEX;#nt25a09qW+#(*2XU9w6HStz>5H;7SM~BoPV;7YQwdLBb4Sje?Fsh(evhio%N`mLi*?lHxW+AH_7q z8YMlY2&E>aJ!JspVag)PI?7JUSCrqVsHk>OsZrTb`BNoQ6;WNGdPwz#YK5ACTAW&s z+KoDr`Z)Ck>Ne^Z)Zd6iqA*dL=t_(r9w*iiJBZ`NA2f_K(ljPCUNnbjifI~X25COg zQqT&~>d@|`jiW83y-wRlJ5NVJCrqbDN1{uhE2g_eH$t~a&qyyvZ$%$WpGAL>{t^8w z1D-*c!GOVwA%&rWp@U(P5n&Wy)MNByOlGWP>|~r~!ZC?3nJ@(~Winl6>SbDBW?)ui zc4UrWE@r;VJi&sn2(y^51hHhZTw@t#S!LyB)nWB!O=qoR9b{c*<7Crj^JdFnt7m)0 zw#v@OZonSMp3C0IKE{FJ5a+Prh~_xM(aAB-$;7F_>B*VFd5v?F3+58%vgJC!Rl(K6 zwaCrGZNweMeTut-d!C1dN1G>rC!eQ~PqTw4;8M}-@OXGPdVOhn>DYDHd&5=FH{_luT`4vFE!RK)_tO2zucVR0pKKk;Jm zJ_%StS;AkURAN99E2$X2McQ0CNxD(`lZ>E@t4yv;m&{LD zCD|a^O4*lk405J&NpiR4zRHWsd&(Ef4=E583>6M3G%0*hlu-0mJfrwriBZWy>8Mh> z(uT5%GFiD!c}_)8#a*RDWki)p)mk-6^`RP8O;_!ZTC>`Uy0UtN`Ze{>8d4g88W%NY zcZ%%v-dVYGQgeqUN%NfM#4f&F?z_(Jn$Y6cB59Rtz10@b_R_A>p3xE03DCKu^GR1$ zm#lkJcUezeFJ7-rAJI3`&(QBRU@&knIBhU)xWmxbu-0(FNXh7cQJXQ=*vvTBc*KO; z#M7k4WWiL$G~V>S8MT?6*=e)4=HljL^V=4%g{eiJ#Vbo8%Mi;(D_~_}m1i|(En>aj z`i>3G#@eRX=AEsa?LpfvJ4U;Gb{FlI>~-z4?O!^GIz&3OJJLG3Io3EXJLx-}aGG$I zagKL>?84#V@6zOocXf2Fa$VeQu)ARQw40J!s@w1$kv%bc9_{7W8?^WCKH7bxeb?Qw z?oRF(-PcK0q;k@thlxjt$AYJx=Sj~GUfN#yUUS}>-Y2|gd^CMd_{{oh`sVr0`DytT z`px_6`JeXx5?~xq7O)&>6?h?VGsr2ZK9~^f9(*f=AtW&5ekf09Oz1$EL|AIr#D4Yt z1^XAsX5^}HD11+NQv?%w$?b_0jXV9I3$hH>W) zU=Da5Xg|n*@bJMm@jCJ65}*W+g!V%Mhf)qrCmJPIAErDUc=$<@Y|@FO@5xTdw^O)N z5>qCR7#*oirAZ~HjvUoET9$@M^H1wdS4cmV{xicXqdQYJvoLc#%QLI{nB1|VW1Gi) zj`wCOXP?Qz<%Hx6=W69vpP)YxcVaTnBCjEzFFzyyd%?bfM}>-oWhW_5MxLA~GB0X6 zC3q_5)cR?^)6a@^i|b3cO43V~OT9`5&uE{iE8{N9EL%P6fA;w~qjL@A!sUe(xQeKX zca@HnUFTKL*Hm#-Wmaum2)Qs(ZCic6My00aBG<+2TDUf{cJ7kfrT)wMmz(OO>dw`( z)Ms7+S0b;xzv_N<_?p?Z_Uo$G>uw0$D7ndaGot}&h;3MC^lO}Ga%md4WqPaqw#Mz7 zcckxJXy$J&zRP?!yM?kPrDd}Q?P;e60Mqp+~9b<`bnSH+z+P8~RlGn)=oHZx8GoXc^QRy!TZ9 z>4PDYp~ufGpA8H<3_l-n8<}|S^?de4(2FmlQKM@w6JKFpWsK2{6^?U_m%kQ!T|c2b zarce>o1V9JZ(mM&PJWmQpIVL&bXYJ2D3(gDEU&voJzh-@7|90WK;`jDN>&5Y<;H9lW5XV#+E z@#{q!VjH)An*DsW8N9g}Mk158uL1~KVILm=zFt7{78d{*`_VfE&9B_(cR|q02aG-) zA!+CXd>={1*kQGCB6to08wD?=CY2?1ATfvL7VSK}6oWTo1=A#pIBP6hD+ev76IUho zH{P9mDf~|a=ml+sjtM^&1!4-~J`yJ-yQP+8L}hK{66I?Zo+-h~aw>aNGt?T?-|Qr6 zs_t^vO4q)s^Gc7PuWaCHm}AsrJZ=h^37Q*O1XyNU-LQUc^TUqCUctf2G1%#tbDhg$ z*EhQt+?MyO?E~(VBz6xePaQ8??|nYrz9c_8|D6GRfl%N?P;2nnkmOLmFzfxQWWI1h z_~(e>$d;(;=)9PO*q}I<111MG<5d!r52+>U95zdGO7>2PIC3O4|7c~}_4Kxkp3LE_ z*T-g#f6V@xvzWVdVli(qf3*;ElDSCol+kI=;zK1xrFCao%O0N{IyYKAQ88Efy$ZR& zTrFOsd(o{n`cnSoYjp$lU#`+$Q@Cz-Bm8DgLrvrDrhB(KZnxdJ(|q%8eM?PiMO*2; zg7%D#gZBeE?H(w1v30FHoOsmN{orv&&%Gzjz4d)X{ow<8gT%q7Pm70qo+%Auhet-L zpGUpW9>tAzq4A~kYGy2VTw{Fl^|1+!i4Ske-r7zgll4;$Q!CT??Q4=wD@mPsG$*WR5(zCJ{Iez(_3a*L~N}0-)Dz{Xh zsJ&JHz7wa(xl2yVLOVz&Tlb1yzy3!pBh5t~%oG`kG@ zEQbuo6sK5cKNovf{oPt_hI?H1M(-e?Fi;@ODs5 zaB)a>Xi^w?zZ+RQoHu+e;#uV7sMKhW7=u{ZIKczl2ifD96X*`%4lO0VJ=~piHMt-q z`pBMC)1&%n=INx2!2t5kUsMcKK0M!2)qJ75`r}3Z+C7&_F2AT}x?*`X4c)y~Zb~#*MAXf`_9Zv;sF<&8n{*G*cRKX}AlCXgYpU4l<9_ckrId3JvG8V*ExE!KIzx|y>4g2*UrTmD&+M8AtBCKBMNvDW#-kHsxMS|dy2r7^jUK2ym>ln%Aeyjr zs3S4&uy2xnvUm#H5vo*>`u*r++LQG9jI2yjmf|t|vC-pKvNLi5bB#_goOqD8J8wQe zu0W?y_hi~B!s(3SsZ#bc@?~mg)y^rE%T$O~@}Fm~qQ5{{O;JOOMv~?w_sd7?F4sT3 zvVKkUy6cS-HwPM-np|#`+#YGh-W6*xYV~M4bni@iOULW`n-BQA^d9;?%Id!Qc(`Yy zSFCS$|H*;zr}9I`o_!jzdwy$_|7F3L%xjuA(NmeTu^%nJF0I&X`Ty59vaNzs1|Sd3 zGaZkibq;{sMl{d70{|-}0MVbpQ%H^7s6J(8QB~=A0~m7l;8xG}AN( zu4uj;1Jcoq@)BqPgJ^cV4lzJtkT#khhC|2De6SOmgdvz8)`h*`qiB{l2(Kanhy@ad zR3jr80!AGZjH$r9z%pRX(2T4D2jR4FiMVz=7H^8r!9OSP6M_i0DDV_c6x9@~lopid zD3_@$sj8_l)E?9wL?L22@e_?bO*5M51rXP3)HJvm9xha-5S~W!xS-5m=srguI2LkIU8lA9wGBkQB3Vy?#RJkTE>dWb7= zI4M8H{K(SL{B+5T&Mfca%-OGVpX7}dY!>mIRx2?-v+rzd`Ps_R3sN;_FUi(TTs?PV zZzI*My5>Et679U5h7ZeosQW|*mWFaiL|+WNI`jJE+rfA4b1NSiK41C@zRNDDEpM)5 ztTwF;t>4+G_!+jTvb}#m+us}*04ERz@<0n%05=c}5$U~Dm&m`*H&)x;ja-odWpv~bC|M|e8C z3;sO*D?x{lM|ek}L6Ju>Pictye4Waf>IRw#lc|S@YQ(cNR5UR(Gqf(Wy>y0jZS=e7 zTN(5ix*6>l$I#4ll{uS5oaHg=el`}i26h|vcO0>tM9xYsZLTTqTpm+i8s0I!>-@)e z_zUO?vI%|=x+|O`;v=dnCM3=vL6F2s5u|Bk7-Z??2y!d(vkD`M-AebA@2PaFzEu0F zL9p=8O%eOmeU*M4M zc-E=a`HQR4?nJjIdwKRnxc7Moc=~u1c;EEt^_}ut3t$OU2(k~33OOCx7WSUZ8g3X7 z6?q|QG=?tLA};&D^LXKe_(PM2eUs@@x>F<4aOp=gF{UxsI%Pma#M`u-YvLpjMbEjVL5S2(ZoIpbT^a?nceTFQFI#`m8q+x~~q)lUz2 zfE3sTY=94l2Zi7Y=mGB`3P=L7fXGl0)CRqWSzvuQ04U7gR4s#o` zj@7|tU`KFbxWl+H{4RVsfj|f)3{j|2lv2W!0hG_E^r`Mpt5M%0S`**VB+>HIw$ZuK zEz##NNHIKN3}GTNRWs`{zha4H4q8s8g5`vQ2Qtr};GNrOja!=*IDRL;8C?~0Os8OrCXF{(UGxU2Rxc%r`(OaoqC9a08 z4z4M!6|H?)H(4)VU)!+Xxcn3O>HPE3Cba3Wd2!4C;XVrT+s6k)tqTBld~@@gFaY=y z0Bq!LZmyMYZf;bd$;K-H+Jb)l@3MW?&;d~LdJ8=)sfqd5|3Cf*hMK?V*1e)V00009 za7bBm000XU000XU0RWnu7ytkS*-1n}R9J<*nA?uyHVlS8%Ca3h$C;h&c7gW&-=(*G zgrYzTbhneu(7cw4eOTl}@i%5kcf~QbbVI%Ry!QUBD3b^u0$_DU4881$9+mjBq&i^v+{s zYpOZ&=eDL>9T;c>vm(Wzkl&vyx_3Qk9l3j|aZ(&KM(Mf9}jgcvFM{ z8-tJz0XitjmM}tyhEtl_@^II(TGY&{5@RlwsdlN%_8Hn?q z`&Gl|A6B%pa_s2f)!n+m82J6q9j9}TEd;#|ORhX~+@?tCc)#SI3eGDJn>jyyT48N5 z0+91gfV)+TEed}7W6R;x1weYO^wyrq!D{{_`|G`D(Nui?xWw85RYg@9lm8MSRb@G^ z`SD?iF(%x!35O@adGdK*C3M8Y<=F8G_}QfkK{BUa3p3VX2- zi3nwpg7_kkYblISSu^?U>ed5bQsiC;p(+>=j4|o|w`Jf@O5;Sa$v&gXlqSv2$W?97 zDTaTiL<+p7>QW(wYJRhEs9auO=gO4i>JmRD>RJSAudjUkaxtkm^l~GmA+Dsox7k z21V!2;eAH@7wD7HM79vYrQ0n+@090r|86kROy7G>=WdK3kGu>eWe}5cIE7e4dekdl zw@13pjfbx{WPd!f+n+JUjLQGG5t8ovC^8ZUo*2>0i{q)|)V&>@FusN^y+3qxolnIq z^}eZIAVbkhoH3(kL`MoEbiLm zx$|^5vp<|EOg6&aVjjtKOU(dBpNc}*?Vs80pI_V_XB_X9zrUU6dPia8^5!v>o{K5W zRQJpeU3z9%@b4gRK71t#yy9pSJDPXpW`8Prkt4-0O1#!EMFcZMXynl~=qsC}c94um z-efgLRcY#ySrumZX#$7qV2m(tOG;~QBRNrWMqu(yF}D&M+Q4oF?$-@%Tk?{`_c4IY zvSHp;aG9s%pH`89$3INV$hY^^!B{IBCzCPC|r7f7xN;>E1U6_I6-^kX0 zOv&e&j8H0psLpu=Vcu4J+_bc_>J37ca l%5XU^6|Cd|?`v-#SNw`M^J`4Z=002ovPDHLkV1i5TjzRzc literal 0 HcmV?d00001 diff --git a/modules/ncenterlite/skins/default/ncenterlite.black.css b/modules/ncenterlite/skins/default/ncenterlite.black.css new file mode 100644 index 000000000..6f2596f91 --- /dev/null +++ b/modules/ncenterlite/skins/default/ncenterlite.black.css @@ -0,0 +1,15 @@ +#nc_container { border-bottom-color:#555; background-color:#333; color:#B0B0B0; opacity:0.95; } +#nc_container a { color:#B0B0B0; } +#nc_container a:hover { color:#B0B0B0; } +#nc_container strong { color:#F3F3F3; } + +#nc_container .list { background-color:#333; filter:alpha(opacity=95); opacity:0.95; -moz-opacity:0.95; } +#nc_container .list li { border-color:#555; } +#nc_container .list li a:hover { background-color:#555; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list .more { background: #555; } +#nc_container a.readall { display:none; color:#555; color:#D83722;} + +#nc_container a.notify { color:white; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } diff --git a/modules/ncenterlite/skins/default/ncenterlite.blacknoprofile.css b/modules/ncenterlite/skins/default/ncenterlite.blacknoprofile.css new file mode 100644 index 000000000..88c94a4a4 --- /dev/null +++ b/modules/ncenterlite/skins/default/ncenterlite.blacknoprofile.css @@ -0,0 +1,17 @@ +#nc_container { border-bottom-color:#555; background-color:#333; color:#B0B0B0; opacity:0.95; } +#nc_container a { color:#B0B0B0; } +#nc_container a:hover { color:#B0B0B0; } +#nc_container strong { color:#F3F3F3; } + +#nc_container .list { background-color:#333; filter:alpha(opacity=95); opacity:0.95; -moz-opacity:0.95; } +#nc_container .list li { border-color:#555; } +#nc_container .list li a:hover { background-color:#555; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list .more { background: #555; } +#nc_container a.readall { display:none; color:#555; color:#D83722;} + +#nc_container a.notify { color:white; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } + +#nc_container img.nc_profile_img { display:none;} diff --git a/modules/ncenterlite/skins/default/ncenterlite.css b/modules/ncenterlite/skins/default/ncenterlite.css new file mode 100644 index 000000000..60df8e548 --- /dev/null +++ b/modules/ncenterlite/skins/default/ncenterlite.css @@ -0,0 +1,185 @@ +#nc_container { + z-index:99; + position:fixed; + top:0; + left:0; + margin:0; + padding:0; + width:100%; + height:30px; + border-bottom:1px solid; + font-size:12px; + line-height:15px; +} + +.ncenterlite_block { + height:28px; +} + +#nc_container a { + padding:0; + font-size:12px; + text-decoration:none; +} + +#nc_container ul.nc_memu { + display:block; + margin:0; + padding:4px; + list-style:none; + line-height:20px; +} + +#nc_container ul:after { + content:""; + display:block; + clear:both; +} + +#nc_container ul.nc_memu li { + display:inline-block; + padding:0 5px; + height:20px; + width:auto; + background:transparent!important; +} + +#nc_container ul.nc_memu li.fLeft { + float:left; +} + +#nc_container ul.nc_memu li.fRight { + float:right; +} + +#nc_container ul.nc_memu li a.notify { + display:block; + float:left; +} + +#nc_container a.close { + display:block; +} + +#nc_container .nc_profile img { + width:20px; + height:20px; + vertical-align:top; +} + +#nc_container a.notify .num { + padding:1px 2px; + border:0; + border-radius:3px; + -webkit-border-radius:3px; + -moz-border-radius:3px; + font-size:12px; + font-weight:700; + font-family:Gulim,"lucida grande",tahoma,verdana,arial,sans-serif; +} + +#nc_container .list { + display:none; + position:absolute; + top:30px; + left:0; + max-width:330px; + width:330px; + box-sizing:border-box; + -moz-box-sizing:border-box; + -webkit-box-sizing:border-box; + -o-box-sizing:border-box; +} + +#nc_container a.readall { + display:none; + float:left; + margin:0 4px; + font-size:11px; + font-weight:700; +} + +#nc_container .listscroll { + overflow-y:auto; + overflow-x:hidden; +} + +#nc_container .list ul { + overflow:hidden; + margin:-1px 0; + padding:0; + list-style:none; +} + +#nc_container .list li { + margin:-1px 0; + border:1px solid #555; + border-width:1px 0; +} + +#nc_container .list li img { + float:left; + margin-right:5px; + width:45px; + height:45px; + border:0; +} + +#nc_container .list li a { + display:block; + overflow:hidden; + padding:10px; + font-size:12px; +} + +#nc_container .list span.ago { + display:block; + font-size:10px; +} + +#nc_container .list .more { + display:block; + padding:10px; + text-align:center; +} + +#nc_container ~ .navbar.navbar-fixed-top { + top:28px; +} + +#nc_container a:hover,#nc_container .list li a:hover,#nc_container .list .more:hover { + text-decoration:none; +} + +@media only screen and max-device-width 480px { + #nc_container { + position:relative; + height:auto; + } + + #nc_container .list { + top:2px; + position:relative; + } + + #nc_container .list ul { + display:block; + position:relative; + } + + #nc_container ul.nc_memu:after { + content:""; + display:block; + visibility:hidden; + height:0; + clear:both; + } + + #nc_container .listscroll { + overflow:visible; + } + + .ncenterlite_block { + display:none; + } +} diff --git a/modules/ncenterlite/skins/default/ncenterlite.gray.css b/modules/ncenterlite/skins/default/ncenterlite.gray.css new file mode 100644 index 000000000..4d167ea21 --- /dev/null +++ b/modules/ncenterlite/skins/default/ncenterlite.gray.css @@ -0,0 +1,16 @@ +#nc_container { border-bottom-color:#C0C0C0; background-color:#efefef; color:#666; opacity:0.95; } +#nc_container a { color:#666; } +#nc_container a:hover { color:#666; } +#nc_container strong { color:#666; } +#nc_container a.notify strong { color:#FC2772; } +#nc_container .list { background-color:#efefef; filter:alpha(opacity=95); opacity:0.95; -moz-opacity:0.95; } +#nc_container .list li { border-color:#ddd; } +#nc_container .list li a:hover { background-color: #ddd; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list a.notify sup.num { background-color:#D83722; color:white; } +#nc_container .list .more { background: #ddd; } +#nc_container a.readall { display:none; color:#555; font-size:10px; color:#D83722;} + +#nc_container a.notify { color:#666; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } diff --git a/modules/ncenterlite/skins/default/ncenterlite.graynoprofile.css b/modules/ncenterlite/skins/default/ncenterlite.graynoprofile.css new file mode 100644 index 000000000..d8704070b --- /dev/null +++ b/modules/ncenterlite/skins/default/ncenterlite.graynoprofile.css @@ -0,0 +1,18 @@ +#nc_container { border-bottom-color:#C0C0C0; background-color:#efefef; color:#666; opacity:0.95; } +#nc_container a { color:#666; } +#nc_container a:hover { color:#666; } +#nc_container strong { color:#666; } +#nc_container a.notify strong { color:#FC2772; } +#nc_container .list { background-color:#efefef; filter:alpha(opacity=95); opacity:0.95; -moz-opacity:0.95; } +#nc_container .list li { border-color:#ddd; } +#nc_container .list li a:hover { background-color: #ddd; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list a.notify sup.num { background-color:#D83722; color:white; } +#nc_container .list .more { background: #ddd; } +#nc_container a.readall { display:none; color:#555; font-size:10px; color:#D83722;} + +#nc_container a.notify { color:#666; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } + +#nc_container img.nc_profile_img { display:none;} diff --git a/modules/ncenterlite/skins/default/ncenterlite.html b/modules/ncenterlite/skins/default/ncenterlite.html new file mode 100644 index 000000000..39d6ebe10 --- /dev/null +++ b/modules/ncenterlite/skins/default/ncenterlite.html @@ -0,0 +1,125 @@ + +
+ + diff --git a/modules/ncenterlite/skins/default/ncenterlite.mobile.css b/modules/ncenterlite/skins/default/ncenterlite.mobile.css new file mode 100644 index 000000000..ef3125177 --- /dev/null +++ b/modules/ncenterlite/skins/default/ncenterlite.mobile.css @@ -0,0 +1,12 @@ +#nc_container { height:auto; position:relative; } +#nc_container .list { top:2px; position:relative; } +#nc_container .list ul { display:block; position:relative; } +#nc_container ul.nc_memu:after { + content: ""; + display: block; + height: 0; + clear: both; + visibility: hidden; +} +#nc_container .listscroll { overflow: visible; } +.ncenterlite_block { display:none; } diff --git a/modules/ncenterlite/skins/default/ncenterlite.white.css b/modules/ncenterlite/skins/default/ncenterlite.white.css new file mode 100644 index 000000000..c3fd4444a --- /dev/null +++ b/modules/ncenterlite/skins/default/ncenterlite.white.css @@ -0,0 +1,16 @@ +#nc_container { border:1px solid #efefef; background-color:#fff; color:#000; opacity:0.95; } +#nc_container a { color:#666; } +#nc_container a:hover { color:#666; } +#nc_container strong { color:#000; } +#nc_container a.notify strong { color:#F60; } +#nc_container .list { border:1px solid #efefef; background-color:#fff; filter:alpha(opacity=95); opacity:0.95; -moz-opacity:0.95; } +#nc_container .list li { border-color:#eee; } +#nc_container .list li a:hover { background-color: #eee; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list a.notify .num { background-color:#D83722; color:white; } +#nc_container .list .more { background: #eee; } +#nc_container a.readall { display:none; color:#555; font-size:10px; color:#D83722;} + +#nc_container a.notify { color:#666; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } diff --git a/modules/ncenterlite/skins/default/ncenterlite.whitenoprofile.css b/modules/ncenterlite/skins/default/ncenterlite.whitenoprofile.css new file mode 100644 index 000000000..69342bd1d --- /dev/null +++ b/modules/ncenterlite/skins/default/ncenterlite.whitenoprofile.css @@ -0,0 +1,18 @@ +#nc_container { border:1px solid #efefef; background-color:#fff; color:#000; opacity:0.95; } +#nc_container a { color:#666; } +#nc_container a:hover { color:#666; } +#nc_container strong { color:#000; } +#nc_container a.notify strong { color:#F60; } +#nc_container .list { border:1px solid #efefef; background-color:#fff; filter:alpha(opacity=97); opacity:0.97; -moz-opacity:0.97; } +#nc_container .list li { border-color:#eee; } +#nc_container .list li a:hover { background-color: #eee; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list a.notify .num { background-color:#D83722; color:white; } +#nc_container .list .more { background: #eee; } +#nc_container a.readall { display:none; color:#555; font-size:10px; color:#D83722;} + +#nc_container a.notify { color:#666; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } + +#nc_container img.nc_profile_img { display:none;} diff --git a/modules/ncenterlite/skins/default/skin.xml b/modules/ncenterlite/skins/default/skin.xml new file mode 100644 index 000000000..78ac42418 --- /dev/null +++ b/modules/ncenterlite/skins/default/skin.xml @@ -0,0 +1,40 @@ + + + XE 알림센터 Lite 기본스킨 + XE Notification Center Lite Default Skin + 1.1.1 + 2013-03-21 + + + XE Public + XE Public + + + + + 검은색 + Black + + + 회색 + Gray + + + 흰색 + White + + + + 검은색(no profile) + Black (no profile) + + + 회색(no profile) + Gray (no profile) + + + 흰색(no profile) + White (no profile) + + + diff --git a/modules/ncenterlite/skins/default/userconfig.html b/modules/ncenterlite/skins/default/userconfig.html new file mode 100644 index 000000000..fffbfeaeb --- /dev/null +++ b/modules/ncenterlite/skins/default/userconfig.html @@ -0,0 +1,68 @@ + + +
+ +
+

{$XE_VALIDATOR_MESSAGE}

+
+ + +
+ + + + +
+

{@$user_str = $member_info->nick_name} + {@$user_str = $logged_info->nick_name} + {@$title_str = Context::getLang('ncenterlite_userconfig_title')} + {sprintf($title_str, $user_str)} +

+ +

{$lang->ncenterlite_userconfig_about} ({$lang->ncenterlite_userconfig_about_warning})

+
+ +
+ + +

{$lang->ncenterlite_comment_noti_about}

+
+
+
+ +
+ + +

{$lang->ncenterlite_mention_noti_about}

+
+
+
+ +
+ + +

{$lang->ncenterlite_message_noti_about}

+
+
+ +
+
+
+ +
+
+
+
+ \ No newline at end of file diff --git a/modules/ncenterlite/skins/default_bottom/NotifyList.html b/modules/ncenterlite/skins/default_bottom/NotifyList.html new file mode 100644 index 000000000..dae56b337 --- /dev/null +++ b/modules/ncenterlite/skins/default_bottom/NotifyList.html @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + {@ + $oMemberModel = getModel('member'); + $member_info = $oMemberModel->getMemberInfoByMemberSrl($val->member_srl); + } + + + + + + + + + +
{$lang->ncenterlite_sender}{$lang->ncenterlite_addressee}{$lang->ncenterlite_noti_contents}{$lang->ncenterlite_read}{$lang->date}
{$val->target_nick_name}{$member_info->nick_name} {$lang->ncenterlite_no_target}{$val->text} + {$lang->ncenterlite_read_y} + {$lang->ncenterlite_read_n} + + {zdate($val->regdate,"Y-m-d")} +
+ {zdate($val->regdate,"H:i:s")} +
+ + + + \ No newline at end of file diff --git a/modules/ncenterlite/skins/default_bottom/img/p.png b/modules/ncenterlite/skins/default_bottom/img/p.png new file mode 100644 index 0000000000000000000000000000000000000000..44e3d8d1203e991132d400e26b13aa3e469862d9 GIT binary patch literal 7463 zcmV+?9oXWDP)5000;CX+uL$Nkc;* zP;zf(X>4Tx09b{USqC_l(c3@I^SsM=?Y(91y_3E79;v+crq@;^B&(2-LJEl#O;kc8 zC0U75q!3D_fvkKFjsO4qkL$a>b6w{=zxRIbbD#U%_kG6wyZ~_AAcchm!E^uwheVKV zjdb~(U0nI_uK*0_0VCi5j3m$SFdb`abMRlw(lCIwo=>H=Wc_!(|5)g~yu&>KfUHsM z;T7%~jN%&rAWfcOBf_?D4vLw`sDdc2+`_)UaMKp{_=S76dF*ZVP&@$u zfto}j`vO3mN9lY~p1!CY8fpM=3?W|rAt);!ig$VXk-Pxla7TG0gM&l2a2AS1J^o3K z@BfnN@mD5^bLzv)$B5xTa&>COHTCiYu4Lh+Daq_M+qJR2G4po3yA6t4${nr-o;c7oYL zJS?qH%z$D+&u~50Uov6kI6r%*-#q4C-ueb8MxzLJCWqSo;r9!Vvin1ii}Ta7{KFqW zGTrhw4T>{J1HVK}K8S?m+RSun24W-(&TL1X=!`3q0jRHnjbXSG~ix=es>t zgrB|fFZl>v1R4OpWFlNX{)Q&MbU`?8%X~TXqmcv5$9%!*7{L zh8IcS?3YcD5?}~OfDF8W2M7ThfDf1hJ)r-)J_{FBK4(#PX(kAK(65KL`L{mmg;D?2B<{-^&xW{fky z>En!XhBz@K4Y`hVA@`6+NHfw1_>fkl3F$%ZZsC6!h4kAhO$n9St|52mJ zzZ}1*G~zT$z;>HI5Pj$WcLc|6t^NN^N+eo6P)PykJF~xY{9|ch)VfL-U5qAbXVk_R z77PcF!YHB^*THC^K2SobdjHz%cPj;awrv@;J(oc64#mL{-Z2qdfvy)C7EAW`^^4%s zL3c}UNj{ShPiaX$d09Dmu(kL8TH4=ixA#M0MDfdf5G$;cVpc-5T*FYm^2KT@N@EG)iXW%860MpB=n*sk zy@1|8bI@046-Hocmx+%XreX`RRoI)@PINs_ zW0!C^91Bhar-DYi3oZb60GEX;#nt25a09qW+#(*2XU9w6HStz>5H;7SM~BoPV;7YQwdLBb4Sje?Fsh(evhio%N`mLi*?lHxW+AH_7q z8YMlY2&E>aJ!JspVag)PI?7JUSCrqVsHk>OsZrTb`BNoQ6;WNGdPwz#YK5ACTAW&s z+KoDr`Z)Ck>Ne^Z)Zd6iqA*dL=t_(r9w*iiJBZ`NA2f_K(ljPCUNnbjifI~X25COg zQqT&~>d@|`jiW83y-wRlJ5NVJCrqbDN1{uhE2g_eH$t~a&qyyvZ$%$WpGAL>{t^8w z1D-*c!GOVwA%&rWp@U(P5n&Wy)MNByOlGWP>|~r~!ZC?3nJ@(~Winl6>SbDBW?)ui zc4UrWE@r;VJi&sn2(y^51hHhZTw@t#S!LyB)nWB!O=qoR9b{c*<7Crj^JdFnt7m)0 zw#v@OZonSMp3C0IKE{FJ5a+Prh~_xM(aAB-$;7F_>B*VFd5v?F3+58%vgJC!Rl(K6 zwaCrGZNweMeTut-d!C1dN1G>rC!eQ~PqTw4;8M}-@OXGPdVOhn>DYDHd&5=FH{_luT`4vFE!RK)_tO2zucVR0pKKk;Jm zJ_%StS;AkURAN99E2$X2McQ0CNxD(`lZ>E@t4yv;m&{LD zCD|a^O4*lk405J&NpiR4zRHWsd&(Ef4=E583>6M3G%0*hlu-0mJfrwriBZWy>8Mh> z(uT5%GFiD!c}_)8#a*RDWki)p)mk-6^`RP8O;_!ZTC>`Uy0UtN`Ze{>8d4g88W%NY zcZ%%v-dVYGQgeqUN%NfM#4f&F?z_(Jn$Y6cB59Rtz10@b_R_A>p3xE03DCKu^GR1$ zm#lkJcUezeFJ7-rAJI3`&(QBRU@&knIBhU)xWmxbu-0(FNXh7cQJXQ=*vvTBc*KO; z#M7k4WWiL$G~V>S8MT?6*=e)4=HljL^V=4%g{eiJ#Vbo8%Mi;(D_~_}m1i|(En>aj z`i>3G#@eRX=AEsa?LpfvJ4U;Gb{FlI>~-z4?O!^GIz&3OJJLG3Io3EXJLx-}aGG$I zagKL>?84#V@6zOocXf2Fa$VeQu)ARQw40J!s@w1$kv%bc9_{7W8?^WCKH7bxeb?Qw z?oRF(-PcK0q;k@thlxjt$AYJx=Sj~GUfN#yUUS}>-Y2|gd^CMd_{{oh`sVr0`DytT z`px_6`JeXx5?~xq7O)&>6?h?VGsr2ZK9~^f9(*f=AtW&5ekf09Oz1$EL|AIr#D4Yt z1^XAsX5^}HD11+NQv?%w$?b_0jXV9I3$hH>W) zU=Da5Xg|n*@bJMm@jCJ65}*W+g!V%Mhf)qrCmJPIAErDUc=$<@Y|@FO@5xTdw^O)N z5>qCR7#*oirAZ~HjvUoET9$@M^H1wdS4cmV{xicXqdQYJvoLc#%QLI{nB1|VW1Gi) zj`wCOXP?Qz<%Hx6=W69vpP)YxcVaTnBCjEzFFzyyd%?bfM}>-oWhW_5MxLA~GB0X6 zC3q_5)cR?^)6a@^i|b3cO43V~OT9`5&uE{iE8{N9EL%P6fA;w~qjL@A!sUe(xQeKX zca@HnUFTKL*Hm#-Wmaum2)Qs(ZCic6My00aBG<+2TDUf{cJ7kfrT)wMmz(OO>dw`( z)Ms7+S0b;xzv_N<_?p?Z_Uo$G>uw0$D7ndaGot}&h;3MC^lO}Ga%md4WqPaqw#Mz7 zcckxJXy$J&zRP?!yM?kPrDd}Q?P;e60Mqp+~9b<`bnSH+z+P8~RlGn)=oHZx8GoXc^QRy!TZ9 z>4PDYp~ufGpA8H<3_l-n8<}|S^?de4(2FmlQKM@w6JKFpWsK2{6^?U_m%kQ!T|c2b zarce>o1V9JZ(mM&PJWmQpIVL&bXYJ2D3(gDEU&voJzh-@7|90WK;`jDN>&5Y<;H9lW5XV#+E z@#{q!VjH)An*DsW8N9g}Mk158uL1~KVILm=zFt7{78d{*`_VfE&9B_(cR|q02aG-) zA!+CXd>={1*kQGCB6to08wD?=CY2?1ATfvL7VSK}6oWTo1=A#pIBP6hD+ev76IUho zH{P9mDf~|a=ml+sjtM^&1!4-~J`yJ-yQP+8L}hK{66I?Zo+-h~aw>aNGt?T?-|Qr6 zs_t^vO4q)s^Gc7PuWaCHm}AsrJZ=h^37Q*O1XyNU-LQUc^TUqCUctf2G1%#tbDhg$ z*EhQt+?MyO?E~(VBz6xePaQ8??|nYrz9c_8|D6GRfl%N?P;2nnkmOLmFzfxQWWI1h z_~(e>$d;(;=)9PO*q}I<111MG<5d!r52+>U95zdGO7>2PIC3O4|7c~}_4Kxkp3LE_ z*T-g#f6V@xvzWVdVli(qf3*;ElDSCol+kI=;zK1xrFCao%O0N{IyYKAQ88Efy$ZR& zTrFOsd(o{n`cnSoYjp$lU#`+$Q@Cz-Bm8DgLrvrDrhB(KZnxdJ(|q%8eM?PiMO*2; zg7%D#gZBeE?H(w1v30FHoOsmN{orv&&%Gzjz4d)X{ow<8gT%q7Pm70qo+%Auhet-L zpGUpW9>tAzq4A~kYGy2VTw{Fl^|1+!i4Ske-r7zgll4;$Q!CT??Q4=wD@mPsG$*WR5(zCJ{Iez(_3a*L~N}0-)Dz{Xh zsJ&JHz7wa(xl2yVLOVz&Tlb1yzy3!pBh5t~%oG`kG@ zEQbuo6sK5cKNovf{oPt_hI?H1M(-e?Fi;@ODs5 zaB)a>Xi^w?zZ+RQoHu+e;#uV7sMKhW7=u{ZIKczl2ifD96X*`%4lO0VJ=~piHMt-q z`pBMC)1&%n=INx2!2t5kUsMcKK0M!2)qJ75`r}3Z+C7&_F2AT}x?*`X4c)y~Zb~#*MAXf`_9Zv;sF<&8n{*G*cRKX}AlCXgYpU4l<9_ckrId3JvG8V*ExE!KIzx|y>4g2*UrTmD&+M8AtBCKBMNvDW#-kHsxMS|dy2r7^jUK2ym>ln%Aeyjr zs3S4&uy2xnvUm#H5vo*>`u*r++LQG9jI2yjmf|t|vC-pKvNLi5bB#_goOqD8J8wQe zu0W?y_hi~B!s(3SsZ#bc@?~mg)y^rE%T$O~@}Fm~qQ5{{O;JOOMv~?w_sd7?F4sT3 zvVKkUy6cS-HwPM-np|#`+#YGh-W6*xYV~M4bni@iOULW`n-BQA^d9;?%Id!Qc(`Yy zSFCS$|H*;zr}9I`o_!jzdwy$_|7F3L%xjuA(NmeTu^%nJF0I&X`Ty59vaNzs1|Sd3 zGaZkibq;{sMl{d70{|-}0MVbpQ%H^7s6J(8QB~=A0~m7l;8xG}AN( zu4uj;1Jcoq@)BqPgJ^cV4lzJtkT#khhC|2De6SOmgdvz8)`h*`qiB{l2(Kanhy@ad zR3jr80!AGZjH$r9z%pRX(2T4D2jR4FiMVz=7H^8r!9OSP6M_i0DDV_c6x9@~lopid zD3_@$sj8_l)E?9wL?L22@e_?bO*5M51rXP3)HJvm9xha-5S~W!xS-5m=srguI2LkIU8lA9wGBkQB3Vy?#RJkTE>dWb7= zI4M8H{K(SL{B+5T&Mfca%-OGVpX7}dY!>mIRx2?-v+rzd`Ps_R3sN;_FUi(TTs?PV zZzI*My5>Et679U5h7ZeosQW|*mWFaiL|+WNI`jJE+rfA4b1NSiK41C@zRNDDEpM)5 ztTwF;t>4+G_!+jTvb}#m+us}*04ERz@<0n%05=c}5$U~Dm&m`*H&)x;ja-odWpv~bC|M|e8C z3;sO*D?x{lM|ek}L6Ju>Pictye4Waf>IRw#lc|S@YQ(cNR5UR(Gqf(Wy>y0jZS=e7 zTN(5ix*6>l$I#4ll{uS5oaHg=el`}i26h|vcO0>tM9xYsZLTTqTpm+i8s0I!>-@)e z_zUO?vI%|=x+|O`;v=dnCM3=vL6F2s5u|Bk7-Z??2y!d(vkD`M-AebA@2PaFzEu0F zL9p=8O%eOmeU*M4M zc-E=a`HQR4?nJjIdwKRnxc7Moc=~u1c;EEt^_}ut3t$OU2(k~33OOCx7WSUZ8g3X7 z6?q|QG=?tLA};&D^LXKe_(PM2eUs@@x>F<4aOp=gF{UxsI%Pma#M`u-YvLpjMbEjVL5S2(ZoIpbT^a?nceTFQFI#`m8q+x~~q)lUz2 zfE3sTY=94l2Zi7Y=mGB`3P=L7fXGl0)CRqWSzvuQ04U7gR4s#o` zj@7|tU`KFbxWl+H{4RVsfj|f)3{j|2lv2W!0hG_E^r`Mpt5M%0S`**VB+>HIw$ZuK zEz##NNHIKN3}GTNRWs`{zha4H4q8s8g5`vQ2Qtr};GNrOja!=*IDRL;8C?~0Os8OrCXF{(UGxU2Rxc%r`(OaoqC9a08 z4z4M!6|H?)H(4)VU)!+Xxcn3O>HPE3Cba3Wd2!4C;XVrT+s6k)tqTBld~@@gFaY=y z0Bq!LZmyMYZf;bd$;K-H+Jb)l@3MW?&;d~LdJ8=)sfqd5|3Cf*hMK?V*1e)V00009 za7bBm000XU000XU0RWnu7ytkS*-1n}R9J<*nA?uyHVlS8%Ca3h$C;h&c7gW&-=(*G zgrYzTbhneu(7cw4eOTl}@i%5kcf~QbbVI%Ry!QUBD3b^u0$_DU4881$9+mjBq&i^v+{s zYpOZ&=eDL>9T;c>vm(Wzkl&vyx_3Qk9l3j|aZ(&KM(Mf9}jgcvFM{ z8-tJz0XitjmM}tyhEtl_@^II(TGY&{5@RlwsdlN%_8Hn?q z`&Gl|A6B%pa_s2f)!n+m82J6q9j9}TEd;#|ORhX~+@?tCc)#SI3eGDJn>jyyT48N5 z0+91gfV)+TEed}7W6R;x1weYO^wyrq!D{{_`|G`D(Nui?xWw85RYg@9lm8MSRb@G^ z`SD?iF(%x!35O@adGdK*C3M8Y<=F8G_}QfkK{BUa3p3VX2- zi3nwpg7_kkYblISSu^?U>ed5bQsiC;p(+>=j4|o|w`Jf@O5;Sa$v&gXlqSv2$W?97 zDTaTiL<+p7>QW(wYJRhEs9auO=gO4i>JmRD>RJSAudjUkaxtkm^l~GmA+Dsox7k z21V!2;eAH@7wD7HM79vYrQ0n+@090r|86kROy7G>=WdK3kGu>eWe}5cIE7e4dekdl zw@13pjfbx{WPd!f+n+JUjLQGG5t8ovC^8ZUo*2>0i{q)|)V&>@FusN^y+3qxolnIq z^}eZIAVbkhoH3(kL`MoEbiLm zx$|^5vp<|EOg6&aVjjtKOU(dBpNc}*?Vs80pI_V_XB_X9zrUU6dPia8^5!v>o{K5W zRQJpeU3z9%@b4gRK71t#yy9pSJDPXpW`8Prkt4-0O1#!EMFcZMXynl~=qsC}c94um z-efgLRcY#ySrumZX#$7qV2m(tOG;~QBRNrWMqu(yF}D&M+Q4oF?$-@%Tk?{`_c4IY zvSHp;aG9s%pH`89$3INV$hY^^!B{IBCzCPC|r7f7xN;>E1U6_I6-^kX0 zOv&e&j8H0psLpu=Vcu4J+_bc_>J37ca l%5XU^6|Cd|?`v-#SNw`M^J`4Z=002ovPDHLkV1i5TjzRzc literal 0 HcmV?d00001 diff --git a/modules/ncenterlite/skins/default_bottom/ncenterlite.black.css b/modules/ncenterlite/skins/default_bottom/ncenterlite.black.css new file mode 100644 index 000000000..e0eb7f2ac --- /dev/null +++ b/modules/ncenterlite/skins/default_bottom/ncenterlite.black.css @@ -0,0 +1,15 @@ +#nc_container { border-bottom-color:#555; background-color:#333; color:#B0B0B0; opacity:0.95;} +#nc_container a { color:#B0B0B0; } +#nc_container a:hover { color:#B0B0B0; } +#nc_container strong { color:#F3F3F3; } + +#nc_container .list { background-color:#333; filter:alpha(opacity=95); opacity:0.95; -moz-opacity:0.95; } +#nc_container .list li { border-color:#555; } +#nc_container .list li a:hover { background-color:#555; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list .more { background: #555; } +#nc_container a.readall { display:none; color:#555; color:#D83722;} + +#nc_container a.notify { color:white; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } diff --git a/modules/ncenterlite/skins/default_bottom/ncenterlite.blacknoprofile.css b/modules/ncenterlite/skins/default_bottom/ncenterlite.blacknoprofile.css new file mode 100644 index 000000000..3db203843 --- /dev/null +++ b/modules/ncenterlite/skins/default_bottom/ncenterlite.blacknoprofile.css @@ -0,0 +1,17 @@ +#nc_container { border-bottom-color:#555; background-color:#333; color:#B0B0B0; opacity:0.95;} +#nc_container a { color:#B0B0B0; } +#nc_container a:hover { color:#B0B0B0; } +#nc_container strong { color:#F3F3F3; } + +#nc_container .list { background-color:#333; filter:alpha(opacity=95); opacity:0.95; -moz-opacity:0.95; } +#nc_container .list li { border-color:#555; } +#nc_container .list li a:hover { background-color:#555; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list .more { background: #555; } +#nc_container a.readall { display:none; color:#555; color:#D83722;} + +#nc_container a.notify { color:white; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } + +#nc_container img.nc_profile_img { display:none;} diff --git a/modules/ncenterlite/skins/default_bottom/ncenterlite.css b/modules/ncenterlite/skins/default_bottom/ncenterlite.css new file mode 100644 index 000000000..d22532260 --- /dev/null +++ b/modules/ncenterlite/skins/default_bottom/ncenterlite.css @@ -0,0 +1,177 @@ +#nc_container { + z-index:99; + position:fixed; + bottom:0; + right:0; + margin:0; + padding:0; + width:330px; + height:50px; + border-bottom:1px solid; + font-size:12px; + line-height:15px; +} + +#nc_container a { + padding:0; + font-size:12px; + text-decoration:none; +} + +#nc_container ul.nc_memu { + display:block; + margin:0; + padding:4px; + list-style:none; + line-height:20px; +} + +#nc_container ul:after { + content:""; + display:block; + clear:both; +} + +#nc_container ul.nc_memu li { + display:inline-block; + padding:0 5px; + height:20px; + width:auto; + background:transparent!important; +} + +#nc_container ul.nc_memu li.fLeft { + float:left; +} + +#nc_container ul.nc_memu li.fRight { + float:right; +} + +#nc_container ul.nc_memu li a.notify { + display:block; + float:left; +} + +#nc_container a.close { + display:block; +} + +#nc_container .nc_profile img { + width:20px; + height:20px; + vertical-align:top; +} + +#nc_container a.notify .num { + padding:1px 2px; + border:0; + border-radius:3px; + -webkit-border-radius:3px; + -moz-border-radius:3px; + font-size:12px; + font-weight:700; + font-family:Gulim,"lucida grande",tahoma,verdana,arial,sans-serif; +} + +#nc_container .list { + display:none; + position:absolute; + left:0; + bottom:50px; + max-width:330px; + width:330px; + box-sizing:border-box; + -moz-box-sizing:border-box; + -webkit-box-sizing:border-box; + -o-box-sizing:border-box; +} + +#nc_container a.readall { + display:none; + float:left; + margin:0 4px; + font-size:11px; + font-weight:700; +} + +#nc_container .listscroll { + overflow-y:auto; + overflow-x:hidden; +} + +#nc_container .list ul { + overflow:hidden; + margin:-1px 0; + padding:0; + list-style:none; +} + +#nc_container .list li { + margin:-1px 0; + border:1px solid #555; + border-width:1px 0; +} + +#nc_container .list li img { + float:left; + margin-right:5px; + width:45px; + height:45px; + border:0; +} + +#nc_container .list li a { + display:block; + overflow:hidden; + padding:10px; + font-size:12px; +} + +#nc_container .list span.ago { + display:block; + font-size:10px; +} + +#nc_container .list .more { + display:block; + padding:10px; + text-align:center; +} + +#nc_container a:hover,#nc_container .list li a:hover,#nc_container .list .more:hover { + text-decoration:none; +} + +@media only screen and max-device-width 480px { + #nc_container { + position:relative; + height:auto; + } + + #nc_container .list { + top:2px; + position:relative; + } + + #nc_container .list ul { + display:block; + position:relative; + } + + #nc_container ul.nc_memu:after { + content:""; + display:block; + visibility:hidden; + height:0; + clear:both; + } + + #nc_container .listscroll { + overflow:visible; + } + + .ncenterlite_block { + display:none; + } +} diff --git a/modules/ncenterlite/skins/default_bottom/ncenterlite.gray.css b/modules/ncenterlite/skins/default_bottom/ncenterlite.gray.css new file mode 100644 index 000000000..91f3e6898 --- /dev/null +++ b/modules/ncenterlite/skins/default_bottom/ncenterlite.gray.css @@ -0,0 +1,16 @@ +#nc_container { border-bottom-color:#C0C0C0; background-color:#efefef; color:#666; opacity:0.95;} +#nc_container a { color:#666; } +#nc_container a:hover { color:#666; } +#nc_container strong { color:#666; } +#nc_container a.notify strong { color:#FC2772; } +#nc_container .list { background-color:#efefef; filter:alpha(opacity=95); opacity:0.95; -moz-opacity:0.95; } +#nc_container .list li { border-color:#ddd; } +#nc_container .list li a:hover { background-color: #ddd; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list a.notify sup.num { background-color:#D83722; color:white; } +#nc_container .list .more { background: #ddd; } +#nc_container a.readall { display:none; color:#555; font-size:10px; color:#D83722;} + +#nc_container a.notify { color:#666; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } diff --git a/modules/ncenterlite/skins/default_bottom/ncenterlite.graynoprofile.css b/modules/ncenterlite/skins/default_bottom/ncenterlite.graynoprofile.css new file mode 100644 index 000000000..b7a8c5d5a --- /dev/null +++ b/modules/ncenterlite/skins/default_bottom/ncenterlite.graynoprofile.css @@ -0,0 +1,18 @@ +#nc_container { border-bottom-color:#C0C0C0; background-color:#efefef; color:#666; opacity:0.95;} +#nc_container a { color:#666; } +#nc_container a:hover { color:#666; } +#nc_container strong { color:#666; } +#nc_container a.notify strong { color:#FC2772; } +#nc_container .list { background-color:#efefef; filter:alpha(opacity=95); opacity:0.95; -moz-opacity:0.95; } +#nc_container .list li { border-color:#ddd; } +#nc_container .list li a:hover { background-color: #ddd; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list a.notify sup.num { background-color:#D83722; color:white; } +#nc_container .list .more { background: #ddd; } +#nc_container a.readall { display:none; color:#555; font-size:10px; color:#D83722;} + +#nc_container a.notify { color:#666; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } + +#nc_container img.nc_profile_img { display:none;} diff --git a/modules/ncenterlite/skins/default_bottom/ncenterlite.html b/modules/ncenterlite/skins/default_bottom/ncenterlite.html new file mode 100644 index 000000000..b4a5b5703 --- /dev/null +++ b/modules/ncenterlite/skins/default_bottom/ncenterlite.html @@ -0,0 +1,124 @@ + + + diff --git a/modules/ncenterlite/skins/default_bottom/ncenterlite.mobile.css b/modules/ncenterlite/skins/default_bottom/ncenterlite.mobile.css new file mode 100644 index 000000000..c221ae4da --- /dev/null +++ b/modules/ncenterlite/skins/default_bottom/ncenterlite.mobile.css @@ -0,0 +1,12 @@ +#nc_container { width:100%; height:auto; position:relative; } +#nc_container .list { top:2px; position:relative; } +#nc_container .list ul { display:block; position:relative; } +#nc_container ul.nc_memu:after { + content: ""; + display: block; + height: 0; + clear: both; + visibility: hidden; +} +#nc_container .listscroll { overflow: visible; } +.ncenterlite_block { display:none; } diff --git a/modules/ncenterlite/skins/default_bottom/ncenterlite.white.css b/modules/ncenterlite/skins/default_bottom/ncenterlite.white.css new file mode 100644 index 000000000..5cd778257 --- /dev/null +++ b/modules/ncenterlite/skins/default_bottom/ncenterlite.white.css @@ -0,0 +1,16 @@ +#nc_container { border:1px solid #EFEFEF; background-color:#fff; color:#000; opacity:0.95;} +#nc_container a { color:#666; } +#nc_container a:hover { color:#666; } +#nc_container strong { color:#000; } +#nc_container a.notify strong { color:#F60; } +#nc_container .list { border:1px solid #EFEFEF; background-color:#fff; filter:alpha(opacity=95); opacity:0.95; -moz-opacity:0.95; } +#nc_container .list li { border-color:#eee; } +#nc_container .list li a:hover { background-color: #eee; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list a.notify .num { background-color:#D83722; color:white; } +#nc_container .list .more { background: #eee; } +#nc_container a.readall { display:none; color:#555; font-size:10px; color:#D83722;} + +#nc_container a.notify { color:#666; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } diff --git a/modules/ncenterlite/skins/default_bottom/ncenterlite.whitenoprofile.css b/modules/ncenterlite/skins/default_bottom/ncenterlite.whitenoprofile.css new file mode 100644 index 000000000..175a58afe --- /dev/null +++ b/modules/ncenterlite/skins/default_bottom/ncenterlite.whitenoprofile.css @@ -0,0 +1,18 @@ +#nc_container { border:1px solid #EFEFEF; background-color:#fff; color:#000; opacity:0.95;} +#nc_container a { color:#666; } +#nc_container a:hover { color:#666; } +#nc_container strong { color:#000; } +#nc_container a.notify strong { color:#F60; } +#nc_container .list { border:1px solid #EFEFEF; background-color:#fff; filter:alpha(opacity=95); opacity:0.95; -moz-opacity:0.95; } +#nc_container .list li { border-color:#eee; } +#nc_container .list li a:hover { background-color: #eee; } +#nc_container .list span.ago { color:#D4AF37; } +#nc_container .list a.notify .num { background-color:#D83722; color:white; } +#nc_container .list .more { background: #eee; } +#nc_container a.readall { display:none; color:#555; font-size:10px; color:#D83722;} + +#nc_container a.notify { color:#666; } +#nc_container a.notify .num { background-color:#D83722; color:white; } +#nc_container ul.nc_memu li { padding-right:5px; } + +#nc_container img.nc_profile_img { display:none;} diff --git a/modules/ncenterlite/skins/default_bottom/skin.xml b/modules/ncenterlite/skins/default_bottom/skin.xml new file mode 100644 index 000000000..aeecfd5cf --- /dev/null +++ b/modules/ncenterlite/skins/default_bottom/skin.xml @@ -0,0 +1,40 @@ + + + XE 알림센터 Lite 기본스킨 - 우측 하단 + XE Notification Center Lite Default Skin - Right bottom + 1.2.0 + 2013-05-10 + + + XE Public + XE Public + + + + + 검은색 + Black + + + 회색 + Gray + + + 흰색 + White + + + + 검은색(no profile) + Black (no profile) + + + 회색(no profile) + Gray (no profile) + + + 흰색(no profile) + White (no profile) + + + diff --git a/modules/ncenterlite/skins/default_bottom/userconfig.html b/modules/ncenterlite/skins/default_bottom/userconfig.html new file mode 100644 index 000000000..52e2e2f0b --- /dev/null +++ b/modules/ncenterlite/skins/default_bottom/userconfig.html @@ -0,0 +1,68 @@ + + +
+ +
+

{$XE_VALIDATOR_MESSAGE}

+
+ + +
+ + + + +
+

{@$user_str = $member_info->nick_name} + {@$user_str = $logged_info->nick_name} + {@$title_str = Context::getLang('ncenterlite_userconfig_title')} + {sprintf($title_str, $user_str)} +

+ +

{$lang->ncenterlite_userconfig_about} ({$lang->ncenterlite_userconfig_about_warning})

+
+ +
+ + +

{$lang->ncenterlite_comment_noti_about}

+
+
+
+ +
+ + +

{$lang->ncenterlite_mention_noti_about}

+
+
+
+ +
+ + +

{$lang->ncenterlite_message_noti_about}

+
+
+ +
+
+
+ +
+
+
+
+ \ No newline at end of file diff --git a/modules/ncenterlite/tpl/Config.html b/modules/ncenterlite/tpl/Config.html new file mode 100644 index 000000000..0067f5496 --- /dev/null +++ b/modules/ncenterlite/tpl/Config.html @@ -0,0 +1,272 @@ + + + + +
+
+ + +

{$lang->ncenterlite_config_environment}

+ {$lang->ncenterlite_config_environment_about} +
+ + +
+
+
+ + +
+ + + + + + + + + + + + + +
+
+ +
+
+ +
+

{$lang->etc}

+
+ +
+ +
+
+ +
+ +
+ +
+
+
+
+
+

{$lang->ncenterlite_install_version} : Lite v{$ncenterlite_module_info->version}

+ +
\ No newline at end of file diff --git a/modules/ncenterlite/tpl/css/ncenter_admin.css b/modules/ncenterlite/tpl/css/ncenter_admin.css new file mode 100644 index 000000000..b01d9527b --- /dev/null +++ b/modules/ncenterlite/tpl/css/ncenter_admin.css @@ -0,0 +1,12 @@ +.mention_option ul.preview, +.mention_option ul.preview li { margin:0; padding:0; list-style:none; } + +.mention_option ul.preview li { display:inline-block; margin:5px; padding:3px 7px; border-radius:50px; color:red; font-size:1.2em; background:#EEE; } +.mention_option ul.preview li strong { color:black; } + +.mention_option ul.preview li.enable, +.mention_option ul.preview li.disable { color:black; } +.mention_option ul.preview li.enable strong, +.mention_option ul.preview li.disable strong { color:#FFF; } +.mention_option ul.preview li.enable { background:green; } +.mention_option ul.preview li.disable { background:red; } diff --git a/modules/ncenterlite/tpl/header.html b/modules/ncenterlite/tpl/header.html new file mode 100644 index 000000000..be44bc02d --- /dev/null +++ b/modules/ncenterlite/tpl/header.html @@ -0,0 +1,14 @@ +
+

{$lang->ncenterlite}

+
+ + + +
+

{$XE_VALIDATOR_MESSAGE}

+
\ No newline at end of file diff --git a/modules/ncenterlite/tpl/js/ncenter_admin.js b/modules/ncenterlite/tpl/js/ncenter_admin.js new file mode 100644 index 000000000..5fa289894 --- /dev/null +++ b/modules/ncenterlite/tpl/js/ncenter_admin.js @@ -0,0 +1,106 @@ +function doDisplaySkinColorset(sel, colorset) +{ + var skin = sel.options[sel.selectedIndex].value; + + var params = new Array(); + params["skin"] = skin; + params["colorset"] = colorset; + + var response_tags = ['error', 'message', 'colorset_list']; + + exec_xml('ncenterlite', 'getColorsetList', params, completeGetSkinColorset, response_tags, params); +} + +function completeGetSkinColorset(ret_obj, response_tags, params, fo_obj) +{ + var sel = get_by_id('fo_ncenterlite').colorset; + var length = sel.options.length; + var selected_colorset = params['colorset']; + for(var i=0;i= steps) { + //element.style.backgroundColor = 'rgb(' + endcolour.toString() + ')'; + element.style.backgroundColor = c; //'rgb(' + endcolour.toString() + ')'; + clearInterval(timer); + } + }, interval); + } + + var s = decodeURIComponent(location.href).replace(/.*#comment_/,''); + if(!s || s === decodeURIComponent(location.href)) return; + s = 'comment_' + s; + jQuery('.xe_content').each(function(){ + var t = jQuery(this); + if(t.hasClass(s) || (new RegExp(s + '_')).test(t.attr('class'))){ + var c = t.css('display','block').css('background-color'); + y(this, [255,255,60], [255,255,255], 750, c); + ncenterlite_need_highlight = false; + } + }); +} + +if(typeof _viewSubComment == 'function') { + old__viewSubComment = _viewSubComment; + _viewSubComment = function(ret_obj) { + old__viewSubComment(ret_obj); + if(ncenterlite_need_highlight) { + setTimeout(function(){ + var s = decodeURIComponent(location.href).match(/#.*comment_([0-9]+)/); + if(s) { + ncenterlite_highlight(); + location.href = '#social_comment_' + s[1]; + } + }, 500); + } + }; +} + +jQuery(function(){ + ncenterlite_highlight(); +}); diff --git a/modules/ncenterlite/tpl/ncenter_list.html b/modules/ncenterlite/tpl/ncenter_list.html new file mode 100644 index 000000000..2bf1b1fad --- /dev/null +++ b/modules/ncenterlite/tpl/ncenter_list.html @@ -0,0 +1,67 @@ + + + +
+

알림 목록

+ +

알림이 없습니다.

+ + +
+
+
+ + + + +

주의! 회원이 확인하지 않은 알림도 삭제됩니다.

+
+
+
+ + + + + + + + + + + + + + {@ + $oMemberModel = getModel('member'); + $member_info = $oMemberModel->getMemberInfoByMemberSrl($val->member_srl); + } + + + + + + + + + +
보낸사람받은사람내용읽음여부{$lang->date}
{$val->target_nick_name}{$member_info->nick_name} 타겟없음{$val->text}{$val->readed} + {zdate($val->regdate,"Y-m-d")} +
+ {zdate($val->regdate,"H:i:s")} +
+ + +
+
\ No newline at end of file From 7ed2d132d90673eecf864cd1d2918e2a797db383 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Thu, 17 Mar 2016 06:06:30 +0900 Subject: [PATCH 120/205] delete moduleObject trigger. --- modules/ncenterlite/ncenterlite.class.php | 1 - .../ncenterlite/ncenterlite.controller.php | 83 ------------------- 2 files changed, 84 deletions(-) diff --git a/modules/ncenterlite/ncenterlite.class.php b/modules/ncenterlite/ncenterlite.class.php index edcc7777f..a622e9384 100644 --- a/modules/ncenterlite/ncenterlite.class.php +++ b/modules/ncenterlite/ncenterlite.class.php @@ -38,7 +38,6 @@ class ncenterlite extends ModuleObject array('document.deleteDocument', 'ncenterlite', 'controller', 'triggerAfterDeleteDocument', 'after'), array('display', 'ncenterlite', 'controller', 'triggerBeforeDisplay', 'before'), array('moduleHandler.proc', 'ncenterlite', 'controller', 'triggerAfterModuleHandlerProc', 'after'), - array('moduleObject.proc', 'ncenterlite', 'controller', 'triggerBeforeModuleObjectProc', 'before'), array('member.deleteMember', 'ncenterlite', 'controller', 'triggerAfterDeleteMember', 'after'), array('communication.sendMessage', 'ncenterlite', 'controller', 'triggerAfterSendMessage', 'after'), array('document.updateVotedCount', 'ncenterlite', 'controller', 'triggerAfterVotedupdate', 'after'), diff --git a/modules/ncenterlite/ncenterlite.controller.php b/modules/ncenterlite/ncenterlite.controller.php index 99538bb5b..c54f1bac1 100644 --- a/modules/ncenterlite/ncenterlite.controller.php +++ b/modules/ncenterlite/ncenterlite.controller.php @@ -258,89 +258,6 @@ class ncenterliteController extends ncenterlite return new Object(); } - function triggerBeforeModuleObjectProc(&$oModule) - { - if(version_compare(__XE_VERSION__, '1.7.4', '>=')) - { - return new Object(); - } - $oNcenterliteModel = getModel('ncenterlite'); - $config = $oNcenterliteModel->getConfig(); - if($config->use != 'Y') return new Object(); - - $vars = Context::getRequestVars(); - $logged_info = Context::get('logged_info'); - - $messages_member_config = $oNcenterliteModel->getMemberConfig($logged_info->member_srl); - $message_member_config = $messages_member_config->data; - - // 쪽지 체크 및 유저 쪽지 알림 채크 - if($config->message_notify == 'Y' && $message_member_config->message_notify != 'N') - { - $flag_path = './files/ncenterlite/new_message_flags/'; - - $need_update = false; - // 쪽지 알림 메시지 체크 - if(strpos(Context::getHtmlFooter(), 'xeNotifyMessage') !== FALSE) - { - $need_update = true; - } - // 메시지 플래그 파일 체크 - else if(file_exists($flag_path . $logged_info->member_srl)) - { - $need_update = true; - } - - if($oModule->act == 'procCommunicationSendMessage') - { - FileHandler::makeDir($flag_path); - $flag_file = sprintf('%s%s', $flag_path, $vars->receiver_srl); - FileHandler::writeFile($flag_file, $vars->receiver_srl); - } - else if($need_update) - { - $oMemberModel = getModel('member'); - $_sender_member_srl = trim(FileHandler::readFile($flag_path . $logged_info->member_srl)); - $sender_member_info = $oMemberModel->getMemberInfoByMemberSrl($_sender_member_srl); - FileHandler::removeFile($flag_path . $logged_info->member_srl); - - // 새 쪽지 수 - $args = new stdClass(); - $args->receiver_srl = $logged_info->member_srl; - $output = executeQuery('ncenterlite.getCountNewMessage', $args); - $message_count = $output->data->count; - - // 기존 쪽지 알림을 읽은 것으로 변경 - $cond = new stdClass(); - $cond->type = $this->_TYPE_MESSAGE; - $cond->member_srl = $logged_info->member_srl; - $output = executeQuery('ncenterlite.updateNotifyReadedByType', $cond); - - if(!$message_count) return; - - // 알림 추가 - $args = new stdClass(); - $args->member_srl = $logged_info->member_srl; - $args->srl = $sender_member_info->member_srl; - if(!$args->srl) $args->srl = 0; - $args->target_p_srl = 1; - $args->target_srl = $sender_member_info->member_srl; - if(!$args->srl) $args->target_srl = 0; - $args->type = $this->_TYPE_MESSAGE; - $args->target_type = $this->_TYPE_MESSAGE; - $args->target_url_params = $target_url_params; - $args->target_summary = $message_count; - $args->target_nick_name = $sender_member_info->nick_name; - $args->target_member_srl = $sender_member_info->member_srl; - $args->regdate = date('YmdHis'); - $args->notify = $this->_getNotifyId($args); - $args->target_url = getNotEncodedFullUrl('', 'act', 'dispCommunicationMessages'); - - $output = $this->_insertNotify($args); - } - } - } - function triggerAfterSendMessage(&$trigger_obj) { $oNcenterliteModel = getModel('ncenterlite'); From d8ef4bfb37ba057d53b79cb3c6f0176fdc0326fd Mon Sep 17 00:00:00 2001 From: BJRambo Date: Thu, 17 Mar 2016 06:13:24 +0900 Subject: [PATCH 121/205] if use massage notify, communication notify do not use. --- modules/communication/communication.controller.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/communication/communication.controller.php b/modules/communication/communication.controller.php index 3845e8b20..6af77537f 100644 --- a/modules/communication/communication.controller.php +++ b/modules/communication/communication.controller.php @@ -820,7 +820,13 @@ class communicationController extends communication { return new Object(); } - + + $ncenterlite_config = getModel('ncenterlite')->getConfig(); + if($ncenterlite_config->message_notify == 'Y' && $ncenterlite_config->use == 'Y') + { + return new Object(); + } + $config = getModel('communication')->getConfig(); if ($config->enable_message == 'N' || starts_with('dispCommunication', Context::get('act'))) { From 2bf39091c5f8c005717b8f6a14f49a4e29362a24 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Thu, 17 Mar 2016 06:15:57 +0900 Subject: [PATCH 122/205] erase code intended to disable a message. --- modules/ncenterlite/ncenterlite.controller.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/modules/ncenterlite/ncenterlite.controller.php b/modules/ncenterlite/ncenterlite.controller.php index c54f1bac1..faefc6c51 100644 --- a/modules/ncenterlite/ncenterlite.controller.php +++ b/modules/ncenterlite/ncenterlite.controller.php @@ -637,10 +637,6 @@ class ncenterliteController extends ncenterlite } } - Context::addHtmlFooter(''); - $this->_addFile(); $html = $this->_getTemplate(); $output_display = $html . $output_display; From 07f2656424fd325e4cac93cafa0ad1f22f32ffe8 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Thu, 17 Mar 2016 06:50:31 +0900 Subject: [PATCH 123/205] Revised methods of improting files. --- modules/ncenterlite/ncenterlite.controller.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/modules/ncenterlite/ncenterlite.controller.php b/modules/ncenterlite/ncenterlite.controller.php index faefc6c51..13602a8e3 100644 --- a/modules/ncenterlite/ncenterlite.controller.php +++ b/modules/ncenterlite/ncenterlite.controller.php @@ -587,11 +587,13 @@ class ncenterliteController extends ncenterlite Context::set('ncenterlite_config', $config); - $oModuleModel = getModel('module'); - $ncenterlite_module_info = $oModuleModel->getModuleInfoXml('ncenterlite'); - $jsCacheRefresh = '?'.$ncenterlite_module_info->version.'.'.$ncenterlite_module_info->date.'.js'; - Context::addJsFile('./modules/ncenterlite/tpl/js/ncenterlite.js'.$jsCacheRefresh, true, '', 100000); - + $js_args = array( + './modules/ncenterlite/tpl/js/ncenterlite.js', + 'body', + '', + 100000 + ); + Context::loadFile($js_args); $oNcenterliteModel = getModel('ncenterlite'); From 7bcc9658d6c2e6fdc161a6c0ed62babd68a54b93 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Thu, 17 Mar 2016 06:58:38 +0900 Subject: [PATCH 124/205] modity XE to Rhymix --- modules/ncenterlite/conf/info.xml | 4 ++-- modules/ncenterlite/conf/module.xml | 6 +++--- modules/ncenterlite/lang/lang.xml | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/ncenterlite/conf/info.xml b/modules/ncenterlite/conf/info.xml index 6d943f59c..5aab600eb 100644 --- a/modules/ncenterlite/conf/info.xml +++ b/modules/ncenterlite/conf/info.xml @@ -1,7 +1,7 @@ - XE 알림센터 Lite - XE Notification Center Lite + Rhymix 알림센터 Lite + Rhymix Notification Center Lite 사이트 사용자간의 커뮤니케이션에 대한 정보를 알려주는 모듈입니다. This module notify users of information about new documents, comments and/or messages that call them. This module will enhance communication beween site users. 2.1.5 diff --git a/modules/ncenterlite/conf/module.xml b/modules/ncenterlite/conf/module.xml index 489517102..0559d6e93 100644 --- a/modules/ncenterlite/conf/module.xml +++ b/modules/ncenterlite/conf/module.xml @@ -22,9 +22,9 @@ - XE 알림센터 Lite - XE Notification Center Lite - XE 通知センター Lite + Rhymix 알림센터 Lite + Rhymix Notification Center Lite + Rhymix 通知センター Lite diff --git a/modules/ncenterlite/lang/lang.xml b/modules/ncenterlite/lang/lang.xml index d6a8762c3..4e2e080f0 100644 --- a/modules/ncenterlite/lang/lang.xml +++ b/modules/ncenterlite/lang/lang.xml @@ -1,14 +1,14 @@ - - - + + + - + From 50581484e49528361e5ab3af4b082c635a314821 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Thu, 17 Mar 2016 07:07:39 +0900 Subject: [PATCH 125/205] modify __XE_VERSION__ to RX_VERSION --- modules/ncenterlite/ncenterlite.admin.view.php | 2 +- modules/ncenterlite/ncenterlite.controller.php | 2 +- modules/ncenterlite/ncenterlite.model.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/ncenterlite/ncenterlite.admin.view.php b/modules/ncenterlite/ncenterlite.admin.view.php index 4c745ed0b..81c1ac34a 100644 --- a/modules/ncenterlite/ncenterlite.admin.view.php +++ b/modules/ncenterlite/ncenterlite.admin.view.php @@ -59,7 +59,7 @@ class ncenterliteAdminView extends ncenterlite $_ncenterlite_iframe_url = 'http://sosifam.com/index.php?mid=ncenterlite_iframe'; if(!$agreement_ver) { - $_host_info = urlencode($_SERVER['HTTP_HOST']) . '-NC' . $ncenterlite_module_info->version . '-PHP' . phpversion() . '-XE' . __XE_VERSION__; + $_host_info = urlencode($_SERVER['HTTP_HOST']) . '-NC' . $ncenterlite_module_info->version . '-PHP' . phpversion() . '-XE' . RX_VERSION; } Context::set('_ncenterlite_iframe_url', $_ncenterlite_iframe_url . '&_host='. $_host_info); Context::set('ncenterlite_module_info', $ncenterlite_module_info); diff --git a/modules/ncenterlite/ncenterlite.controller.php b/modules/ncenterlite/ncenterlite.controller.php index 13602a8e3..516d705ba 100644 --- a/modules/ncenterlite/ncenterlite.controller.php +++ b/modules/ncenterlite/ncenterlite.controller.php @@ -270,7 +270,7 @@ class ncenterliteController extends ncenterlite $messages_member_config = $oNcenterliteModel->getMemberConfig($trigger_obj->receiver_srl); $message_member_config = $messages_member_config->data; - if(version_compare(__XE_VERSION__, '1.8', '>=') && $message_member_config->message_notify != 'N') + if(version_compare(RX_VERSION, '1.8', '>=') && $message_member_config->message_notify != 'N') { $args = new stdClass(); $args->member_srl = $trigger_obj->receiver_srl; diff --git a/modules/ncenterlite/ncenterlite.model.php b/modules/ncenterlite/ncenterlite.model.php index bb4c48c18..23a6d749c 100644 --- a/modules/ncenterlite/ncenterlite.model.php +++ b/modules/ncenterlite/ncenterlite.model.php @@ -161,7 +161,7 @@ class ncenterliteModel extends ncenterlite break; // 메시지. 쪽지 case 'E': - if(version_compare(__XE_VERSION__, '1.7.4', '>=')) + if(version_compare(RX_VERSION, '1.7.4', '>=')) { $str = sprintf($lang->ncenterlite_message_mention,$target_member, $v->target_summary); } From 36d7b83e4352b813068286b59007625e6d338865 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Thu, 17 Mar 2016 07:09:48 +0900 Subject: [PATCH 126/205] do not check XE Version. --- modules/ncenterlite/ncenterlite.controller.php | 18 +----------------- modules/ncenterlite/ncenterlite.model.php | 9 +-------- 2 files changed, 2 insertions(+), 25 deletions(-) diff --git a/modules/ncenterlite/ncenterlite.controller.php b/modules/ncenterlite/ncenterlite.controller.php index 516d705ba..821c9e8a4 100644 --- a/modules/ncenterlite/ncenterlite.controller.php +++ b/modules/ncenterlite/ncenterlite.controller.php @@ -270,7 +270,7 @@ class ncenterliteController extends ncenterlite $messages_member_config = $oNcenterliteModel->getMemberConfig($trigger_obj->receiver_srl); $message_member_config = $messages_member_config->data; - if(version_compare(RX_VERSION, '1.8', '>=') && $message_member_config->message_notify != 'N') + if($message_member_config->message_notify != 'N') { $args = new stdClass(); $args->member_srl = $trigger_obj->receiver_srl; @@ -285,22 +285,6 @@ class ncenterliteController extends ncenterlite $args->target_url = getNotEncodedFullUrl('', 'act', 'dispCommunicationMessages', 'message_srl', $trigger_obj->related_srl); $output = $this->_insertNotify($args); } - elseif($message_member_config->message_notify != 'N') - { - $args = new stdClass(); - $args->member_srl = $trigger_obj->receiver_srl; - $args->srl = $trigger_obj->receiver_srl; - $args->target_p_srl = '1'; - $args->target_srl = $trigger_obj->sender_srl; - $args->type = $this->_TYPE_MESSAGE; - $args->target_type = $this->_TYPE_MESSAGE; - $args->target_summary = $trigger_obj->title; - $args->regdate = date('YmdHis'); - $args->notify = $this->_getNotifyId($args); - $args->target_url = getNotEncodedFullUrl('', 'act', 'dispCommunicationMessages'); - $output = $this->_insertNotify($args); - } - } function triggerAfterVotedupdate(&$obj) diff --git a/modules/ncenterlite/ncenterlite.model.php b/modules/ncenterlite/ncenterlite.model.php index 23a6d749c..77c261582 100644 --- a/modules/ncenterlite/ncenterlite.model.php +++ b/modules/ncenterlite/ncenterlite.model.php @@ -161,14 +161,7 @@ class ncenterliteModel extends ncenterlite break; // 메시지. 쪽지 case 'E': - if(version_compare(RX_VERSION, '1.7.4', '>=')) - { - $str = sprintf($lang->ncenterlite_message_mention,$target_member, $v->target_summary); - } - else - { - $str = sprintf($lang->ncenterlite_message_string, $v->target_summary); - } + $str = sprintf($lang->ncenterlite_message_mention,$target_member, $v->target_summary); break; case 'T': $str = sprintf($lang->ncenterlite_test_noti, $target_member); From 47da220e439a29668788a1a65915572ccb068625 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Thu, 17 Mar 2016 07:14:19 +0900 Subject: [PATCH 127/205] modify check RX Version. --- modules/ncenterlite/ncenterlite.admin.view.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ncenterlite/ncenterlite.admin.view.php b/modules/ncenterlite/ncenterlite.admin.view.php index 81c1ac34a..cd28e11f9 100644 --- a/modules/ncenterlite/ncenterlite.admin.view.php +++ b/modules/ncenterlite/ncenterlite.admin.view.php @@ -59,7 +59,7 @@ class ncenterliteAdminView extends ncenterlite $_ncenterlite_iframe_url = 'http://sosifam.com/index.php?mid=ncenterlite_iframe'; if(!$agreement_ver) { - $_host_info = urlencode($_SERVER['HTTP_HOST']) . '-NC' . $ncenterlite_module_info->version . '-PHP' . phpversion() . '-XE' . RX_VERSION; + $_host_info = urlencode($_SERVER['HTTP_HOST']) . '-NC' . $ncenterlite_module_info->version . '-PHP' . phpversion() . '-RX' . RX_VERSION; } Context::set('_ncenterlite_iframe_url', $_ncenterlite_iframe_url . '&_host='. $_host_info); Context::set('ncenterlite_module_info', $ncenterlite_module_info); From 76a62b0edad5a772ed722f2421a6f0bec602a139 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Thu, 17 Mar 2016 07:21:32 +0900 Subject: [PATCH 128/205] delete unused methods. --- modules/ncenterlite/conf/module.xml | 1 - modules/ncenterlite/ncenterlite.controller.php | 15 +++++---------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/modules/ncenterlite/conf/module.xml b/modules/ncenterlite/conf/module.xml index 0559d6e93..f2a415997 100644 --- a/modules/ncenterlite/conf/module.xml +++ b/modules/ncenterlite/conf/module.xml @@ -13,7 +13,6 @@ - diff --git a/modules/ncenterlite/ncenterlite.controller.php b/modules/ncenterlite/ncenterlite.controller.php index 821c9e8a4..e455a39d8 100644 --- a/modules/ncenterlite/ncenterlite.controller.php +++ b/modules/ncenterlite/ncenterlite.controller.php @@ -262,6 +262,11 @@ class ncenterliteController extends ncenterlite { $oNcenterliteModel = getModel('ncenterlite'); $config = $oNcenterliteModel->getConfig(); + $communication_config = getModel('communication')->getConfig(); + if($communication_config->enable_message != 'Y') + { + return new Object(); + } if($config->use != 'Y') return new Object(); if($config->message_notify == 'N') { @@ -742,16 +747,6 @@ class ncenterliteController extends ncenterlite return $ouptut; } - function procNcenterliteNotifyRead() - { - $logged_info = Context::get('logged_info'); - $target_srl = Context::get('target_srl'); - if(!$logged_info || !$target_srl) return new Object(-1, 'msg_invalid_request'); - - $output = $this->updateNotifyRead($notify, $logged_info->member_srl); - return $output; - } - function procNcenterliteNotifyReadAll() { $logged_info = Context::get('logged_info'); From 25e491e2fc6915c89d63ddc60bf80f09aeb35290 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Thu, 17 Mar 2016 07:38:11 +0900 Subject: [PATCH 129/205] delete does not use functions. --- modules/ncenterlite/ncenterlite.controller.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/ncenterlite/ncenterlite.controller.php b/modules/ncenterlite/ncenterlite.controller.php index e455a39d8..7adaa5dff 100644 --- a/modules/ncenterlite/ncenterlite.controller.php +++ b/modules/ncenterlite/ncenterlite.controller.php @@ -46,9 +46,6 @@ class ncenterliteController extends ncenterlite function triggerAfterDeleteMember($obj) { - $oNcenterliteModel = getModel('ncenterlite'); - $config = $oNcenterliteModel->getConfig(); - $member_srl = $obj->member_srl; if(!$member_srl) return new Object(); @@ -77,9 +74,7 @@ class ncenterliteController extends ncenterlite $mention_targets = $this->_getMentionTarget($content); if(!$mention_targets || !count($mention_targets)) return new Object(); - $oDocumentModel = getModel('document'); $document_srl = $obj->document_srl; - $oDocument = $oDocumentModel->getDocument($document_srl); $module_info = $oModuleModel->getModuleInfoByDocumentSrl($document_srl); $is_anonymous = $this->_isAnonymous($this->_TYPE_DOCUMENT, $obj); From e7b21ea2ee941749a46dea17046fde0b5e423126 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Thu, 17 Mar 2016 07:48:18 +0900 Subject: [PATCH 130/205] =?UTF-8?q?=EC=BD=94=EB=93=9C=EB=A5=BC=20=EA=B9=94?= =?UTF-8?q?=EA=B8=88=ED=95=98=EA=B2=8C=20=EC=A0=95=EB=A6=AC=ED=95=A8.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ncenterlite/ncenterlite.controller.php | 252 +++++++++++++----- 1 file changed, 184 insertions(+), 68 deletions(-) diff --git a/modules/ncenterlite/ncenterlite.controller.php b/modules/ncenterlite/ncenterlite.controller.php index 7adaa5dff..f8368eb9f 100644 --- a/modules/ncenterlite/ncenterlite.controller.php +++ b/modules/ncenterlite/ncenterlite.controller.php @@ -1,4 +1,5 @@ member_srl; } - if($logged_info->member_srl != $member_srl && $logged_info->is_admin != 'Y') return new Object(-1, 'ncenterlite_stop_no_permission_other_user_settings'); + if($logged_info->member_srl != $member_srl && $logged_info->is_admin != 'Y') + { + return new Object(-1, 'ncenterlite_stop_no_permission_other_user_settings'); + } $output = $oNcenterliteModel->getMemberConfig($member_srl); @@ -36,9 +40,9 @@ class ncenterliteController extends ncenterlite $this->setMessage('success_updated'); - if(!in_array(Context::getRequestMethod(),array('XMLRPC','JSON'))) + if(!in_array(Context::getRequestMethod(), array('XMLRPC', 'JSON'))) { - $returnUrl = Context::get('success_return_url') ? Context::get('success_return_url') : getNotEncodedUrl('', 'act', 'dispNcenterliteUserConfig','member_srl',$member_srl); + $returnUrl = Context::get('success_return_url') ? Context::get('success_return_url') : getNotEncodedUrl('', 'act', 'dispNcenterliteUserConfig', 'member_srl', $member_srl); header('location: ' . $returnUrl); return; } @@ -47,7 +51,10 @@ class ncenterliteController extends ncenterlite function triggerAfterDeleteMember($obj) { $member_srl = $obj->member_srl; - if(!$member_srl) return new Object(); + if(!$member_srl) + { + return new Object(); + } $args = new stdClass(); $args->member_srl = $member_srl; @@ -63,16 +70,25 @@ class ncenterliteController extends ncenterlite { $oModuleModel = getModel('module'); - if($this->_isDisable()) return; + if($this->_isDisable()) + { + return; + } $oNcenterliteModel = getModel('ncenterlite'); $config = $oNcenterliteModel->getConfig(); - if($config->use != 'Y') return new Object(); + if($config->use != 'Y') + { + return new Object(); + } $content = strip_tags($obj->title . ' ' . $obj->content); $mention_targets = $this->_getMentionTarget($content); - if(!$mention_targets || !count($mention_targets)) return new Object(); + if(!$mention_targets || !count($mention_targets)) + { + return new Object(); + } $document_srl = $obj->document_srl; $module_info = $oModuleModel->getModuleInfoByDocumentSrl($document_srl); @@ -115,11 +131,17 @@ class ncenterliteController extends ncenterlite function triggerAfterInsertComment(&$obj) { - if($this->_isDisable()) return; + if($this->_isDisable()) + { + return; + } $oNcenterliteModel = getModel('ncenterlite'); $config = $oNcenterliteModel->getConfig(); - if($config->use != 'Y') return new Object(); + if($config->use != 'Y') + { + return new Object(); + } $logged_info = Context::get('logged_info'); $notify_member_srl = array(); @@ -154,7 +176,7 @@ class ncenterliteController extends ncenterlite $args->target_srl = $obj->comment_srl; $args->type = $this->_TYPE_COMMENT; $args->target_type = $this->_TYPE_MENTION; - $args->target_url = getNotEncodedFullUrl('', 'document_srl', $document_srl, '_comment_srl', $comment_srl) . '#comment_'. $comment_srl; + $args->target_url = getNotEncodedFullUrl('', 'document_srl', $document_srl, '_comment_srl', $comment_srl) . '#comment_' . $comment_srl; $args->target_summary = cut_str(strip_tags($content), 50); $args->target_nick_name = $obj->nick_name; $args->target_email_address = $obj->email_address; @@ -167,7 +189,7 @@ class ncenterliteController extends ncenterlite $admin_list = $oNcenterliteModel->getMemberAdmins(); $admins_list = $admin_list->data; - + foreach($admins_list as $admins) { if(is_array($config->admin_comment_module_srls) && in_array($module_info->module_srl, $config->admin_comment_module_srls)) @@ -179,7 +201,7 @@ class ncenterliteController extends ncenterlite $args->target_srl = $obj->comment_srl; $args->type = $this->_TYPE_COMMENT; $args->target_type = $this->_TYPE_ADMIN_COMMENT; - $args->target_url = getNotEncodedFullUrl('', 'document_srl', $document_srl, '_comment_srl', $comment_srl) . '#comment_'. $comment_srl; + $args->target_url = getNotEncodedFullUrl('', 'document_srl', $document_srl, '_comment_srl', $comment_srl) . '#comment_' . $comment_srl; $args->target_summary = cut_str(strip_tags($content), 50); $args->target_nick_name = $obj->nick_name; $args->target_email_address = $obj->email_address; @@ -208,7 +230,7 @@ class ncenterliteController extends ncenterlite $args->target_srl = $obj->comment_srl; $args->type = $this->_TYPE_COMMENT; $args->target_type = $this->_TYPE_COMMENT; - $args->target_url = getNotEncodedFullUrl('', 'document_srl', $document_srl, '_comment_srl', $comment_srl) . '#comment_'. $comment_srl; + $args->target_url = getNotEncodedFullUrl('', 'document_srl', $document_srl, '_comment_srl', $comment_srl) . '#comment_' . $comment_srl; $args->target_summary = cut_str(strip_tags($content), 50); $args->target_nick_name = $obj->nick_name; $args->target_email_address = $obj->email_address; @@ -239,7 +261,7 @@ class ncenterliteController extends ncenterlite $args->target_srl = $comment_srl; $args->type = $this->_TYPE_DOCUMENT; $args->target_type = $this->_TYPE_COMMENT; - $args->target_url = getNotEncodedFullUrl('', 'document_srl', $document_srl, '_comment_srl', $comment_srl) . '#comment_'. $comment_srl; + $args->target_url = getNotEncodedFullUrl('', 'document_srl', $document_srl, '_comment_srl', $comment_srl) . '#comment_' . $comment_srl; $args->target_summary = cut_str(strip_tags($content), 50); $args->target_nick_name = $obj->nick_name; $args->target_email_address = $obj->email_address; @@ -262,7 +284,10 @@ class ncenterliteController extends ncenterlite { return new Object(); } - if($config->use != 'Y') return new Object(); + if($config->use != 'Y') + { + return new Object(); + } if($config->message_notify == 'N') { return new Object(); @@ -294,9 +319,18 @@ class ncenterliteController extends ncenterlite $oNcenterliteModel = getModel('ncenterlite'); $config = $oNcenterliteModel->getConfig(); - if($config->use != 'Y') return new Object(); - if($config->voted_format != 'Y') return new Object(); - if($obj->point < 0) return new Object(); + if($config->use != 'Y') + { + return new Object(); + } + if($config->voted_format != 'Y') + { + return new Object(); + } + if($obj->point < 0) + { + return new Object(); + } $args = new stdClass(); $args->member_srl = $obj->member_srl; @@ -309,14 +343,17 @@ class ncenterliteController extends ncenterlite $args->regdate = date('YmdHis'); $args->notify = $this->_getNotifyId($args); $args->target_url = getNotEncodedFullUrl('', 'document_srl', $obj->document_srl); - $output = $this->_insertNotify($args, $is_anonymous); + $output = $this->_insertNotify($args); } function triggerAfterDeleteComment(&$obj) { $oNcenterliteModel = getModel('ncenterlite'); $config = $oNcenterliteModel->getConfig(); - if($config->use != 'Y') return new Object(); + if($config->use != 'Y') + { + return new Object(); + } $args = new stdClass(); $args->srl = $obj->comment_srl; @@ -328,7 +365,10 @@ class ncenterliteController extends ncenterlite { $oNcenterliteModel = getModel('ncenterlite'); $config = $oNcenterliteModel->getConfig(); - if($config->use != 'Y') return new Object(); + if($config->use != 'Y') + { + return new Object(); + } $args = new stdClass(); $args->srl = $obj->document_srl; @@ -342,11 +382,17 @@ class ncenterliteController extends ncenterlite $logged_info = Context::get('logged_info'); $args = new stdClass(); - if($oModule->getLayoutFile() == 'popup_layout.html') Context::set('ncenterlite_is_popup', TRUE); + if($oModule->getLayoutFile() == 'popup_layout.html') + { + Context::set('ncenterlite_is_popup', TRUE); + } $oNcenterliteModel = getModel('ncenterlite'); $config = $oNcenterliteModel->getConfig(); - if($config->use != 'Y') return new Object(); + if($config->use != 'Y') + { + return new Object(); + } $this->_hide_ncenterlite = false; if($oModule->module == 'beluxe' && Context::get('is_modal')) @@ -380,7 +426,7 @@ class ncenterliteController extends ncenterlite $oDocument = Context::get('oDocument'); $logged_info = Context::get('logged_info'); - if($document_srl && $logged_info && $config->document_read=='Y') + if($document_srl && $logged_info && $config->document_read == 'Y') { $args->srl = $document_srl; $args->member_srl = $logged_info->member_srl; @@ -394,7 +440,7 @@ class ncenterliteController extends ncenterlite { if(array_key_exists($comment_srl, $_comment_list)) { - $url = getNotEncodedUrl('_comment_srl','') . '#comment_' . $comment_srl; + $url = getNotEncodedUrl('_comment_srl', '') . '#comment_' . $comment_srl; $need_check_socialxe = true; } else @@ -402,7 +448,7 @@ class ncenterliteController extends ncenterlite $cpage = $oDocument->comment_page_navigation->cur_page; if($cpage > 1) { - $url = getNotEncodedUrl('cpage', $cpage-1) . '#comment_' . $comment_srl; + $url = getNotEncodedUrl('cpage', $cpage - 1) . '#comment_' . $comment_srl; $need_check_socialxe = true; } else @@ -440,7 +486,7 @@ class ncenterliteController extends ncenterlite } } - $url = str_replace('&','&',$url); + $url = str_replace('&', '&', $url); header('location: ' . $url); Context::close(); exit; @@ -490,7 +536,7 @@ class ncenterliteController extends ncenterlite $args->target_type = $this->_TYPE_COMMENT; $args->target_srl = $vars->parent_srl; $args->target_p_srl = '1'; - $args->target_url = getNotEncodedFullUrl('', 'document_srl', $vars->document_srl, '_comment_srl', $vars->parent_srl) . '#comment_'. $vars->parent_srl; + $args->target_url = getNotEncodedFullUrl('', 'document_srl', $vars->document_srl, '_comment_srl', $vars->parent_srl) . '#comment_' . $vars->parent_srl; $args->target_summary = cut_str(strip_tags($vars->content), 50); $args->target_nick_name = $logged_info->nick_name; $args->target_email_address = $logged_info->email_address; @@ -531,65 +577,96 @@ class ncenterliteController extends ncenterlite { $act = Context::get('act'); // 팝업창이면 중지 - if(Context::get('ncenterlite_is_popup')) return; + if(Context::get('ncenterlite_is_popup')) + { + return; + } // 자신의 알림목록을 보고 있을 경우엔 알림센터창을 띄우지 않는다. - if($act == 'dispNcenterliteNotifyList') return; + if($act == 'dispNcenterliteNotifyList') + { + return; + } if(count($this->disable_notify_bar_act)) { - if(in_array(Context::get('act'), $this->disable_notify_bar_act)) return; + if(in_array(Context::get('act'), $this->disable_notify_bar_act)) + { + return; + } } // HTML 모드가 아니면 중지 + act에 admin이 포함되어 있으면 중지 - if(Context::getResponseMethod() != 'HTML' || strpos(strtolower(Context::get('act')), 'admin') !== false) return; + if(Context::getResponseMethod() != 'HTML' || strpos(strtolower(Context::get('act')), 'admin') !== false) + { + return; + } $logged_info = Context::get('logged_info'); // 로그인 상태가 아니면 중지 - if(!$logged_info) return; + if(!$logged_info) + { + return; + } $module_info = Context::get('module_info'); if(count($this->disable_notify_bar_mid)) { - if(in_array($module_info->mid, $this->disable_notify_bar_mid)) return; + if(in_array($module_info->mid, $this->disable_notify_bar_mid)) + { + return; + } } // admin 모듈이면 중지 - if($module_info->module == 'admin' ) return; + if($module_info->module == 'admin') + { + return; + } $oNcenterliteModel = getModel('ncenterlite'); $config = $oNcenterliteModel->getConfig(); // 알림센터가 비활성화 되어 있으면 중지 - if($config->use != 'Y') return new Object(); - if($config->display_use == 'N') return new Object(); + if($config->use != 'Y') + { + return new Object(); + } + if($config->display_use == 'N') + { + return new Object(); + } // 노티바 제외 페이지이면 중지 - if(in_array($module_info->module_srl, $config->hide_module_srls)) return new Object(); + if(in_array($module_info->module_srl, $config->hide_module_srls)) + { + return new Object(); + } Context::set('ncenterlite_config', $config); - $js_args = array( - './modules/ncenterlite/tpl/js/ncenterlite.js', - 'body', - '', - 100000 - ); + $js_args = array('./modules/ncenterlite/tpl/js/ncenterlite.js', 'body', '', 100000); Context::loadFile($js_args); $oNcenterliteModel = getModel('ncenterlite'); // 알림 목록 가져오기 $_output = $oNcenterliteModel->getMyNotifyList(); - if(!$_output->data) return; // 알림 메시지가 없어도 항상 표시하게 하려면 이 줄을 제거 또는 주석 처리하세요. + if(!$_output->data) + { + return; + } // 알림 메시지가 없어도 항상 표시하게 하려면 이 줄을 제거 또는 주석 처리하세요. $_latest_notify_id = array_slice($_output->data, 0, 1); $_latest_notify_id = $_latest_notify_id[0]->notify; Context::set('ncenterlite_latest_notify_id', $_latest_notify_id); - if($_COOKIE['_ncenterlite_hide_id'] && $_COOKIE['_ncenterlite_hide_id'] == $_latest_notify_id) return; + if($_COOKIE['_ncenterlite_hide_id'] && $_COOKIE['_ncenterlite_hide_id'] == $_latest_notify_id) + { + return; + } setcookie('_ncenterlite_hide_id', '', 0, '/'); $oMemberModel = getModel('member'); @@ -607,19 +684,19 @@ class ncenterliteController extends ncenterlite if(Mobile::isFromMobilePhone()) { $this->template_path = sprintf('%sm.skins/%s/', $this->module_path, $config->mskin); - if(!is_dir($this->template_path)||!$config->mskin) + if(!is_dir($this->template_path) || !$config->mskin) { $config->mskin = 'default'; - $this->template_path = sprintf('%sm.skins/%s/',$this->module_path, $config->mskin); + $this->template_path = sprintf('%sm.skins/%s/', $this->module_path, $config->mskin); } } else { $this->template_path = sprintf('%sskins/%s/', $this->module_path, $config->skin); - if(!is_dir($this->template_path)||!$config->skin) + if(!is_dir($this->template_path) || !$config->skin) { $config->skin = 'default'; - $this->template_path = sprintf('%sskins/%s/',$this->module_path, $config->skin); + $this->template_path = sprintf('%sskins/%s/', $this->module_path, $config->skin); } } @@ -636,16 +713,19 @@ class ncenterliteController extends ncenterlite if($config->user_config_list == 'Y') { $logged_info = Context::get('logged_info'); - if(!Context::get('is_logged')) return new Object(); + if(!Context::get('is_logged')) + { + return new Object(); + } $target_srl = Context::get('target_srl'); $oMemberController = getController('member'); $oMemberController->addMemberMenu('dispNcenterliteNotifyList', 'ncenterlite_my_list'); $oMemberController->addMemberMenu('dispNcenterliteUserConfig', 'ncenterlite_my_settings'); - if($logged_info->is_admin== 'Y') + if($logged_info->is_admin == 'Y') { - $url = getUrl('','act','dispNcenterliteUserConfig','member_srl',$target_srl); + $url = getUrl('', 'act', 'dispNcenterliteUserConfig', 'member_srl', $target_srl); $str = Context::getLang('ncenterlite_user_settings'); $oMemberController->addMemberPopupMenu($url, $str, ''); } @@ -669,14 +749,14 @@ class ncenterliteController extends ncenterlite { if($config->colorset && file_exists(FileHandler::getRealPath($this->template_path . 'ncenterlite.' . $config->colorset . '.css'))) { - Context::addCssFile($this->template_path . 'ncenterlite.'.$config->colorset.'.css', true, 'all', '', 100); + Context::addCssFile($this->template_path . 'ncenterlite.' . $config->colorset . '.css', true, 'all', '', 100); } } elseif(Mobile::isFromMobilePhone()) { if($config->mcolorset && file_exists(FileHandler::getRealPath($this->template_path . 'ncenterlite.' . $config->mcolorset . '.css'))) { - Context::addCssFile($this->template_path . 'ncenterlite.'.$config->mcolorset.'.css', true, 'all', '', 100); + Context::addCssFile($this->template_path . 'ncenterlite.' . $config->mcolorset . '.css', true, 'all', '', 100); } Context::loadFile(array('./common/js/jquery.min.js', 'head', '', -100000), true); @@ -745,7 +825,10 @@ class ncenterliteController extends ncenterlite function procNcenterliteNotifyReadAll() { $logged_info = Context::get('logged_info'); - if(!$logged_info) return new Object(-1, 'msg_invalid_request'); + if(!$logged_info) + { + return new Object(-1, 'msg_invalid_request'); + } $output = $this->updateNotifyReadAll($logged_info->member_srl); return $output; @@ -756,10 +839,16 @@ class ncenterliteController extends ncenterlite $logged_info = Context::get('logged_info'); $url = Context::get('url'); $notify = Context::get('notify'); - if(!$logged_info || !$url || !$notify) return new Object(-1, 'msg_invalid_request'); + if(!$logged_info || !$url || !$notify) + { + return new Object(-1, 'msg_invalid_request'); + } $output = $this->updateNotifyRead($notify, $logged_info->member_srl); - if(!$output->toBool()) return $output; + if(!$output->toBool()) + { + return $output; + } $url = str_replace('&', '&', $url); header('Location: ' . $url, TRUE, 302); @@ -774,23 +863,35 @@ class ncenterliteController extends ncenterlite function _isAnonymous($source_type, $triggerObj) { // 회원번호가 음수 - if($triggerObj->member_srl < 0) return TRUE; + if($triggerObj->member_srl < 0) + { + return TRUE; + } $module_info = Context::get('module_info'); // DX 익명 체크박스 - if($module_info->module == 'beluxe' && $triggerObj->anonymous == 'Y') return TRUE; + if($module_info->module == 'beluxe' && $triggerObj->anonymous == 'Y') + { + return TRUE; + } if($source_type == $this->_TYPE_COMMENT) { // DX 익명 강제 - if($module_info->module == 'beluxe' && $module_info->use_anonymous == 'Y') return TRUE; + if($module_info->module == 'beluxe' && $module_info->use_anonymous == 'Y') + { + return TRUE; + } } if($source_type == $this->_TYPE_DOCUMENT) { // DX 익명 강제 - if($module_info->module == 'beluxe' && $module_info->use_anonymous == 'Y') return TRUE; + if($module_info->module == 'beluxe' && $module_info->use_anonymous == 'Y') + { + return TRUE; + } } return FALSE; @@ -801,7 +902,10 @@ class ncenterliteController extends ncenterlite $oNcenterliteModel = getModel('ncenterlite'); $config = $oNcenterliteModel->getConfig(); // 비회원 노티 제거 - if($args->member_srl <= 0) return new Object(); + if($args->member_srl <= 0) + { + return new Object(); + } $logged_info = Context::get('logged_info'); @@ -858,7 +962,7 @@ class ncenterliteController extends ncenterlite if(!$trigger_notify->toBool()) { return $trigger_notify; - } + } } return $output; @@ -889,7 +993,10 @@ class ncenterliteController extends ncenterlite // 정규표현식 정리 $split = array(); - if(in_array('comma', $config->mention_format)) $split[] = ','; + if(in_array('comma', $config->mention_format)) + { + $split[] = ','; + } $regx = join('', array('/(^|\s)@([^@\s', join('', $split), ']+)/i')); preg_match_all($regx, $content, $matches); @@ -903,7 +1010,10 @@ class ncenterliteController extends ncenterlite if($pos !== false && $pos > 0) { $matches[2][$idx] = trim(substr($item, 0, $pos)); - if($logged_info && $logged_info->nick_name == $matches[2][$idx]) unset($matches[2][$idx]); + if($logged_info && $logged_info->nick_name == $matches[2][$idx]) + { + unset($matches[2][$idx]); + } } } } @@ -920,7 +1030,10 @@ class ncenterliteController extends ncenterlite $vars = new stdClass(); $vars->user_id = $user_id; $output = executeQuery('ncenterlite.getMemberSrlById', $vars); - if($output->data && $output->data->member_srl) $list[] = $output->data->member_srl; + if($output->data && $output->data->member_srl) + { + $list[] = $output->data->member_srl; + } } } else @@ -930,7 +1043,10 @@ class ncenterliteController extends ncenterlite $vars = new stdClass(); $vars->nick_name = $nick_name; $output = executeQuery('ncenterlite.getMemberSrlByNickName', $vars); - if($output->data && $output->data->member_srl) $list[] = $output->data->member_srl; + if($output->data && $output->data->member_srl) + { + $list[] = $output->data->member_srl; + } } } From 8e39e77db75e489a45f35d05121969bc7fd6a7f5 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Thu, 17 Mar 2016 07:55:08 +0900 Subject: [PATCH 131/205] Fixed the position of the comment --- modules/ncenterlite/ncenterlite.controller.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/ncenterlite/ncenterlite.controller.php b/modules/ncenterlite/ncenterlite.controller.php index f8368eb9f..ae9928f8e 100644 --- a/modules/ncenterlite/ncenterlite.controller.php +++ b/modules/ncenterlite/ncenterlite.controller.php @@ -654,10 +654,11 @@ class ncenterliteController extends ncenterlite // 알림 목록 가져오기 $_output = $oNcenterliteModel->getMyNotifyList(); + // 알림 메시지가 없어도 항상 표시하게 하려면 이 줄을 제거 또는 주석 처리하세요. if(!$_output->data) { return; - } // 알림 메시지가 없어도 항상 표시하게 하려면 이 줄을 제거 또는 주석 처리하세요. + } $_latest_notify_id = array_slice($_output->data, 0, 1); $_latest_notify_id = $_latest_notify_id[0]->notify; From e3e6a3d8f4907658003011052905ec790892a8f5 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Thu, 17 Mar 2016 08:02:44 +0900 Subject: [PATCH 132/205] fix typo --- modules/ncenterlite/ncenterlite.controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ncenterlite/ncenterlite.controller.php b/modules/ncenterlite/ncenterlite.controller.php index ae9928f8e..c7bc678ab 100644 --- a/modules/ncenterlite/ncenterlite.controller.php +++ b/modules/ncenterlite/ncenterlite.controller.php @@ -820,7 +820,7 @@ class ncenterliteController extends ncenterlite $output = executeQuery('ncenterlite.updateNotifyReadedAll', $args); //$output = executeQuery('ncenterlite.deleteNotifyByMemberSrl', $args); - return $ouptut; + return $output; } function procNcenterliteNotifyReadAll() From d2960ddb9f52335e427223ae632cad703b7b2926 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Thu, 17 Mar 2016 08:10:07 +0900 Subject: [PATCH 133/205] trigger ModuleObject deleted. --- modules/ncenterlite/ncenterlite.class.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/modules/ncenterlite/ncenterlite.class.php b/modules/ncenterlite/ncenterlite.class.php index a622e9384..ccf8564a4 100644 --- a/modules/ncenterlite/ncenterlite.class.php +++ b/modules/ncenterlite/ncenterlite.class.php @@ -43,6 +43,9 @@ class ncenterlite extends ModuleObject array('document.updateVotedCount', 'ncenterlite', 'controller', 'triggerAfterVotedupdate', 'after'), array('moduleHandler.init', 'ncenterlite', 'controller', 'triggerAddMemberMenu', 'after'), ); + private $delete_triggers = array( + array('moduleObject.proc', 'ncenterlite', 'controller', 'triggerBeforeModuleObjectProc', 'before') + ); function _isDisable() { @@ -71,6 +74,14 @@ class ncenterlite extends ModuleObject if(!$oModuleModel->getTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4])) return true; } + foreach($this->delete_triggers as $trigger) + { + if($oModuleModel->getTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4])) + { + return true; + } + } + if(!$oDB->isColumnExists('ncenterlite_notify', 'readed')) { return true; @@ -138,6 +149,14 @@ class ncenterlite extends ModuleObject } } + foreach($this->delete_triggers as $trigger) + { + if($oModuleModel->getTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4])) + { + $oModuleController->deleteTrigger($trigger[0], $trigger[1], $trigger[2], $trigger[3], $trigger[4]); + } + } + if(!$oDB->isColumnExists('ncenterlite_notify','readed')) { $oDB->addColumn('ncenterlite_notify', 'readed', 'char', 1, 'N', true); From 5d13c67caec0269db916a68f3bb0384c3c24b72a Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 17 Mar 2016 13:29:35 +0900 Subject: [PATCH 134/205] Merge Lang::getPluginLang() back into Lang::loadDirectory() --- common/framework/lang.php | 98 ++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 58 deletions(-) diff --git a/common/framework/lang.php b/common/framework/lang.php index e2cfb49be..a08e0ce09 100644 --- a/common/framework/lang.php +++ b/common/framework/lang.php @@ -108,70 +108,52 @@ class Lang return true; } - // Load the language file. - $lang = $this->getPluginLang($dir); + // Initialize variables. + $filename = null; + $lang = new \stdClass; + $result = true; - // Load the default language file. + // Find a suitable language file in the given directory. + if (file_exists($dir . '/' . $this->_language . '.php')) + { + $filename = $dir . '/' . $this->_language . '.php'; + } + elseif (($hyphen = strpos($this->_language, '-')) !== false && file_exists($dir . '/' . substr($this->_language, 0, $hyphen) . '.php')) + { + $filename = $dir . '/' . substr($this->_language, 0, $hyphen) . '.php'; + } + elseif (file_exists("$dir/lang.xml")) + { + $filename = Parsers\LangParser::compileXMLtoPHP("$dir/lang.xml", $this->_language === 'ja' ? 'jp' : $this->_language); + } + elseif (file_exists($dir . '/' . ($this->_language === 'ja' ? 'jp' : $this->_language) . '.lang.php')) + { + $filename = $dir . '/' . ($this->_language === 'ja' ? 'jp' : $this->_language) . '.lang.php'; + } + + // Load the language file. + if ($filename) + { + include $filename; + array_unshift($this->_search_priority, $plugin_name); + $result = true; + } + else + { + $result = false; + } + + // Mark this directory and plugin as loaded. + $this->_loaded_directories[$dir] = true; + $this->_loaded_plugins[$plugin_name] = $lang; + + // Load the same directory in the default language, too. if ($this->_language !== 'en') { self::getInstance('en')->loadDirectory($dir, $plugin_name); } - if (!empty($lang)) - { - $this->_loaded_directories[$dir] = true; - $this->_loaded_plugins[$plugin_name] = $lang; - array_unshift($this->_search_priority, $plugin_name); - return true; - } - else - { - $this->_loaded_directories[$dir] = true; - $this->_loaded_plugins[$plugin_name] = new \stdClass; - return false; - } - } - - /** - * Get the language file from plugin. - * - * @param string $dir - * @param string $language - * @return object - */ - public function getPluginLang($dir, $language = null) - { - if (!$language) - { - $language = $this->_language; - } - - if (file_exists($dir . '/' . $language . '.php')) - { - $filename = $dir . '/' . $language . '.php'; - } - elseif (($hyphen = strpos($language, '-')) !== false && file_exists($dir . '/' . substr($language, 0, $hyphen) . '.php')) - { - $filename = $dir . '/' . substr($language, 0, $hyphen) . '.php'; - } - elseif (file_exists("$dir/lang.xml")) - { - $filename = Parsers\LangParser::compileXMLtoPHP("$dir/lang.xml", $language === 'ja' ? 'jp' : $language); - } - elseif (file_exists($dir . '/' . ($language === 'ja' ? 'jp' : $language) . '.lang.php')) - { - $filename = $dir . '/' . ($language === 'ja' ? 'jp' : $language) . '.lang.php'; - } - - if (!$filename) - { - return new \stdClass; - } - - $lang = new \stdClass; - include $filename; - - return $lang; + return $result; } /** From ece681bda6fbf78cd4a613ffea4345c47b7191e1 Mon Sep 17 00:00:00 2001 From: BJRambo Date: Thu, 17 Mar 2016 13:47:28 +0900 Subject: [PATCH 135/205] revised language file info Rhymix form. --- modules/ncenterlite/lang/de.php | 1 + modules/ncenterlite/lang/en.php | 102 +++++++ modules/ncenterlite/lang/es.php | 1 + modules/ncenterlite/lang/fr.php | 1 + modules/ncenterlite/lang/ja.php | 70 +++++ modules/ncenterlite/lang/ko.php | 102 +++++++ modules/ncenterlite/lang/lang.xml | 458 ----------------------------- modules/ncenterlite/lang/mn.php | 1 + modules/ncenterlite/lang/ru.php | 1 + modules/ncenterlite/lang/tr.php | 1 + modules/ncenterlite/lang/vi.php | 1 + modules/ncenterlite/lang/zh-CN.php | 1 + modules/ncenterlite/lang/zh-TW.php | 1 + 13 files changed, 283 insertions(+), 458 deletions(-) create mode 100644 modules/ncenterlite/lang/de.php create mode 100644 modules/ncenterlite/lang/en.php create mode 100644 modules/ncenterlite/lang/es.php create mode 100644 modules/ncenterlite/lang/fr.php create mode 100644 modules/ncenterlite/lang/ja.php create mode 100644 modules/ncenterlite/lang/ko.php delete mode 100644 modules/ncenterlite/lang/lang.xml create mode 100644 modules/ncenterlite/lang/mn.php create mode 100644 modules/ncenterlite/lang/ru.php create mode 100644 modules/ncenterlite/lang/tr.php create mode 100644 modules/ncenterlite/lang/vi.php create mode 100644 modules/ncenterlite/lang/zh-CN.php create mode 100644 modules/ncenterlite/lang/zh-TW.php diff --git a/modules/ncenterlite/lang/de.php b/modules/ncenterlite/lang/de.php new file mode 100644 index 000000000..b3d9bbc7f --- /dev/null +++ b/modules/ncenterlite/lang/de.php @@ -0,0 +1 @@ +ncenterlite = 'Rhymix Notification Center Lite'; +$lang->ncenterlite_install_version = 'Installed Notification Center Lite version'; +$lang->ncenterlite_document = 'document'; +$lang->ncenterlite_comment = 'comment'; +$lang->ncenterlite_type_message = 'message'; +$lang->ncenterlite_sender = 'Sender'; +$lang->ncenterlite_addressee = 'Addressee'; +$lang->ncenterlite_noti_contents = 'Contents'; +$lang->ncenterlite_read = 'Have read'; +$lang->ncenterlite_read_y = 'Have read'; +$lang->ncenterlite_read_n = 'Not read'; +$lang->ncenterlite_no_target = 'no target'; +$lang->ncenterlite_my_list = 'My notification list'; +$lang->ncenterlite_my_settings = 'My notification settings'; +$lang->ncenterlite_user_settings = 'User notification settings'; +$lang->ncenterlite_userconfig_title = 'Notification Center Settings of %s'; +$lang->ncenterlite_userconfig_about = 'Personalized settings of notification center can be controlled by you.'; +$lang->ncenterlite_comment_noti = 'Comment notice'; +$lang->ncenterlite_comment_noti_about = 'Get notice of a comment after someone replies to my documents or comments.'; +$lang->ncenterlite_mention_noti = 'Mention notice'; +$lang->ncenterlite_mention_noti_about = 'Get notice of a mention after someone mention me on documents and/or comments. ( @Nickname to mention )'; +$lang->ncenterlite_message_noti = 'Message notice'; +$lang->ncenterlite_message_noti_about = 'Get notice of a message after someone send the message to me.'; +$lang->ncenterlite_activate = 'Activate'; +$lang->ncenterlite_inactivate = 'Inactivate'; +$lang->ncenterlite_userconfig_about_warning = 'Watch out! You are controlling other user\'s settings via this page.'; +$lang->ncenterlite_article = '%1$s wrote an article as "%2$s".'; +$lang->ncenterlite_board = '%1$s wrote an article as "%3$s" on the board %2$s.'; +$lang->ncenterlite_commented = '%1$s commented as "%3$s" on your %2$s.'; +$lang->ncenterlite_commented_board = '%1$s commented as "%3$s" on the board %2$s.'; +$lang->ncenterlite_mentioned = '%s mentioned you on "%s" %s.'; +$lang->ncenterlite_message_string = 'You have %d new message(s).'; +$lang->ncenterlite_message_mention = '%1$s sent a message as "%2$s".'; +$lang->ncenterlite_test_noti = '%s! Skin test notification has been done.'; +$lang->ncenterlite_vote = '%1$s marked the article "%2$s" with a recommendation.'; +$lang->ncenterlite_ago = 'ago'; +$lang->ncenterlite_date['0'] = 'Year'; +$lang->ncenterlite_date['1'] = 'Month'; +$lang->ncenterlite_date['2'] = 'Day'; +$lang->ncenterlite_date['3'] = 'Hour'; +$lang->ncenterlite_date['4'] = 'Minute'; +$lang->ncenterlite_date['5'] = 'Second'; +$lang->ncenterlite_sir = ' '; +$lang->ncenterlite_message = 'You have %s notification.'; +$lang->ncenterlite_messages = 'you have %s notifications.'; +$lang->ncenterlite_thisistest = '[*] This is a test notice.'; +$lang->ncenterlite_delete_all = 'delete all'; +$lang->ncenterlite_more = 'More'; +$lang->ncenterlite_stop_login_required = 'Sign in to control your own notification settings.'; +$lang->ncenterlite_stop_no_permission_other_user = 'You don\'t have the authority to read settings of other members.'; +$lang->ncenterlite_stop_no_permission_other_user_settings = 'You don\'t have the authority to control settings of other members.'; +$lang->ncenterlite_message_delete_notification_before = 'Notifications before %s are deleted.'; +$lang->ncenterlite_message_delete_notification_all = 'Every notification is deleted.'; +$lang->ncenterlite_config_environment = 'HELP! Agreement on gathering server environment information'; +$lang->ncenterlite_config_environment_about = ' +

\'Notification Center Lite\' is distributed as an free software.

+

\'Notification Center Lite\' is developing with collective intelligence, such as users\' bug reports, suggestions and/or contributions.
If you select to \'Agree on gathering\', the items below will be gathered by developers of this program, and the information will be referenced to enhance functions of this program. Thank you.

+
    +
  • Domain name of this website
  • +
  • Version value of Notification Center Lite
  • +
  • XE Core version value
  • +
  • PHP version value
  • +
+ '; +$lang->ncenterlite_config_environment_agree = 'Agree on gathering'; +$lang->ncenterlite_config_environment_disagree = 'Disagree on gathering'; +$lang->ncenterlite_click_to_open = 'Click here to open'; +$lang->ncenterlite_notice_list = 'Notification list'; +$lang->ncenterlite_basic_settings = 'Basic function settings'; +$lang->ncenterlite_warning = 'Watch out!'; +$lang->ncenterlite_io = 'Activate Notification Center'; +$lang->ncenterlite_io_about = 'You can activate or inactivate every function of Notification Center Lite module.'; +$lang->ncenterlite_on = 'Active'; +$lang->ncenterlite_off = 'Inactive'; +$lang->ncenterlite_display = 'Display Notification Center'; +$lang->ncenterlite_display_y = 'Display'; +$lang->ncenterlite_display_n = 'Hide'; +$lang->ncenterlite_display_about = '알림센터의 스킨을 사용할 것인지 혹은 위젯 레이아웃에서 제작되어 들어간 스킨을 사용할지 선택합니다. 기본은 알림센터모듈의 기본스킨을 사용하도록 합니다. + Watch out! 알림센터를 레이아웃이나 위젯으로 표현할 수 있을 때에만 동작안함 선택하세요.'; +$lang->ncenterlite_mention_target = 'Mention target'; +$lang->ncenterlite_mention_target_about = 'Mention target can be nicknames or IDs (@Nickname or @ID).'; +$lang->ncenterlite_skin_settings = 'Skin settings'; +$lang->ncenterlite_zindex = 'Z-index of notification'; +$lang->ncenterlite_zindex_about = 'Some elements that have high z-index can hide notification layer. If so, set z-index of notification layer as 100, 200, and so on to show it.'; +$lang->ncenterlite_test_make_dummy = 'Make dummy data'; +$lang->ncenterlite_test_mention = 'Web page notification test'; +$lang->ncenterlite_test_mention_about = 'Create dummy data for module and/or skin test.'; +$lang->ncenterlite_test_push = 'Push notification test'; +$lang->ncenterlite_test_push_about = 'Create dummy data for mobile push notification test.'; +$lang->ncenterlite_document_event_settings = 'Document event notification'; +$lang->ncenterlite_document_event_vote = 'Recommendation'; +$lang->ncenterlite_document_event_vote_about = 'When someone recommend document, the author of it can get notifying.'; +$lang->ncenterlite_document_event_read = 'Delete notifying after read the article'; +$lang->ncenterlite_document_event_read_preserve = 'Preserve notification'; +$lang->ncenterlite_document_event_read_delete = 'Delete notification'; +$lang->ncenterlite_document_event_read_about = 'Delete every notification after read the article. Default is do not delete (preserve).'; +$lang->ncenterlite_commnet_event = 'Comments'; +$lang->ncenterlite_commnet_event_noti_all = 'Notice every comments to the author'; +$lang->ncenterlite_commnet_event_noti_some = 'Notice only direct replies to the author'; +$lang->ncenterlite_message_event = 'Notify message'; +$lang->ncenterlite_message_event_about = 'Do not notify message (Use XE Core message notification system).'; diff --git a/modules/ncenterlite/lang/es.php b/modules/ncenterlite/lang/es.php new file mode 100644 index 000000000..b3d9bbc7f --- /dev/null +++ b/modules/ncenterlite/lang/es.php @@ -0,0 +1 @@ +ncenterlite = 'Rhymix 通知センター Lite'; +$lang->ncenterlite_install_version = 'Installed Rhymix 通知センター Lite version'; +$lang->ncenterlite_document = '書き込み'; +$lang->ncenterlite_comment = 'コメント'; +$lang->ncenterlite_type_message = 'メッセージ'; +$lang->ncenterlite_sender = 'Sender'; +$lang->ncenterlite_addressee = 'Addressee'; +$lang->ncenterlite_noti_contents = 'Contents'; +$lang->ncenterlite_read = 'Have read'; +$lang->ncenterlite_read_y = 'Have read'; +$lang->ncenterlite_read_n = 'Not read'; +$lang->ncenterlite_no_target = 'no target'; +$lang->ncenterlite_my_list = 'My notification list'; +$lang->ncenterlite_my_settings = 'My notification settings'; +$lang->ncenterlite_user_settings = 'User notification settings'; +$lang->ncenterlite_userconfig_title = 'Notification Center Settings of %s'; +$lang->ncenterlite_userconfig_about = 'Personalized settings of notification center can be controlled by you.'; +$lang->ncenterlite_comment_noti = 'Comment notice'; +$lang->ncenterlite_comment_noti_about = 'Get notice of a comment after someone replies to my documents or comments.'; +$lang->ncenterlite_mention_noti = 'Mention notice'; +$lang->ncenterlite_mention_noti_about = 'Get notice of a mention after someone mention me on documents and/or comments. ( @Nickname to mention )'; +$lang->ncenterlite_message_noti = 'Message notice'; +$lang->ncenterlite_message_noti_about = 'Get notice of a message after someone send the message to me.'; +$lang->ncenterlite_activate = 'Activate'; +$lang->ncenterlite_inactivate = 'Inactivate'; +$lang->ncenterlite_userconfig_about_warning = 'Watch out! You are controlling other user\'s settings via this page.'; +$lang->ncenterlite_article = '%1$s wrote an article as "%2$s".'; +$lang->ncenterlite_board = '%1$s wrote an article as "%3$s" on the board %2$s.'; +$lang->ncenterlite_commented = '%sさんがあなたの%sに「%s」とコメントしました。'; +$lang->ncenterlite_commented_board = '%1$s commented as "%3$s" on the board %2$s.'; +$lang->ncenterlite_mentioned = '%sさんが「%s」%sにあなたをタグ付けしました。'; +$lang->ncenterlite_message_string = '%d件の読んでないメッセージがあります。'; +$lang->ncenterlite_message_mention = '%1$s sent a message as "%2$s".'; +$lang->ncenterlite_test_noti = '%s! Skin test notification has been done.'; +$lang->ncenterlite_vote = '%1$s marked the article "%2$s" with a recommendation.'; +$lang->ncenterlite_ago = '前'; +$lang->ncenterlite_date['0'] = '年'; +$lang->ncenterlite_date['1'] = 'ヶ月'; +$lang->ncenterlite_date['2'] = '日'; +$lang->ncenterlite_date['3'] = '時間'; +$lang->ncenterlite_date['4'] = '分'; +$lang->ncenterlite_date['5'] = '秒'; +$lang->ncenterlite_sir = 'さん'; +$lang->ncenterlite_message = '%s件のお知らせがあります。'; +$lang->ncenterlite_messages = '%s件のお知らせがあります。'; +$lang->ncenterlite_thisistest = '[*] This is a test notice.'; +$lang->ncenterlite_delete_all = 'すべて削除'; +$lang->ncenterlite_more = 'もっと見る'; +$lang->ncenterlite_stop_login_required = 'Sign in to control your own notification settings.'; +$lang->ncenterlite_stop_no_permission_other_user = 'You don\'t have the authority to read settings of other members.'; +$lang->ncenterlite_stop_no_permission_other_user_settings = 'You don\'t have the authority to control settings of other members.'; +$lang->ncenterlite_message_delete_notification_before = 'Notifications before %s are deleted.'; +$lang->ncenterlite_message_delete_notification_all = 'Every notification is deleted.'; +$lang->ncenterlite_config_environment = 'HELP! Agreement on gathering server environment information'; +$lang->ncenterlite_config_environment_about = ' +

\'Notification Center Lite\' is distributed as an free software.

+

\'Notification Center Lite\' is developing with collective intelligence, such as users\' bug reports, suggestions and/or contributions.
If you select to \'Agree on gathering\', the items below will be gathered by developers of this program, and the information will be referenced to enhance functions of this program. Thank you.

+
    +
  • Domain name of this website
  • +
  • Version value of Notification Center Lite
  • +
  • XE Core version value
  • +
  • PHP version value
  • +
+ '; +$lang->ncenterlite_config_environment_agree = 'Agree on gathering'; +$lang->ncenterlite_config_environment_disagree = 'Disagree on gathering'; +$lang->ncenterlite_click_to_open = 'Click here to open'; +$lang->ncenterlite_notice_list = 'Notification list'; +$lang->ncenterlite_basic_settings = 'Basic function settings'; diff --git a/modules/ncenterlite/lang/ko.php b/modules/ncenterlite/lang/ko.php new file mode 100644 index 000000000..0af3cc4bf --- /dev/null +++ b/modules/ncenterlite/lang/ko.php @@ -0,0 +1,102 @@ +ncenterlite = 'Rhymix 알림센터 Lite'; +$lang->ncenterlite_install_version = '설치된 알림센터 Lite 버전'; +$lang->ncenterlite_document = '글'; +$lang->ncenterlite_comment = '댓글'; +$lang->ncenterlite_type_message = '쪽지'; +$lang->ncenterlite_sender = '보낸 사람'; +$lang->ncenterlite_addressee = '받는 사람'; +$lang->ncenterlite_noti_contents = '내용'; +$lang->ncenterlite_read = '읽음 확인'; +$lang->ncenterlite_read_y = '읽음'; +$lang->ncenterlite_read_n = '읽지 않음'; +$lang->ncenterlite_no_target = '대상 없음'; +$lang->ncenterlite_my_list = '내 알림 목록'; +$lang->ncenterlite_my_settings = '내 알림 설정'; +$lang->ncenterlite_user_settings = '사용자 알림 설정'; +$lang->ncenterlite_userconfig_title = '%s님의 알림센터 설정'; +$lang->ncenterlite_userconfig_about = '알림센터의 개인의 설정을 저장하도록 합니다.'; +$lang->ncenterlite_comment_noti = '댓글 알림'; +$lang->ncenterlite_comment_noti_about = '내 게시물의 혹은 내 댓글에 댓글이 달릴경우 알림을 받습니다.'; +$lang->ncenterlite_mention_noti = '멘션 알림'; +$lang->ncenterlite_mention_noti_about = '누군가 글, 혹은 댓글을 통해서 나를 맨션 했을 경우 알려줍니다. (맨션 방법 @닉네임 )'; +$lang->ncenterlite_message_noti = '쪽지 알림'; +$lang->ncenterlite_message_noti_about = '누군가에게 받은 쪽지를 알림을 받습니다.'; +$lang->ncenterlite_activate = '사용'; +$lang->ncenterlite_inactivate = '사용 안함'; +$lang->ncenterlite_userconfig_about_warning = '주의! 당신은 관리자 권한으로 다른 사용자의 설정창을 접속하였습니다.'; +$lang->ncenterlite_article = '%1$s님이 "%2$s"라고 글을 남겼습니다.'; +$lang->ncenterlite_board = '%1$s님이 게시판 "%2$s""%3$s"라고 글을 남겼습니다.'; +$lang->ncenterlite_commented = '%1$s님이 회원님의 %2$s에 "%3$s"라고 댓글을 남겼습니다.'; +$lang->ncenterlite_commented_board = '%1$s님이 게시판 "%2$s""%3$s"라고 댓글을 남겼습니다.'; +$lang->ncenterlite_mentioned = '%s님이 "%s" %s에서 회원님을 언급하였습니다.'; +$lang->ncenterlite_message_string = '%d개의 읽지 않은 메시지가 있습니다.'; +$lang->ncenterlite_message_mention = '%s님께서 "%s"라고 메세지를 보내셨습니다.'; +$lang->ncenterlite_test_noti = '%s님! 스킨 테스트 알림을 완료했습니다.'; +$lang->ncenterlite_vote = '%s님이 "%s" 글을 추천하였습니다.'; +$lang->ncenterlite_ago = '전'; +$lang->ncenterlite_date['0'] = '년'; +$lang->ncenterlite_date['1'] = '개월'; +$lang->ncenterlite_date['2'] = '일'; +$lang->ncenterlite_date['3'] = '시간'; +$lang->ncenterlite_date['4'] = '분'; +$lang->ncenterlite_date['5'] = '초'; +$lang->ncenterlite_sir = '님'; +$lang->ncenterlite_message = '%s개의 알림이 있습니다.'; +$lang->ncenterlite_messages = '%s개의 알림이 있습니다.'; +$lang->ncenterlite_thisistest = '[*] 시험용 알림입니다'; +$lang->ncenterlite_delete_all = '모두 삭제'; +$lang->ncenterlite_more = '더보기'; +$lang->ncenterlite_stop_login_required = '알림센터 설정을 하시려면 로그인 해주세요.'; +$lang->ncenterlite_stop_no_permission_other_user = '다른 회원의 설정을 볼 권한이 없습니다.'; +$lang->ncenterlite_stop_no_permission_other_user_settings = '다른 회원의 설정을 변경할 권한이 없습니다.'; +$lang->ncenterlite_message_delete_notification_before = '%s까지 알림 정보를 삭제했습니다.'; +$lang->ncenterlite_message_delete_notification_all = '모든 알림을 삭제했습니다.'; +$lang->ncenterlite_config_environment = '도와주세요! 사용 환경 정보 제공 동의'; +$lang->ncenterlite_config_environment_about = ' +

\'알림센터 Lite\'를 자유소프트웨어로 제공하고 있습니다.

+

\'알림센터 Lite\'는 사용자의 버그 보고, 의견 제출을 통해 공동의 힘으로 발전하고 있습니다.
\'제공 동의\'를 선택하면 아래와 같은 항목이 전송되며, 알림센터 Lite의 기능을 개선하는데 참고하겠습니다. 감사합니다.

+
    +
  • 이 사이트의 도메인
  • +
  • 알림센터 Lite의 버전
  • +
  • XE 버전
  • +
  • PHP 버전
  • +
+ '; +$lang->ncenterlite_config_environment_agree = '제공 동의'; +$lang->ncenterlite_config_environment_disagree = '제공 안 함'; +$lang->ncenterlite_click_to_open = '클릭하시면 기능설정을 할 수 있습니다.'; +$lang->ncenterlite_notice_list = '알림 목록'; +$lang->ncenterlite_basic_settings = '기본 기능 설정'; +$lang->ncenterlite_warning = '주의!'; +$lang->ncenterlite_io = '알림센터 동작여부'; +$lang->ncenterlite_io_about = '알림센터의 모든 기능을 중지 시킬 수 있습니다.'; +$lang->ncenterlite_on = '동작'; +$lang->ncenterlite_off = '동작 안 함'; +$lang->ncenterlite_display = '알림센터 노출여부'; +$lang->ncenterlite_display_y = '노출'; +$lang->ncenterlite_display_n = '숨김'; +$lang->ncenterlite_display_about = '알림센터의 스킨을 사용할 것인지 혹은 위젯 레이아웃에서 제작되어 들어간 스킨을 사용할지 선택합니다. 기본은 알림센터모듈의 기본스킨을 사용하도록 합니다. + 주의! 알림센터를 레이아웃이나 위젯으로 표현할 수 있을 때에만 동작안함 선택하세요.'; +$lang->ncenterlite_mention_target = '멘션 타겟 설정'; +$lang->ncenterlite_mention_target_about = '알림센터에서 닉네임 혹은 아이디 선택하여 맨션설정을 할 수 있습니다.'; +$lang->ncenterlite_skin_settings = '스킨 설정'; +$lang->ncenterlite_zindex = '알림센터의 z-index'; +$lang->ncenterlite_zindex_about = '알림센터가 다른 요소에 가려지는 경우에 100, 200, ... 이상으로 높여 보세요.'; +$lang->ncenterlite_test_make_dummy = '더미 데이터 생성'; +$lang->ncenterlite_test_mention = '시험용 스킨 알림 생성'; +$lang->ncenterlite_test_mention_about = '스킨 및 모듈 테스트를 위한 시험용 알림 생성.'; +$lang->ncenterlite_test_push = '시험용 푸시 알림 생성'; +$lang->ncenterlite_test_push_about = '모듈 및 모바일 테스트를 위한 시험용 알림 생성'; +$lang->ncenterlite_document_event_settings = '새 글 알림 및 문서알림'; +$lang->ncenterlite_document_event_vote = '글 추천 알림'; +$lang->ncenterlite_document_event_vote_about = '글이 추천받았을때 추천알림을 받을 수 있습니다.'; +$lang->ncenterlite_document_event_read = '게시판 읽을경우 관련알림 삭제'; +$lang->ncenterlite_document_event_read_preserve = '알림을 지우지 않음'; +$lang->ncenterlite_document_event_read_delete = '알림을 지움'; +$lang->ncenterlite_document_event_read_about = '게시판을 읽을경우 해당 글의 포함된 모든 알림내역을 알림내역에서 삭제 합니다. 기본값은 알림을 보존합니다.'; +$lang->ncenterlite_commnet_event = '댓글 알림 설정'; +$lang->ncenterlite_commnet_event_noti_all = '글쓴이에게 모든 댓글 알림'; +$lang->ncenterlite_commnet_event_noti_some = '대댓글은 알리지 않음'; +$lang->ncenterlite_message_event = '쪽지 알림 설정'; +$lang->ncenterlite_message_event_about = '쪽지 알림을 사용하지 않음(XE의 기본 쪽지 알림을 사용)'; diff --git a/modules/ncenterlite/lang/lang.xml b/modules/ncenterlite/lang/lang.xml deleted file mode 100644 index 4e2e080f0..000000000 --- a/modules/ncenterlite/lang/lang.xml +++ /dev/null @@ -1,458 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %1$s님이 "%2$s"라고 글을 남겼습니다.]]> - %1$s wrote an article as "%2$s".]]> - %1$s wrote an article as "%2$s".]]> - - - %1$s님이 게시판 "%2$s""%3$s"라고 글을 남겼습니다.]]> - %1$s wrote an article as "%3$s" on the board %2$s.]]> - %1$s wrote an article as "%3$s" on the board %2$s.]]> - - - %1$s님이 회원님의 %2$s에 "%3$s"라고 댓글을 남겼습니다.]]> - %1$s commented as "%3$s" on your %2$s.]]> - %sさんがあなたの%sに「%s」とコメントしました。]]> - - - %1$s님이 게시판 "%2$s""%3$s"라고 댓글을 남겼습니다.]]> - %1$s commented as "%3$s" on the board %2$s.]]> - %1$s commented as "%3$s" on the board %2$s.]]> - - - %s님이 "%s" %s에서 회원님을 언급하였습니다.]]> - %s mentioned you on "%s" %s.]]> - %sさんが「%s」%sにあなたをタグ付けしました。]]> - - - %d개의 읽지 않은 메시지가 있습니다.]]> - %d new message(s).]]> - %d件の読んでないメッセージがあります。]]> - - - %s님께서 "%s"라고 메세지를 보내셨습니다.]]> - %1$s sent a message as "%2$s".]]> - %1$s sent a message as "%2$s".]]> - - - %s님! 스킨 테스트 알림을 완료했습니다.]]> - %s! Skin test notification has been done.]]> - %s! Skin test notification has been done.]]> - - - %s님이 "%s" 글을 추천하였습니다.]]> - %1$s marked the article "%2$s" with a recommendation.]]> - %1$s marked the article "%2$s" with a recommendation.]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %s개의 알림이 있습니다.]]> - %s notification.]]> - %s件のお知らせがあります。]]> - - - %s개의 알림이 있습니다.]]> - %s notifications.]]> - %s件のお知らせがあります。]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 도와주세요! 사용 환경 정보 제공 동의]]> - HELP! Agreement on gathering server environment information]]> - HELP! Agreement on gathering server environment information]]> - - - '알림센터 Lite'를 자유소프트웨어로 제공하고 있습니다.

-

'알림센터 Lite'는 사용자의 버그 보고, 의견 제출을 통해 공동의 힘으로 발전하고 있습니다.
'제공 동의'를 선택하면 아래와 같은 항목이 전송되며, 알림센터 Lite의 기능을 개선하는데 참고하겠습니다. 감사합니다.

-
    -
  • 이 사이트의 도메인
  • -
  • 알림센터 Lite의 버전
  • -
  • XE 버전
  • -
  • PHP 버전
  • -
- ]]>
- 'Notification Center Lite' is distributed as an free software.

-

'Notification Center Lite' is developing with collective intelligence, such as users' bug reports, suggestions and/or contributions.
If you select to 'Agree on gathering', the items below will be gathered by developers of this program, and the information will be referenced to enhance functions of this program. Thank you.

-
    -
  • Domain name of this website
  • -
  • Version value of Notification Center Lite
  • -
  • XE Core version value
  • -
  • PHP version value
  • -
- ]]>
- 'Notification Center Lite' is distributed as an free software.

-

'Notification Center Lite' is developing with collective intelligence, such as users' bug reports, suggestions and/or contributions.
If you select to 'Agree on gathering', the items below will be gathered by developers of this program, and the information will be referenced to enhance functions of this program. Thank you.

-
    -
  • Domain name of this website
  • -
  • Version value of Notification Center Lite
  • -
  • XE Core version value
  • -
  • PHP version value
  • -
- ]]>
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 주의! 알림센터를 레이아웃이나 위젯으로 표현할 수 있을 때에만 동작안함 선택하세요.]]> - Watch out! 알림센터를 레이아웃이나 위젯으로 표현할 수 있을 때에만 동작안함 선택하세요.]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
\ No newline at end of file diff --git a/modules/ncenterlite/lang/mn.php b/modules/ncenterlite/lang/mn.php new file mode 100644 index 000000000..b3d9bbc7f --- /dev/null +++ b/modules/ncenterlite/lang/mn.php @@ -0,0 +1 @@ + Date: Thu, 17 Mar 2016 13:58:57 +0900 Subject: [PATCH 136/205] Refactor default language search routine for better performance --- common/framework/lang.php | 111 +++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 54 deletions(-) diff --git a/common/framework/lang.php b/common/framework/lang.php index a08e0ce09..ec47de9ad 100644 --- a/common/framework/lang.php +++ b/common/framework/lang.php @@ -191,6 +191,24 @@ class Lang $this->__set($key, $value); } + /** + * Fallback method for getting the default translation. + * + * @param string $key + * @return string + */ + public function getFromDefaultLang($key) + { + if ($this->_language === 'en') + { + return $key; + } + else + { + return self::getInstance('en')->__get($key); + } + } + /** * Magic method for translations without arguments. * @@ -199,45 +217,58 @@ class Lang */ public function __get($key) { - // Get default language - if ($this->_language !== 'en') + // Load a dot-separated key (prefixed by plugin name). + if (preg_match('/^[a-z0-9_.-]+$/i', $key) && ($keys = explode('.', $key)) && count($keys) >= 2) { - $lang_en = self::getInstance('en')->{$key}; - } - - // Separate the plugin name from the key. - if (preg_match('/^[a-z0-9_.-]+$/i', $key) && ($keys = explode('.', $key, 2)) && count($keys) === 2) - { - list($plugin_name, $lang_key) = $keys; + // Attempt to load the plugin. + $plugin_name = array_shift($keys); if (!isset($this->_loaded_plugins[$plugin_name])) { $this->loadPlugin($plugin_name); } - - if (isset($this->_loaded_plugins[$plugin_name]->{$lang_key})) + if (!isset($this->_loaded_plugins[$plugin_name])) { - $lang = $this->_loaded_plugins[$plugin_name]->{$lang_key}; - if (is_array($lang) && is_array($lang_en) && count($lang_en, COUNT_RECURSIVE) > count($lang, COUNT_RECURSIVE)) + return $this->getFromDefaultLang($key); + } + + // Find the given key. + $lang = $this->_loaded_plugins[$plugin_name]; + foreach ($keys as $subkey) + { + if (isset($lang->{$subkey})) { - return $lang_en; + $lang = is_scalar($lang->{$subkey}) ? $lang->{$subkey} : new \ArrayObject($lang->{$subkey}, 3); } - + else + { + return $this->getFromDefaultLang($key); + } + } + return $lang; + } + + // Search custom translations first. + if (isset($this->_loaded_plugins['_custom_']->{$key})) + { + $lang = $this->_loaded_plugins['_custom_']->{$key}; + if (is_array($lang)) + { + return new \ArrayObject($lang, 3); + } + else + { return $lang; } } - else + + // Search other plugins. + foreach ($this->_search_priority as $plugin_name) { - // Search custom translations first. - if (isset($this->_loaded_plugins['_custom_']->{$key})) + if (isset($this->_loaded_plugins[$plugin_name]->{$key})) { - $lang = $this->_loaded_plugins['_custom_']->{$key}; + $lang = $this->_loaded_plugins[$plugin_name]->{$key}; if (is_array($lang)) { - if (is_array($lang_en) && count($lang_en, COUNT_RECURSIVE) > count($lang, COUNT_RECURSIVE)) - { - return new \ArrayObject($lang_en, 3); - } - return new \ArrayObject($lang, 3); } else @@ -245,38 +276,10 @@ class Lang return $lang; } } - - // Search other plugins. - foreach ($this->_search_priority as $plugin_name) - { - if (isset($this->_loaded_plugins[$plugin_name]->{$key})) - { - $lang = $this->_loaded_plugins[$plugin_name]->{$key}; - if (is_array($lang)) - { - if (is_array($lang_en) && count($lang_en, COUNT_RECURSIVE) > count($lang, COUNT_RECURSIVE)) - { - return new \ArrayObject($lang_en, 3); - } - - return new \ArrayObject($lang, 3); - } - else - { - return $lang; - } - } - } } - // Search other language. - if (isset($lang_en)) - { - return $lang_en; - } - - // If no translation is found, return the key. - return $key; + // If no translation is found, return the default language. + return $this->getFromDefaultLang($key); } /** From 014e7f13a495f54fa0505dd68315d5c044b5ae0f Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 17 Mar 2016 14:11:33 +0900 Subject: [PATCH 137/205] Move string interpolation from __call() to get() --- common/framework/lang.php | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/common/framework/lang.php b/common/framework/lang.php index ec47de9ad..8366e715f 100644 --- a/common/framework/lang.php +++ b/common/framework/lang.php @@ -176,7 +176,19 @@ class Lang { $args = func_get_args(); array_shift($args); - return $this->__call($key, $args); + if (count($args) === 1 && is_array($args[0])) + { + $args = $args[0]; + } + + // Get the translation. + $translation = $this->__get($key); + + // If there are no arguments, return the translation. + if (!count($args)) return $translation; + + // If there are arguments, interpolate them into the translation and return the result. + return vsprintf($translation, $args); } /** @@ -338,16 +350,6 @@ class Lang */ public function __call($key, $args = array()) { - // Remove a colon from the beginning of the string. - if ($key !== '' && $key[0] === ':') $key = substr($key, 1); - - // Find the translation. - $translation = $this->__get($key); - - // If there are no arguments, return the translation. - if (!count($args)) return $translation; - - // If there are arguments, interpolate them into the translation and return the result. - return vsprintf($translation, $args); + return $this->get($key, $args); } } From ef30f0fa408dcb12e0a4dbd774a756f3753505fc Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 17 Mar 2016 14:19:35 +0900 Subject: [PATCH 138/205] Update and expand unit tests for Lang class --- tests/unit/framework/LangTest.php | 42 +++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/tests/unit/framework/LangTest.php b/tests/unit/framework/LangTest.php index 2ca57cb61..094cb9843 100644 --- a/tests/unit/framework/LangTest.php +++ b/tests/unit/framework/LangTest.php @@ -4,30 +4,50 @@ class LangTest extends \Codeception\TestCase\Test { public function testLang() { + // Test separation of languages. $ko = Rhymix\Framework\Lang::getInstance('ko'); $en = Rhymix\Framework\Lang::getInstance('en'); $this->assertTrue($ko instanceof Rhymix\Framework\Lang); $this->assertTrue($en instanceof Rhymix\Framework\Lang); $this->assertFalse($ko === $en); + // Test backward compatible language code for Japanese. $ja = Rhymix\Framework\Lang::getInstance('ja'); $jp = Rhymix\Framework\Lang::getInstance('jp'); $this->assertTrue($ja === $jp); - $this->assertEquals('도움말', $ko->get('common.help')); - $this->assertEquals('Help', $en->get('common.help')); - $this->assertEquals('도움말', $ko->help); - $this->assertEquals('Help', $en->help); - - $this->assertEquals('common.nonexistent', $ko->get('common.nonexistent')); - $this->assertEquals('common.nonexistent', $ko->get('common.nonexistent', 'foo', 'bar')); - - $this->assertEquals('admin.help', $ko->get('admin.help')); - $this->assertEquals('admin.help', $en->get('admin.help')); - + // Test loading new plugins. + $this->assertNotEquals('ヘルプ', $ja->help); $ja->loadPlugin('common'); $this->assertEquals('ヘルプ', $ja->help); + // Test simple translations with namespacing. + $this->assertEquals('도움말', $ko->get('common.help')); + $this->assertEquals('Help', $en->get('common.help')); + + // Test simple translations without namespacing. + $this->assertEquals('도움말', $ko->help); + $this->assertEquals('Help', $en->help); + + // Test complex translations with multidimensional arrays. + $this->assertEquals('%d분 전', $ko->get('common.time_gap.min')); + $this->assertEquals('10분 전', $ko->get('common.time_gap.min', 10)); + $this->assertTrue($ko->get('common.time_gap') instanceof \ArrayObject); + $this->assertEquals('%d분 전', $ko->get('common.time_gap')->min); + + // Test nonexistent keys. + $this->assertEquals('common.nonexistent', $ko->get('common.nonexistent')); + $this->assertEquals('common.nonexistent', $ko->get('common.nonexistent', 'foo', 'bar')); + $this->assertEquals('admin.help', $ko->get('admin.help')); + $this->assertEquals('admin.help', $en->get('admin.help')); + + // Test fallback to English. + $en->only_in_english = 'Hello world'; + $this->assertEquals('Hello world', $ko->only_in_english); + $this->assertEquals('Hello world', $en->only_in_english); + $this->assertEquals('Hello world', $ja->only_in_english); + + // Test string interpolation. $ko->foobartestlang = '%s님 안녕하세요?'; $this->assertEquals('Travis님 안녕하세요?', $ko->foobartestlang('Travis')); $en->foobartestlang = 'Hello, %s!'; From 15e33606098e619fadd7e79007816b786c43e421 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 17 Mar 2016 14:52:05 +0900 Subject: [PATCH 139/205] Also support dot notation when setting new language keys --- common/framework/lang.php | 103 ++++++++++++++++++++++-------- tests/unit/framework/LangTest.php | 13 ++++ 2 files changed, 90 insertions(+), 26 deletions(-) diff --git a/common/framework/lang.php b/common/framework/lang.php index 8366e715f..023ece8d7 100644 --- a/common/framework/lang.php +++ b/common/framework/lang.php @@ -247,30 +247,27 @@ class Lang $lang = $this->_loaded_plugins[$plugin_name]; foreach ($keys as $subkey) { - if (isset($lang->{$subkey})) + if (is_object($lang) && isset($lang->{$subkey})) { - $lang = is_scalar($lang->{$subkey}) ? $lang->{$subkey} : new \ArrayObject($lang->{$subkey}, 3); + $lang = $lang->{$subkey}; + } + elseif (is_array($lang) && isset($lang[$subkey])) + { + $lang = $lang[$subkey]; } else { return $this->getFromDefaultLang($key); } } - return $lang; + return is_array($lang) ? new \ArrayObject($lang, 3) : $lang; } // Search custom translations first. if (isset($this->_loaded_plugins['_custom_']->{$key})) { $lang = $this->_loaded_plugins['_custom_']->{$key}; - if (is_array($lang)) - { - return new \ArrayObject($lang, 3); - } - else - { - return $lang; - } + return is_array($lang) ? new \ArrayObject($lang, 3) : $lang; } // Search other plugins. @@ -279,14 +276,7 @@ class Lang if (isset($this->_loaded_plugins[$plugin_name]->{$key})) { $lang = $this->_loaded_plugins[$plugin_name]->{$key}; - if (is_array($lang)) - { - return new \ArrayObject($lang, 3); - } - else - { - return $lang; - } + return is_array($lang) ? new \ArrayObject($lang, 3) : $lang; } } @@ -303,6 +293,73 @@ class Lang */ public function __set($key, $value) { + // Set a dot-separated key (prefixed by plugin name). + if (preg_match('/^[a-z0-9_.-]+$/i', $key) && ($keys = explode('.', $key)) && count($keys) >= 2) + { + // Attempt to load the plugin. + $plugin_name = array_shift($keys); + if (!isset($this->_loaded_plugins[$plugin_name])) + { + $this->loadPlugin($plugin_name); + } + if (!isset($this->_loaded_plugins[$plugin_name])) + { + return false; + } + + // Set the given key. + $count = count($keys); + $lang = $this->_loaded_plugins[$plugin_name]; + foreach ($keys as $i => $subkey) + { + if (is_object($lang) && isset($lang->{$subkey})) + { + if ($i === $count - 1) + { + $lang->{$subkey} = $value; + break; + } + elseif (is_array($lang->{$subkey})) + { + $lang = &$lang->{$subkey}; + } + else + { + return false; + } + } + elseif (is_array($lang) && isset($lang[$subkey])) + { + if ($i === $count - 1) + { + $lang[$subkey] = $value; + break; + } + elseif (is_array($lang[$subkey])) + { + $lang = &$lang[$subkey]; + } + else + { + return false; + } + } + else + { + if (is_object($lang)) + { + $lang->{$subkey} = $value; + } + else + { + $lang[$subkey] = $value; + } + break; + } + } + } + + // Set a regular key. $this->_loaded_plugins['_custom_']->{$key} = $value; } @@ -332,13 +389,7 @@ class Lang */ public function __unset($key) { - foreach ($this->_loaded_plugins as $plugin_name => $translations) - { - if (isset($translations->{$key})) - { - unset($translations->{$key}); - } - } + $this->set($key, null); } /** diff --git a/tests/unit/framework/LangTest.php b/tests/unit/framework/LangTest.php index 094cb9843..4533b063c 100644 --- a/tests/unit/framework/LangTest.php +++ b/tests/unit/framework/LangTest.php @@ -41,6 +41,19 @@ class LangTest extends \Codeception\TestCase\Test $this->assertEquals('admin.help', $ko->get('admin.help')); $this->assertEquals('admin.help', $en->get('admin.help')); + // Test setting new keys with and without namespacing. + $ko->set('foo', 'FOO!'); + $this->assertEquals('FOO!', $ko->get('foo')); + $ko->set('common.foobar', 'FOOBAR!'); + $this->assertEquals('FOOBAR!', $ko->get('common.foobar')); + $this->assertEquals('FOOBAR!', $ko->get('foobar')); + + // Test setting new keys with multidimensional arrays. + $ko->set('common.time_gap.foobar', 'FOOBAR!'); + $this->assertEquals('FOOBAR!', $ko->get('common.time_gap.foobar')); + $ko->set('common.foobar.baz', 'BAZ!'); + $this->assertNotEquals('BAZ!', $ko->get('common.foobar.baz')); + // Test fallback to English. $en->only_in_english = 'Hello world'; $this->assertEquals('Hello world', $ko->only_in_english); From a9d6cf598d30a73f959277fdc616e178b51a95e1 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 17 Mar 2016 15:45:31 +0900 Subject: [PATCH 140/205] Add MIME class --- classes/mail/Mail.class.php | 76 +-------------- common/framework/mime.php | 152 ++++++++++++++++++++++++++++++ tests/unit/framework/MIMETest.php | 22 +++++ 3 files changed, 175 insertions(+), 75 deletions(-) create mode 100644 common/framework/mime.php create mode 100644 tests/unit/framework/MIMETest.php diff --git a/classes/mail/Mail.class.php b/classes/mail/Mail.class.php index c2d9725db..465e623be 100644 --- a/classes/mail/Mail.class.php +++ b/classes/mail/Mail.class.php @@ -460,81 +460,7 @@ class Mail */ function returnMIMEType($filename) { - preg_match("|\.([a-z0-9]{2,4})$|i", $filename, $fileSuffix); - switch(strtolower($fileSuffix[1])) - { - case "js" : - return "application/x-javascript"; - case "json" : - return "application/json"; - case "jpg" : - case "jpeg" : - case "jpe" : - return "image/jpg"; - case "png" : - case "gif" : - case "bmp" : - case "tiff" : - return "image/" . strtolower($fileSuffix[1]); - case "css" : - return "text/css"; - case "xml" : - return "application/xml"; - case "doc" : - case "docx" : - return "application/msword"; - case "xls" : - case "xlt" : - case "xlm" : - case "xld" : - case "xla" : - case "xlc" : - case "xlw" : - case "xll" : - return "application/vnd.ms-excel"; - case "ppt" : - case "pps" : - return "application/vnd.ms-powerpoint"; - case "rtf" : - return "application/rtf"; - case "pdf" : - return "application/pdf"; - case "html" : - case "htm" : - case "php" : - return "text/html"; - case "txt" : - return "text/plain"; - case "mpeg" : - case "mpg" : - case "mpe" : - return "video/mpeg"; - case "mp3" : - return "audio/mpeg3"; - case "wav" : - return "audio/wav"; - case "aiff" : - case "aif" : - return "audio/aiff"; - case "avi" : - return "video/msvideo"; - case "wmv" : - return "video/x-ms-wmv"; - case "mov" : - return "video/quicktime"; - case "zip" : - return "application/zip"; - case "tar" : - return "application/x-tar"; - case "swf" : - return "application/x-shockwave-flash"; - default : - if(function_exists("mime_content_type")) - { - $fileSuffix = mime_content_type($filename); - } - return "unknown/" . trim($fileSuffix[0], "."); - } + return Rhymix\Framework\MIME::getTypeByFilename($filename); } } diff --git a/common/framework/mime.php b/common/framework/mime.php new file mode 100644 index 000000000..00488659a --- /dev/null +++ b/common/framework/mime.php @@ -0,0 +1,152 @@ + $mime) + { + if (!strncasecmp($type, $mime, strlen($type))) return $extension; + } + return false; + } + + /** + * The default MIME type for unknown extensions. + */ + protected static $_default = 'application/octet-stream'; + + /** + * The list of known MIME types. + */ + protected static $_types = array( + + // Text-based document formats. + 'html' => 'text/html', + 'htm' => 'text/html', + 'shtml' => 'text/html', + 'txt' => 'text/plain', + 'text' => 'text/plain', + 'log' => 'text/plain', + 'rtf' => 'text/rtf', + 'xml' => 'text/xml', + 'xsl' => 'text/xml', + 'css' => 'text/css', + 'csv' => 'text/csv', + + // Binary document formats. + 'doc' => 'application/msword', + 'dot' => 'application/msword', + 'xls' => 'application/vnd.ms-excel', + 'ppt' => 'application/vnd.ms-powerpoint', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'pdf' => 'application/pdf', + + // Images. + 'bmp' => 'image/bmp', + 'gif' => 'image/gif', + 'jpg' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'png' => 'image/png', + 'svg' => 'image/svg+xml', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'ico' => 'image/vnd.microsoft.icon', + + // Audio. + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'mpga' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'ra' => 'audio/x-realaudio', + 'wav' => 'audio/x-wav', + 'ogg' => 'audio/ogg', + + // Video. + 'avi' => 'video/x-msvideo', + 'flv' => 'video/x-flv', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'mp4' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'movie' => 'video/x-sgi-movie', + 'rv' => 'video/vnd.rn-realvideo', + 'dvi' => 'application/x-dvi', + + // Other multimedia file formats. + 'psd' => 'application/x-photoshop', + 'swf' => 'application/x-shockwave-flash', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'mif' => 'application/vnd.mif', + 'xul' => 'application/vnd.mozilla.xul+xml', + + // Source code formats. + 'phps' => 'application/x-httpd-php-source', + 'js' => 'application/x-javascript', + + // Archives. + 'bz2' => 'application/x-bzip', + 'gz' => 'application/x-gzip', + 'tar' => 'application/x-tar', + 'tgz' => 'application/x-tar', + 'gtar' => 'application/x-gtar', + 'rar' => 'application/x-rar-compressed', + 'zip' => 'application/x-zip', + + // RFC822 email message. + 'eml' => 'message/rfc822', + ); +} diff --git a/tests/unit/framework/MIMETest.php b/tests/unit/framework/MIMETest.php new file mode 100644 index 000000000..f62324ce7 --- /dev/null +++ b/tests/unit/framework/MIMETest.php @@ -0,0 +1,22 @@ +assertEquals('audio/ogg', Rhymix\Framework\MIME::getTypeByExtension('ogg')); + $this->assertEquals('image/gif', Rhymix\Framework\MIME::getTypeByExtension('gif')); + $this->assertEquals('text/html', Rhymix\Framework\MIME::getTypeByExtension('htm')); + + $this->assertEquals('application/msword', Rhymix\Framework\MIME::getTypeByFilename('attachment.doc')); + $this->assertEquals('application/pdf', Rhymix\Framework\MIME::getTypeByFilename('라이믹스.pdf')); + $this->assertEquals('application/postscript', Rhymix\Framework\MIME::getTypeByFilename('MyGraphics.v2.eps')); + $this->assertEquals('application/vnd.ms-excel', Mail::returnMIMEType('MySpreadsheet.xls')); + $this->assertEquals('application/octet-stream', Mail::returnMIMEType('Untitled File')); + + $this->assertEquals('odt', Rhymix\Framework\MIME::getExtensionByType('application/vnd.oasis.opendocument.text')); + $this->assertEquals('jpg', Rhymix\Framework\MIME::getExtensionByType('image/jpeg')); + $this->assertEquals('mpeg', Rhymix\Framework\MIME::getExtensionByType('video/mpeg')); + $this->assertFalse(Rhymix\Framework\MIME::getExtensionByType('application/octet-stream')); + } +} From 63006e025a0ba871a29c77e120b4e69ae17fc36f Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 17 Mar 2016 16:04:36 +0900 Subject: [PATCH 141/205] Add Timer class --- common/framework/timer.php | 109 +++++++++++++++++++++++++++++ tests/unit/framework/TimerTest.php | 63 +++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 common/framework/timer.php create mode 100644 tests/unit/framework/TimerTest.php diff --git a/common/framework/timer.php b/common/framework/timer.php new file mode 100644 index 000000000..b4650b663 --- /dev/null +++ b/common/framework/timer.php @@ -0,0 +1,109 @@ +assertGreaterThanOrEqual($t1, $started); + $this->assertLessThanOrEqual($t2, $started); + $this->assertGreaterThanOrEqual($t2 - $started, $elapsed); + $this->assertLessThanOrEqual($t3 - $t1, $elapsed); + $this->assertGreaterThan(0, $elapsed); + } + + function testNestedTimers() + { + $t1 = Rhymix\Framework\Timer::start(); + $t2 = Rhymix\Framework\Timer::start(); + $t3 = Rhymix\Framework\Timer::stop(); + $t4 = Rhymix\Framework\Timer::stop(); + + $this->assertGreaterThanOrEqual($t1, $t2); + $this->assertGreaterThan($t4, $t3); + } + + function testMultipleTimers() + { + $t1 = Rhymix\Framework\Timer::start('timer1'); + usleep(5000); + $t2 = Rhymix\Framework\Timer::start('timer2'); + + $t3 = Rhymix\Framework\Timer::stop('timer1'); + usleep(2000); + $t4 = Rhymix\Framework\Timer::stop('timer2'); + + $this->assertGreaterThanOrEqual($t1, $t2); + $this->assertGreaterThanOrEqual($t4, $t3); + } + + function testTimerFormat() + { + $t1 = Rhymix\Framework\Timer::start(); + $t2 = Rhymix\Framework\Timer::stopFormat(); + + $this->assertRegexp('/^[0-9\.]+ms$/', $t2); + } + + function testTimerSinceStartup() + { + $t1 = Rhymix\Framework\Timer::sinceStartup(); + $t2 = Rhymix\Framework\Timer::sinceStartup(); + + $this->assertGreaterThanOrEqual($t1, $t2); + + $t3 = Rhymix\Framework\Timer::sinceStartupFormat(); + $this->assertRegexp('/^[0-9\.]+ms$/', $t3); + } +} From 15a0c591dc0bbfb0dcf0d77b97f91b03d215186b Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 17 Mar 2016 16:22:18 +0900 Subject: [PATCH 142/205] Add Calendar class --- common/framework/calendar.php | 132 ++++++++++++++++++++++++++ tests/unit/framework/CalendarTest.php | 52 ++++++++++ 2 files changed, 184 insertions(+) create mode 100644 common/framework/calendar.php create mode 100644 tests/unit/framework/CalendarTest.php diff --git a/common/framework/calendar.php b/common/framework/calendar.php new file mode 100644 index 000000000..43f49a030 --- /dev/null +++ b/common/framework/calendar.php @@ -0,0 +1,132 @@ +assertEquals('January', Rhymix\Framework\Calendar::getMonthName('01')); + $this->assertEquals('October', Rhymix\Framework\Calendar::getMonthName('10')); + $this->assertEquals('Nov', Rhymix\Framework\Calendar::getMonthName(11, false)); + $this->assertEquals('Dec', Rhymix\Framework\Calendar::getMonthName(12, false)); + } + + public function testGetMonthStartDayOfWeek() + { + $this->assertEquals(5, Rhymix\Framework\Calendar::getMonthStartDayOfWeek(1, 2016)); + $this->assertEquals(1, Rhymix\Framework\Calendar::getMonthStartDayOfWeek(2, 2016)); + $this->assertEquals(2, Rhymix\Framework\Calendar::getMonthStartDayOfWeek(3, 2016)); + $this->assertEquals(5, Rhymix\Framework\Calendar::getMonthStartDayOfWeek(4, 2016)); + } + + public function testGetMonthDays() + { + $this->assertEquals(30, Rhymix\Framework\Calendar::getMonthDays(11, 2015)); + $this->assertEquals(31, Rhymix\Framework\Calendar::getMonthDays(12, 2015)); + $this->assertEquals(31, Rhymix\Framework\Calendar::getMonthDays(1, 2016)); + $this->assertEquals(29, Rhymix\Framework\Calendar::getMonthDays(2, 2016)); + } + + public function testGetMonthCalendar() + { + $target_201508 = array( + array(null, null, null, null, null, null, 1), + array(2, 3, 4, 5, 6, 7, 8), + array(9, 10, 11, 12, 13, 14, 15), + array(16, 17, 18, 19, 20, 21, 22), + array(23, 24, 25, 26, 27, 28, 29), + array(30, 31, null, null, null, null, null), + ); + + $target_201603 = array( + array(null, null, 1, 2, 3, 4, 5), + array(6, 7, 8, 9, 10, 11, 12), + array(13, 14, 15, 16, 17, 18, 19), + array(20, 21, 22, 23, 24, 25, 26), + array(27, 28, 29, 30, 31, null, null), + array(null, null, null, null, null, null, null), + ); + + $this->assertEquals($target_201508, Rhymix\Framework\Calendar::getMonthCalendar(8, 2015)); + $this->assertEquals($target_201603, Rhymix\Framework\Calendar::getMonthCalendar(3, 2016)); + } +} From 8b559598803fee0ca8d896819a86fca4995df752 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 17 Mar 2016 16:24:27 +0900 Subject: [PATCH 143/205] Fix unit tests to accommodate exceptionally long-running tests --- tests/unit/framework/TimerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/framework/TimerTest.php b/tests/unit/framework/TimerTest.php index d69623563..87dd9bc6d 100644 --- a/tests/unit/framework/TimerTest.php +++ b/tests/unit/framework/TimerTest.php @@ -47,7 +47,7 @@ class TimerTest extends \Codeception\TestCase\Test $t1 = Rhymix\Framework\Timer::start(); $t2 = Rhymix\Framework\Timer::stopFormat(); - $this->assertRegexp('/^[0-9\.]+ms$/', $t2); + $this->assertRegexp('/^[0-9\.,]+ms$/', $t2); } function testTimerSinceStartup() @@ -58,6 +58,6 @@ class TimerTest extends \Codeception\TestCase\Test $this->assertGreaterThanOrEqual($t1, $t2); $t3 = Rhymix\Framework\Timer::sinceStartupFormat(); - $this->assertRegexp('/^[0-9\.]+ms$/', $t3); + $this->assertRegexp('/^[0-9\.,]+ms$/', $t3); } } From 475c8d49a9264c8163420a8c8f4a1b685f9fa844 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 17 Mar 2016 17:33:58 +0900 Subject: [PATCH 144/205] Fix short array syntax --- common/framework/calendar.php | 4 ++-- common/framework/timer.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/framework/calendar.php b/common/framework/calendar.php index 43f49a030..b24e7b379 100644 --- a/common/framework/calendar.php +++ b/common/framework/calendar.php @@ -101,7 +101,7 @@ class Calendar $count = self::getMonthDays($month_number, $year); $initial_blank_cells = (7 + $start - $start_dow) % 7; $final_blank_cells = 42 - $count - $initial_blank_cells; - $temp = []; + $temp = array(); for ($i = 0; $i < $initial_blank_cells; $i++) { @@ -116,7 +116,7 @@ class Calendar $temp[] = null; } - $return = []; + $return = array(); for ($i = 0; $i < 6; $i++) { $week = []; diff --git a/common/framework/timer.php b/common/framework/timer.php index b4650b663..86842158a 100644 --- a/common/framework/timer.php +++ b/common/framework/timer.php @@ -10,7 +10,7 @@ class Timer /** * Timestamps are stored here. */ - protected static $_timestamps = []; + protected static $_timestamps = array(); /** * Start a timer. From 509893e791cf2e7372caed674aa25fae507d7263 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 17 Mar 2016 17:41:03 +0900 Subject: [PATCH 145/205] Fix short array syntax and backwards test --- common/framework/calendar.php | 2 +- tests/unit/framework/TimerTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/framework/calendar.php b/common/framework/calendar.php index b24e7b379..67aaafbe4 100644 --- a/common/framework/calendar.php +++ b/common/framework/calendar.php @@ -119,7 +119,7 @@ class Calendar $return = array(); for ($i = 0; $i < 6; $i++) { - $week = []; + $week = array(); for ($j = 0; $j < 7; $j++) { $week[] = array_shift($temp); diff --git a/tests/unit/framework/TimerTest.php b/tests/unit/framework/TimerTest.php index 87dd9bc6d..b0427d688 100644 --- a/tests/unit/framework/TimerTest.php +++ b/tests/unit/framework/TimerTest.php @@ -25,7 +25,7 @@ class TimerTest extends \Codeception\TestCase\Test $t4 = Rhymix\Framework\Timer::stop(); $this->assertGreaterThanOrEqual($t1, $t2); - $this->assertGreaterThan($t4, $t3); + $this->assertGreaterThan($t3, $t4); } function testMultipleTimers() From 97c10ab2cf4e8a141e95fb47ba7bd6c503ec7df0 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 17 Mar 2016 17:45:15 +0900 Subject: [PATCH 146/205] Add some delays to timer test --- tests/unit/framework/TimerTest.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/unit/framework/TimerTest.php b/tests/unit/framework/TimerTest.php index b0427d688..1c9783c0d 100644 --- a/tests/unit/framework/TimerTest.php +++ b/tests/unit/framework/TimerTest.php @@ -5,9 +5,13 @@ class TimerTest extends \Codeception\TestCase\Test function testStartStop() { $t1 = microtime(true); + usleep(1000); $started = Rhymix\Framework\Timer::start(); + usleep(1000); $t2 = microtime(true); + usleep(1000); $elapsed = Rhymix\Framework\Timer::stop(); + usleep(1000); $t3 = microtime(true); $this->assertGreaterThanOrEqual($t1, $started); @@ -20,8 +24,11 @@ class TimerTest extends \Codeception\TestCase\Test function testNestedTimers() { $t1 = Rhymix\Framework\Timer::start(); + usleep(1000); $t2 = Rhymix\Framework\Timer::start(); + usleep(1000); $t3 = Rhymix\Framework\Timer::stop(); + usleep(1000); $t4 = Rhymix\Framework\Timer::stop(); $this->assertGreaterThanOrEqual($t1, $t2); @@ -33,7 +40,7 @@ class TimerTest extends \Codeception\TestCase\Test $t1 = Rhymix\Framework\Timer::start('timer1'); usleep(5000); $t2 = Rhymix\Framework\Timer::start('timer2'); - + usleep(1000); $t3 = Rhymix\Framework\Timer::stop('timer1'); usleep(2000); $t4 = Rhymix\Framework\Timer::stop('timer2'); @@ -45,6 +52,7 @@ class TimerTest extends \Codeception\TestCase\Test function testTimerFormat() { $t1 = Rhymix\Framework\Timer::start(); + usleep(1000); $t2 = Rhymix\Framework\Timer::stopFormat(); $this->assertRegexp('/^[0-9\.,]+ms$/', $t2); From 59ee4a7387831e127f245d545eff7aebeb975008 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Thu, 17 Mar 2016 19:46:32 +0900 Subject: [PATCH 147/205] Add pagination class --- common/framework/pagination.php | 153 ++++++++++++++++++++++++ tests/unit/framework/PaginationTest.php | 48 ++++++++ 2 files changed, 201 insertions(+) create mode 100644 common/framework/pagination.php create mode 100644 tests/unit/framework/PaginationTest.php diff --git a/common/framework/pagination.php b/common/framework/pagination.php new file mode 100644 index 000000000..cc26906e4 --- /dev/null +++ b/common/framework/pagination.php @@ -0,0 +1,153 @@ + $total_pages) + { + $last_shown = $total_pages; + } + } + else + { + $first_shown = $current_page - floor(($count - 1) / 2); + if ($first_shown < 1) + { + $first_shown = 1; + } + $last_shown = $first_shown + $count - 1; + if ($last_shown > $total_pages) + { + $last_shown = $total_pages; + $first_shown = max(1, $last_shown - $count + 1); + } + } + + // Open the
tag. + $return = array('