Enh: Asset Management Enhancement with yii asset command support minif/uglify.

This commit is contained in:
buddha87 2016-08-21 19:58:55 +02:00
parent 056f4440a5
commit 600d587cc8
123 changed files with 13205 additions and 4177 deletions

View File

@ -1,6 +1,15 @@
module.exports = function (grunt) {
var uglifyAssetcfg = {};
uglifyAssetcfg[grunt.option('to')] = grunt.option('from');
var cssMinAssetcfg = {};
cssMinAssetcfg[grunt.option('to')] = [grunt.option('from')];
grunt.log.write(grunt.option('from'));
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
separator: ';',
@ -24,6 +33,14 @@ module.exports = function (grunt) {
files: {
'js/dist/humhub.all.min.js': ['js/dist/humhub.all.js']
}
},
assets: {
files: uglifyAssetcfg
}
},
cssmin: {
target: {
files: cssMinAssetcfg
}
},
less: {
@ -38,6 +55,7 @@ module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-clean');

View File

@ -37,6 +37,12 @@
"bower-asset/select2" : "^4.0.2",
"bower-asset/bluebird" : "^3.3.5",
"bower-asset/select2-bootstrap-theme" : "0.1.0-beta.4",
"bower-asset/jquery.cookie": "^1.4.1",
"bower-asset/jquery-color": "^2.1.2",
"bower-asset/autosize": "1.*",
"bower-asset/jquery-pjax": "1.*",
"bower-asset/At.js": "0.5.0",
"bower-asset/animate.css": "*",
"jonnyw/php-phantomjs": "4.*"
},
"require-dev": {

6
css/animate.min.css vendored

File diff suppressed because one or more lines are too long

View File

@ -1,204 +0,0 @@
/*!
* Datetimepicker for Bootstrap v3
* https://github.com/Eonasdan/bootstrap-datetimepicker/
*/
.bootstrap-datetimepicker-widget {
top: 0;
left: 0;
width: 250px;
padding: 4px;
margin-top: 1px;
z-index: 99999 !important;
border-radius: 4px;
}
.bootstrap-datetimepicker-widget.timepicker-sbs {
width: 600px;
}
.bootstrap-datetimepicker-widget.bottom:before {
content: '';
display: inline-block;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-bottom: 7px solid #ccc;
border-bottom-color: rgba(0, 0, 0, 0.2);
position: absolute;
top: -7px;
left: 7px;
}
.bootstrap-datetimepicker-widget.bottom:after {
content: '';
display: inline-block;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-bottom: 6px solid white;
position: absolute;
top: -6px;
left: 8px;
}
.bootstrap-datetimepicker-widget.top:before {
content: '';
display: inline-block;
border-left: 7px solid transparent;
border-right: 7px solid transparent;
border-top: 7px solid #ccc;
border-top-color: rgba(0, 0, 0, 0.2);
position: absolute;
bottom: -7px;
left: 6px;
}
.bootstrap-datetimepicker-widget.top:after {
content: '';
display: inline-block;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
border-top: 6px solid white;
position: absolute;
bottom: -6px;
left: 7px;
}
.bootstrap-datetimepicker-widget .dow {
width: 14.2857%;
}
.bootstrap-datetimepicker-widget.pull-right:before {
left: auto;
right: 6px;
}
.bootstrap-datetimepicker-widget.pull-right:after {
left: auto;
right: 7px;
}
.bootstrap-datetimepicker-widget > ul {
list-style-type: none;
margin: 0;
}
.bootstrap-datetimepicker-widget .timepicker-hour,
.bootstrap-datetimepicker-widget .timepicker-minute,
.bootstrap-datetimepicker-widget .timepicker-second {
width: 100%;
font-weight: bold;
font-size: 1.2em;
}
.bootstrap-datetimepicker-widget table[data-hour-format="12"] .separator {
width: 4px;
padding: 0;
margin: 0;
}
.bootstrap-datetimepicker-widget .datepicker > div {
display: none;
}
.bootstrap-datetimepicker-widget .picker-switch {
text-align: center;
}
.bootstrap-datetimepicker-widget table {
width: 100%;
margin: 0;
}
.bootstrap-datetimepicker-widget td,
.bootstrap-datetimepicker-widget th {
text-align: center;
width: 20px;
height: 20px;
border-radius: 4px;
}
.bootstrap-datetimepicker-widget td.day:hover,
.bootstrap-datetimepicker-widget td.hour:hover,
.bootstrap-datetimepicker-widget td.minute:hover,
.bootstrap-datetimepicker-widget td.second:hover {
background: #eeeeee;
cursor: pointer;
}
.bootstrap-datetimepicker-widget td.old,
.bootstrap-datetimepicker-widget td.new {
color: #999999;
}
.bootstrap-datetimepicker-widget td.today {
position: relative;
}
.bootstrap-datetimepicker-widget td.today:before {
content: '';
display: inline-block;
border-left: 7px solid transparent;
border-bottom: 7px solid #428bca;
border-top-color: rgba(0, 0, 0, 0.2);
position: absolute;
bottom: 4px;
right: 4px;
}
.bootstrap-datetimepicker-widget td.active,
.bootstrap-datetimepicker-widget td.active:hover {
background-color: #428bca;
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.bootstrap-datetimepicker-widget td.active.today:before {
border-bottom-color: #fff;
}
.bootstrap-datetimepicker-widget td.disabled,
.bootstrap-datetimepicker-widget td.disabled:hover {
background: none;
color: #999999;
cursor: not-allowed;
}
.bootstrap-datetimepicker-widget td span {
display: block;
width: 47px;
height: 54px;
line-height: 54px;
float: left;
margin: 2px;
cursor: pointer;
border-radius: 4px;
}
.bootstrap-datetimepicker-widget td span:hover {
background: #eeeeee;
}
.bootstrap-datetimepicker-widget td span.active {
background-color: #428bca;
color: #fff;
text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
}
.bootstrap-datetimepicker-widget td span.old {
color: #999999;
}
.bootstrap-datetimepicker-widget td span.disabled,
.bootstrap-datetimepicker-widget td span.disabled:hover {
background: none;
color: #999999;
cursor: not-allowed;
}
.bootstrap-datetimepicker-widget th.switch {
width: 145px;
}
.bootstrap-datetimepicker-widget th.next,
.bootstrap-datetimepicker-widget th.prev {
font-size: 21px;
}
.bootstrap-datetimepicker-widget th.disabled,
.bootstrap-datetimepicker-widget th.disabled:hover {
background: none;
color: #999999;
cursor: not-allowed;
}
.bootstrap-datetimepicker-widget thead tr:first-child th {
cursor: pointer;
}
.bootstrap-datetimepicker-widget thead tr:first-child th:hover {
background: #eeeeee;
}
.input-group.date .input-group-addon span {
display: block;
cursor: pointer;
width: 16px;
height: 16px;
}
.bootstrap-datetimepicker-widget.left-oriented:before {
left: auto;
right: 6px;
}
.bootstrap-datetimepicker-widget.left-oriented:after {
left: auto;
right: 7px;
}
.bootstrap-datetimepicker-widget ul.list-unstyled li div.timepicker div.timepicker-picker table.table-condensed tbody > tr > td {
padding: 0px !important;
}

View File

@ -0,0 +1,97 @@
.atwho-view {
position: absolute;
top: 0;
left: 0;
display: none;
margin-top: 18px;
background: white;
color: #555555;
font-size: 14px;
font-weight: 400;
border: 1px solid #d7d7d7;
border-radius: 4px;
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
min-width: 120px;
max-width: 265px;
z-index: 11110 !important;
padding: 5px 0;
}
.atwho-view strong, .atwho-view b {
font-weight: normal;
}
.atwho-view ul li.hint {
background: #fff !important;
border-left: 3px solid transparent !important;
font-size: 12px;
color: #999;
}
.atwho-view .cur small {
color: red;
}
.atwho-view strong {
background-color: #f9f0d2;
}
.atwho-view .cur strong {
background-color: #f9f0d2;
}
.atwho-view ul {
/* width: 100px; */
list-style: none;
padding: 0;
margin: auto;
}
.atwho-view ul li {
display: block;
padding: 5px 10px;
border-left: 3px solid transparent;
padding: 4px 15px 4px 8px;
cursor: pointer;
/* border-top: 1px solid #C8C8C8; */
}
.atwho-view small {
font-size: smaller;
color: #777;
font-weight: normal;
}
.atwho-input.form-control {
min-height: 36px;
height: auto;
padding-right: 95px;
word-wrap: break-word;
}
.atwho-input p {
padding: 0;
margin: 0;
}
.atwho-placeholder {
color: #bebebe !important;
}
.atwho-emoji-entry {
float: left;
padding: 4px !important;
margin: 0px !important;
border:none !important;
}
.atwho-emoji-entry:hover, .atwho-emoji-entry:active, .atwho-emoji-entry:focus {
padding: 4px !important;
margin: 0px !important;
border:none !important;
background-color: #f7f7f7 !important;
border-radius: 3px;
}

View File

@ -20,7 +20,7 @@ require(__DIR__ . '/protected/vendor/yiisoft/yii2/Yii.php');
$config = yii\helpers\ArrayHelper::merge(
// add more configurations here
(is_readable(__DIR__ . '/protected/config/dynamic.php')) ? require(__DIR__ . '/protected/config/dynamic.php') : [],
(is_readable(__DIR__ . '/protected/humhub/tests/codeception/config/dynamic.php')) ? require(__DIR__ . '/protected/humhub/tests/codeception/config/dynamic.php') : [],
require(__DIR__ . '/protected/humhub/tests/codeception/config/acceptance.php')
);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,88 +0,0 @@
$(document).ready(function() {
$('.hhtml-datetime-field').each(function(index, element) {
var $element = $(element)
, dateTimepickerDefaultOptions = {
pickDate: true,
pickTime: false,
useMinutes: true,
useSeconds: false,
showToday: true,
language: localeId.replace("\_", "\-"),
use24hours: true,
sideBySide: false,
// Initally use DB Timestamp Format
// After that we switch to locale format
format: 'YYYY-MM-DD hh:mm'
}
, $dateInput = $element.clone()
, dateTimepickerOptions = {}
, re_dataAttr = /^data\-options\-(.+)$/
;
$dateInput.removeAttr('name').removeAttr('id');
$.each(element.attributes, function(index, attr) {
if (re_dataAttr.test(attr.nodeName)) {
var key = attr.nodeName.match(re_dataAttr)[1]
, nodeValue = attr.nodeValue
;
nodeValue = nodeValue === 'true' ? true : nodeValue;
nodevalue = nodeValue === 'false' ? false : nodeValue;
dateTimepickerOptions[key] = nodeValue;
}
});
if ($element.attr('data-options-pickTime') == "true") {
dateTimepickerOptions.pickTime = true;
}
dateTimepickerOptions = $.extend({}, dateTimepickerDefaultOptions, dateTimepickerOptions);
$dateInput.datetimepicker(dateTimepickerOptions);
var datepicker = $dateInput.data("DateTimePicker");
if (typeof $element.attr('data-options-displayFormat') === "undefined") {
// Switch to format given by locale
localeData = moment().localeData();
datepicker.format = (datepicker.options.pickDate ? localeData.longDateFormat('L') : '');
if (datepicker.options.pickDate && datepicker.options.pickTime) {
datepicker.format += ' ';
}
datepicker.format += (datepicker.options.pickTime ? localeData.longDateFormat('LT') : '');
if (datepicker.options.useSeconds) {
if (localeData.longDateFormat('LT').indexOf(' A') !== -1) {
datepicker.format = datepicker.format.split(' A')[0] + ':ss A';
}
else {
datepicker.format += ':ss';
}
}
} else {
datepicker.format = $element.attr('data-options-displayFormat');
}
if ($element.val() != "") {
datepicker.setDate(datepicker.date); // update visible date to correct Format
}
$dateInput.bind('blur change', function(ev) {
if ($dateInput.val()) {
if (datepicker.getDate()) {
$element.val(datepicker.getDate().format('YYYY-MM-DD HH:mm:00'));
}
} else {
$element.val('');
}
});
$element
.hide()
.after($dateInput)
;
$dateInput.trigger('change');
});
});

File diff suppressed because one or more lines are too long

View File

@ -1,7 +0,0 @@
/*!
Autosize v1.17.8 - 2013-09-07
Automatically adjust textarea height based on user input.
(c) 2013 Jack Moore - http://www.jacklmoore.com/autosize
license: http://www.opensource.org/licenses/mit-license.php
*/
(function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(window.jQuery||window.$)})(function(e){var t,o={className:"autosizejs",append:"",callback:!1,resizeDelay:10},i='<textarea tabindex="-1" style="position:absolute; top:-999px; left:0; right:auto; bottom:auto; border:0; padding: 0; -moz-box-sizing:content-box; -webkit-box-sizing:content-box; box-sizing:content-box; word-wrap:break-word; height:0 !important; min-height:0 !important; overflow:hidden; transition:none; -webkit-transition:none; -moz-transition:none;"/>',n=["fontFamily","fontSize","fontWeight","fontStyle","letterSpacing","textTransform","wordSpacing","textIndent"],s=e(i).data("autosize",!0)[0];s.style.lineHeight="99px","99px"===e(s).css("lineHeight")&&n.push("lineHeight"),s.style.lineHeight="",e.fn.autosize=function(i){return this.length?(i=e.extend({},o,i||{}),s.parentNode!==document.body&&e(document.body).append(s),this.each(function(){function o(){var t,o;"getComputedStyle"in window?(t=window.getComputedStyle(u),o=u.getBoundingClientRect().width,e.each(["paddingLeft","paddingRight","borderLeftWidth","borderRightWidth"],function(e,i){o-=parseInt(t[i],10)}),s.style.width=o+"px"):s.style.width=Math.max(p.width(),0)+"px"}function a(){var a={};if(t=u,s.className=i.className,d=parseInt(p.css("maxHeight"),10),e.each(n,function(e,t){a[t]=p.css(t)}),e(s).css(a),o(),window.chrome){var r=u.style.width;u.style.width="0px",u.offsetWidth,u.style.width=r}}function r(){var e,n;t!==u?a():o(),s.value=u.value+i.append,s.style.overflowY=u.style.overflowY,n=parseInt(u.style.height,10),s.scrollTop=0,s.scrollTop=9e4,e=s.scrollTop,d&&e>d?(u.style.overflowY="scroll",e=d):(u.style.overflowY="hidden",c>e&&(e=c)),e+=f,n!==e&&(u.style.height=e+"px",w&&i.callback.call(u,u))}function l(){clearTimeout(h),h=setTimeout(function(){var e=p.width();e!==g&&(g=e,r())},parseInt(i.resizeDelay,10))}var d,c,h,u=this,p=e(u),f=0,w=e.isFunction(i.callback),z={height:u.style.height,overflow:u.style.overflow,overflowY:u.style.overflowY,wordWrap:u.style.wordWrap,resize:u.style.resize},g=p.width();p.data("autosize")||(p.data("autosize",!0),("border-box"===p.css("box-sizing")||"border-box"===p.css("-moz-box-sizing")||"border-box"===p.css("-webkit-box-sizing"))&&(f=p.outerHeight()-p.height()),c=Math.max(parseInt(p.css("minHeight"),10)-f||0,p.height()),p.css({overflow:"hidden",overflowY:"hidden",wordWrap:"break-word",resize:"none"===p.css("resize")||"vertical"===p.css("resize")?"none":"horizontal"}),"onpropertychange"in u?"oninput"in u?p.on("input.autosize keyup.autosize",r):p.on("propertychange.autosize",function(){"value"===event.propertyName&&r()}):p.on("input.autosize",r),i.resizeDelay!==!1&&e(window).on("resize.autosize",l),p.on("autosize.resize",r),p.on("autosize.resizeIncludeStyle",function(){t=null,r()}),p.on("autosize.destroy",function(){t=null,clearTimeout(h),e(window).off("resize",l),p.off("autosize").off(".autosize").css(z).removeData("autosize")}),r())})):this}});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,113 +0,0 @@
/*!
* jQuery Cookie Plugin v1.4.0
* https://github.com/carhartl/jquery-cookie
*
* Copyright 2013 Klaus Hartl
* Released under the MIT license
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as anonymous module.
define(['jquery'], factory);
} else {
// Browser globals.
factory(jQuery);
}
}(function ($) {
var pluses = /\+/g;
function encode(s) {
return config.raw ? s : encodeURIComponent(s);
}
function decode(s) {
return config.raw ? s : decodeURIComponent(s);
}
function stringifyCookieValue(value) {
return encode(config.json ? JSON.stringify(value) : String(value));
}
function parseCookieValue(s) {
if (s.indexOf('"') === 0) {
// This is a quoted cookie as according to RFC2068, unescape...
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
}
try {
// Replace server-side written pluses with spaces.
// If we can't decode the cookie, ignore it, it's unusable.
// If we can't parse the cookie, ignore it, it's unusable.
s = decodeURIComponent(s.replace(pluses, ' '));
return config.json ? JSON.parse(s) : s;
} catch(e) {}
}
function read(s, converter) {
var value = config.raw ? s : parseCookieValue(s);
return $.isFunction(converter) ? converter(value) : value;
}
var config = $.cookie = function (key, value, options) {
// Write
if (value !== undefined && !$.isFunction(value)) {
options = $.extend({}, config.defaults, options);
if (typeof options.expires === 'number') {
var days = options.expires, t = options.expires = new Date();
t.setDate(t.getDate() + days);
}
return (document.cookie = [
encode(key), '=', stringifyCookieValue(value),
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
options.path ? '; path=' + options.path : '',
options.domain ? '; domain=' + options.domain : '',
options.secure ? '; secure' : ''
].join(''));
}
// Read
var result = key ? undefined : {};
// To prevent the for loop in the first place assign an empty array
// in case there are no cookies at all. Also prevents odd result when
// calling $.cookie().
var cookies = document.cookie ? document.cookie.split('; ') : [];
for (var i = 0, l = cookies.length; i < l; i++) {
var parts = cookies[i].split('=');
var name = decode(parts.shift());
var cookie = parts.join('=');
if (key && key === name) {
// If second argument (value) is a function it's a converter...
result = read(cookie, value);
break;
}
// Prevent storing a cookie that we couldn't decode.
if (!key && (cookie = read(cookie)) !== undefined) {
result[name] = cookie;
}
}
return result;
};
config.defaults = {};
$.removeCookie = function (key, options) {
if ($.cookie(key) === undefined) {
return false;
}
// Must not alter options, thus extending a fresh object...
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
return !$.cookie(key);
};
}));

View File

@ -1,919 +0,0 @@
/*!
* Copyright 2012, Chris Wanstrath
* Released under the MIT License
* https://github.com/defunkt/jquery-pjax
*/
(function($){
// When called on a container with a selector, fetches the href with
// ajax into the container or with the data-pjax attribute on the link
// itself.
//
// Tries to make sure the back button and ctrl+click work the way
// you'd expect.
//
// Exported as $.fn.pjax
//
// Accepts a jQuery ajax options object that may include these
// pjax specific options:
//
//
// container - Where to stick the response body. Usually a String selector.
// $(container).html(xhr.responseBody)
// (default: current jquery context)
// push - Whether to pushState the URL. Defaults to true (of course).
// replace - Want to use replaceState instead? That's cool.
//
// For convenience the second parameter can be either the container or
// the options object.
//
// Returns the jQuery object
function fnPjax(selector, container, options) {
var context = this
return this.on('click.pjax', selector, function(event) {
var opts = $.extend({}, optionsFor(container, options))
if (!opts.container)
opts.container = $(this).attr('data-pjax') || context
handleClick(event, opts)
})
}
// Public: pjax on click handler
//
// Exported as $.pjax.click.
//
// event - "click" jQuery.Event
// options - pjax options
//
// Examples
//
// $(document).on('click', 'a', $.pjax.click)
// // is the same as
// $(document).pjax('a')
//
// $(document).on('click', 'a', function(event) {
// var container = $(this).closest('[data-pjax-container]')
// $.pjax.click(event, container)
// })
//
// Returns nothing.
function handleClick(event, container, options) {
options = optionsFor(container, options)
var link = event.currentTarget
if (link.tagName.toUpperCase() !== 'A')
throw "$.fn.pjax or $.pjax.click requires an anchor element"
// Middle click, cmd click, and ctrl click should open
// links in a new tab as normal.
if ( event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey )
return
// Ignore cross origin links
if ( location.protocol !== link.protocol || location.hostname !== link.hostname )
return
// Ignore case when a hash is being tacked on the current URL
if ( link.href.indexOf('#') > -1 && stripHash(link) == stripHash(location) )
return
// Ignore event with default prevented
if (event.isDefaultPrevented())
return
var defaults = {
url: link.href,
container: $(link).attr('data-pjax'),
target: link
}
var opts = $.extend({}, defaults, options)
var clickEvent = $.Event('pjax:click')
$(link).trigger(clickEvent, [opts])
if (!clickEvent.isDefaultPrevented()) {
pjax(opts)
event.preventDefault()
$(link).trigger('pjax:clicked', [opts])
}
}
// Public: pjax on form submit handler
//
// Exported as $.pjax.submit
//
// event - "click" jQuery.Event
// options - pjax options
//
// Examples
//
// $(document).on('submit', 'form', function(event) {
// var container = $(this).closest('[data-pjax-container]')
// $.pjax.submit(event, container)
// })
//
// Returns nothing.
function handleSubmit(event, container, options) {
options = optionsFor(container, options)
var form = event.currentTarget
if (form.tagName.toUpperCase() !== 'FORM')
throw "$.pjax.submit requires a form element"
var defaults = {
type: form.method.toUpperCase(),
url: form.action,
container: $(form).attr('data-pjax'),
target: form
}
if (defaults.type !== 'GET' && window.FormData !== undefined) {
defaults.data = new FormData(form);
defaults.processData = false;
defaults.contentType = false;
} else {
// Can't handle file uploads, exit
if ($(form).find(':file').length) {
return;
}
// Fallback to manually serializing the fields
defaults.data = $(form).serializeArray();
}
pjax($.extend({}, defaults, options))
event.preventDefault()
}
// Loads a URL with ajax, puts the response body inside a container,
// then pushState()'s the loaded URL.
//
// Works just like $.ajax in that it accepts a jQuery ajax
// settings object (with keys like url, type, data, etc).
//
// Accepts these extra keys:
//
// container - Where to stick the response body.
// $(container).html(xhr.responseBody)
// push - Whether to pushState the URL. Defaults to true (of course).
// replace - Want to use replaceState instead? That's cool.
//
// Use it just like $.ajax:
//
// var xhr = $.pjax({ url: this.href, container: '#main' })
// console.log( xhr.readyState )
//
// Returns whatever $.ajax returns.
function pjax(options) {
options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options)
if ($.isFunction(options.url)) {
options.url = options.url()
}
var target = options.target
var hash = parseURL(options.url).hash
var context = options.context = findContainerFor(options.container)
// We want the browser to maintain two separate internal caches: one
// for pjax'd partial page loads and one for normal page loads.
// Without adding this secret parameter, some browsers will often
// confuse the two.
if (!options.data) options.data = {}
if ($.isArray(options.data)) {
options.data.push({name: '_pjax', value: context.selector})
} else {
options.data._pjax = context.selector
}
function fire(type, args, props) {
if (!props) props = {}
props.relatedTarget = target
var event = $.Event(type, props)
context.trigger(event, args)
return !event.isDefaultPrevented()
}
var timeoutTimer
options.beforeSend = function(xhr, settings) {
// No timeout for non-GET requests
// Its not safe to request the resource again with a fallback method.
if (settings.type !== 'GET') {
settings.timeout = 0
}
xhr.setRequestHeader('X-PJAX', 'true')
xhr.setRequestHeader('X-PJAX-Container', context.selector)
if (!fire('pjax:beforeSend', [xhr, settings]))
return false
if (settings.timeout > 0) {
timeoutTimer = setTimeout(function() {
if (fire('pjax:timeout', [xhr, options]))
xhr.abort('timeout')
}, settings.timeout)
// Clear timeout setting so jquerys internal timeout isn't invoked
settings.timeout = 0
}
var url = parseURL(settings.url)
if (hash) url.hash = hash
options.requestUrl = stripInternalParams(url)
}
options.complete = function(xhr, textStatus) {
if (timeoutTimer)
clearTimeout(timeoutTimer)
fire('pjax:complete', [xhr, textStatus, options])
fire('pjax:end', [xhr, options])
}
options.error = function(xhr, textStatus, errorThrown) {
var container = extractContainer("", xhr, options)
var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options])
if (options.type == 'GET' && textStatus !== 'abort' && allowed) {
locationReplace(container.url)
}
}
options.success = function(data, status, xhr) {
var previousState = pjax.state;
// If $.pjax.defaults.version is a function, invoke it first.
// Otherwise it can be a static string.
var currentVersion = (typeof $.pjax.defaults.version === 'function') ?
$.pjax.defaults.version() :
$.pjax.defaults.version
var latestVersion = xhr.getResponseHeader('X-PJAX-Version')
var container = extractContainer(data, xhr, options)
var url = parseURL(container.url)
if (hash) {
url.hash = hash
container.url = url.href
}
// If there is a layout version mismatch, hard load the new url
if (currentVersion && latestVersion && currentVersion !== latestVersion) {
locationReplace(container.url)
return
}
// If the new response is missing a body, hard load the page
if (!container.contents) {
locationReplace(container.url)
return
}
pjax.state = {
id: options.id || uniqueId(),
url: container.url,
title: container.title,
container: context.selector,
fragment: options.fragment,
timeout: options.timeout
}
if (options.push || options.replace) {
window.history.replaceState(pjax.state, container.title, container.url)
}
// Clear out any focused controls before inserting new page contents.
try {
document.activeElement.blur()
} catch (e) { }
if (container.title) document.title = container.title
fire('pjax:beforeReplace', [container.contents, options], {
state: pjax.state,
previousState: previousState
})
context.html(container.contents)
// FF bug: Won't autofocus fields that are inserted via JS.
// This behavior is incorrect. So if theres no current focus, autofocus
// the last field.
//
// http://www.w3.org/html/wg/drafts/html/master/forms.html
var autofocusEl = context.find('input[autofocus], textarea[autofocus]').last()[0]
if (autofocusEl && document.activeElement !== autofocusEl) {
autofocusEl.focus();
}
executeScriptTags(container.scripts)
var scrollTo = options.scrollTo
// Ensure browser scrolls to the element referenced by the URL anchor
if (hash) {
var name = decodeURIComponent(hash.slice(1))
var target = document.getElementById(name) || document.getElementsByName(name)[0]
if (target) scrollTo = $(target).offset().top
}
if (typeof scrollTo == 'number') $(window).scrollTop(scrollTo)
fire('pjax:success', [data, status, xhr, options])
}
// Initialize pjax.state for the initial page load. Assume we're
// using the container and options of the link we're loading for the
// back button to the initial page. This ensures good back button
// behavior.
if (!pjax.state) {
pjax.state = {
id: uniqueId(),
url: window.location.href,
title: document.title,
container: context.selector,
fragment: options.fragment,
timeout: options.timeout
}
window.history.replaceState(pjax.state, document.title)
}
// Cancel the current request if we're already pjaxing
abortXHR(pjax.xhr)
pjax.options = options
var xhr = pjax.xhr = $.ajax(options)
if (xhr.readyState > 0) {
if (options.push && !options.replace) {
// Cache current container element before replacing it
cachePush(pjax.state.id, cloneContents(context))
window.history.pushState(null, "", options.requestUrl)
}
fire('pjax:start', [xhr, options])
fire('pjax:send', [xhr, options])
}
return pjax.xhr
}
// Public: Reload current page with pjax.
//
// Returns whatever $.pjax returns.
function pjaxReload(container, options) {
var defaults = {
url: window.location.href,
push: false,
replace: true,
scrollTo: false
}
return pjax($.extend(defaults, optionsFor(container, options)))
}
// Internal: Hard replace current state with url.
//
// Work for around WebKit
// https://bugs.webkit.org/show_bug.cgi?id=93506
//
// Returns nothing.
function locationReplace(url) {
window.history.replaceState(null, "", pjax.state.url)
window.location.replace(url)
}
var initialPop = true
var initialURL = window.location.href
var initialState = window.history.state
// Initialize $.pjax.state if possible
// Happens when reloading a page and coming forward from a different
// session history.
if (initialState && initialState.container) {
pjax.state = initialState
}
// Non-webkit browsers don't fire an initial popstate event
if ('state' in window.history) {
initialPop = false
}
// popstate handler takes care of the back and forward buttons
//
// You probably shouldn't use pjax on pages with other pushState
// stuff yet.
function onPjaxPopstate(event) {
// Hitting back or forward should override any pending PJAX request.
if (!initialPop) {
abortXHR(pjax.xhr)
}
var previousState = pjax.state
var state = event.state
var direction
if (state && state.container) {
// When coming forward from a separate history session, will get an
// initial pop with a state we are already at. Skip reloading the current
// page.
if (initialPop && initialURL == state.url) return
if (previousState) {
// If popping back to the same state, just skip.
// Could be clicking back from hashchange rather than a pushState.
if (previousState.id === state.id) return
// Since state IDs always increase, we can deduce the navigation direction
direction = previousState.id < state.id ? 'forward' : 'back'
}
var cache = cacheMapping[state.id] || []
var container = $(cache[0] || state.container), contents = cache[1]
if (container.length) {
if (previousState) {
// Cache current container before replacement and inform the
// cache which direction the history shifted.
cachePop(direction, previousState.id, cloneContents(container))
}
var popstateEvent = $.Event('pjax:popstate', {
state: state,
direction: direction
})
container.trigger(popstateEvent)
var options = {
id: state.id,
url: state.url,
container: container,
push: false,
fragment: state.fragment,
timeout: state.timeout,
scrollTo: false
}
if (contents) {
container.trigger('pjax:start', [null, options])
pjax.state = state
if (state.title) document.title = state.title
var beforeReplaceEvent = $.Event('pjax:beforeReplace', {
state: state,
previousState: previousState
})
container.trigger(beforeReplaceEvent, [contents, options])
container.html(contents)
container.trigger('pjax:end', [null, options])
} else {
pjax(options)
}
// Force reflow/relayout before the browser tries to restore the
// scroll position.
container[0].offsetHeight
} else {
locationReplace(location.href)
}
}
initialPop = false
}
// Fallback version of main pjax function for browsers that don't
// support pushState.
//
// Returns nothing since it retriggers a hard form submission.
function fallbackPjax(options) {
var url = $.isFunction(options.url) ? options.url() : options.url,
method = options.type ? options.type.toUpperCase() : 'GET'
var form = $('<form>', {
method: method === 'GET' ? 'GET' : 'POST',
action: url,
style: 'display:none'
})
if (method !== 'GET' && method !== 'POST') {
form.append($('<input>', {
type: 'hidden',
name: '_method',
value: method.toLowerCase()
}))
}
var data = options.data
if (typeof data === 'string') {
$.each(data.split('&'), function(index, value) {
var pair = value.split('=')
form.append($('<input>', {type: 'hidden', name: pair[0], value: pair[1]}))
})
} else if ($.isArray(data)) {
$.each(data, function(index, value) {
form.append($('<input>', {type: 'hidden', name: value.name, value: value.value}))
})
} else if (typeof data === 'object') {
var key
for (key in data)
form.append($('<input>', {type: 'hidden', name: key, value: data[key]}))
}
$(document.body).append(form)
form.submit()
}
// Internal: Abort an XmlHttpRequest if it hasn't been completed,
// also removing its event handlers.
function abortXHR(xhr) {
if ( xhr && xhr.readyState < 4) {
xhr.onreadystatechange = $.noop
xhr.abort()
}
}
// Internal: Generate unique id for state object.
//
// Use a timestamp instead of a counter since ids should still be
// unique across page loads.
//
// Returns Number.
function uniqueId() {
return (new Date).getTime()
}
function cloneContents(container) {
var cloned = container.clone()
// Unmark script tags as already being eval'd so they can get executed again
// when restored from cache. HAXX: Uses jQuery internal method.
cloned.find('script').each(function(){
if (!this.src) jQuery._data(this, 'globalEval', false)
})
return [container.selector, cloned.contents()]
}
// Internal: Strip internal query params from parsed URL.
//
// Returns sanitized url.href String.
function stripInternalParams(url) {
url.search = url.search.replace(/([?&])(_pjax|_)=[^&]*/g, '')
return url.href.replace(/\?($|#)/, '$1')
}
// Internal: Parse URL components and returns a Locationish object.
//
// url - String URL
//
// Returns HTMLAnchorElement that acts like Location.
function parseURL(url) {
var a = document.createElement('a')
a.href = url
return a
}
// Internal: Return the `href` component of given URL object with the hash
// portion removed.
//
// location - Location or HTMLAnchorElement
//
// Returns String
function stripHash(location) {
return location.href.replace(/#.*/, '')
}
// Internal: Build options Object for arguments.
//
// For convenience the first parameter can be either the container or
// the options object.
//
// Examples
//
// optionsFor('#container')
// // => {container: '#container'}
//
// optionsFor('#container', {push: true})
// // => {container: '#container', push: true}
//
// optionsFor({container: '#container', push: true})
// // => {container: '#container', push: true}
//
// Returns options Object.
function optionsFor(container, options) {
// Both container and options
if ( container && options )
options.container = container
// First argument is options Object
else if ( $.isPlainObject(container) )
options = container
// Only container
else
options = {container: container}
// Find and validate container
if (options.container)
options.container = findContainerFor(options.container)
return options
}
// Internal: Find container element for a variety of inputs.
//
// Because we can't persist elements using the history API, we must be
// able to find a String selector that will consistently find the Element.
//
// container - A selector String, jQuery object, or DOM Element.
//
// Returns a jQuery object whose context is `document` and has a selector.
function findContainerFor(container) {
container = $(container)
if ( !container.length ) {
throw "no pjax container for " + container.selector
} else if ( container.selector !== '' && container.context === document ) {
return container
} else if ( container.attr('id') ) {
return $('#' + container.attr('id'))
} else {
throw "cant get selector for pjax container!"
}
}
// Internal: Filter and find all elements matching the selector.
//
// Where $.fn.find only matches descendants, findAll will test all the
// top level elements in the jQuery object as well.
//
// elems - jQuery object of Elements
// selector - String selector to match
//
// Returns a jQuery object.
function findAll(elems, selector) {
return elems.filter(selector).add(elems.find(selector));
}
function parseHTML(html) {
return $.parseHTML(html, document, true)
}
// Internal: Extracts container and metadata from response.
//
// 1. Extracts X-PJAX-URL header if set
// 2. Extracts inline <title> tags
// 3. Builds response Element and extracts fragment if set
//
// data - String response data
// xhr - XHR response
// options - pjax options Object
//
// Returns an Object with url, title, and contents keys.
function extractContainer(data, xhr, options) {
var obj = {}, fullDocument = /<html/i.test(data)
// Prefer X-PJAX-URL header if it was set, otherwise fallback to
// using the original requested url.
var serverUrl = xhr.getResponseHeader('X-PJAX-URL')
obj.url = serverUrl ? stripInternalParams(parseURL(serverUrl)) : options.requestUrl
// Attempt to parse response html into elements
if (fullDocument) {
var $head = $(parseHTML(data.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0]))
var $body = $(parseHTML(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0]))
} else {
var $head = $body = $(parseHTML(data))
}
// If response data is empty, return fast
if ($body.length === 0)
return obj
// If there's a <title> tag in the header, use it as
// the page's title.
obj.title = findAll($head, 'title').last().text()
if (options.fragment) {
// If they specified a fragment, look for it in the response
// and pull it out.
if (options.fragment === 'body') {
var $fragment = $body
} else {
var $fragment = findAll($body, options.fragment).first()
}
if ($fragment.length) {
obj.contents = options.fragment === 'body' ? $fragment : $fragment.contents()
// If there's no title, look for data-title and title attributes
// on the fragment
if (!obj.title)
obj.title = $fragment.attr('title') || $fragment.data('title')
}
} else if (!fullDocument) {
obj.contents = $body
}
// Clean up any <title> tags
if (obj.contents) {
// Remove any parent title elements
obj.contents = obj.contents.not(function() { return $(this).is('title') })
// Then scrub any titles from their descendants
obj.contents.find('title').remove()
// Gather all script[src] elements
obj.scripts = findAll(obj.contents, 'script[src]').remove()
obj.contents = obj.contents.not(obj.scripts)
}
// Trim any whitespace off the title
if (obj.title) obj.title = $.trim(obj.title)
return obj
}
// Load an execute scripts using standard script request.
//
// Avoids jQuery's traditional $.getScript which does a XHR request and
// globalEval.
//
// scripts - jQuery object of script Elements
//
// Returns nothing.
function executeScriptTags(scripts) {
if (!scripts) return
var existingScripts = $('script[src]')
scripts.each(function() {
var src = this.src
var matchedScripts = existingScripts.filter(function() {
return this.src === src
})
if (matchedScripts.length) return
var script = document.createElement('script')
var type = $(this).attr('type')
if (type) script.type = type
script.src = $(this).attr('src')
document.head.appendChild(script)
})
}
// Internal: History DOM caching class.
var cacheMapping = {}
var cacheForwardStack = []
var cacheBackStack = []
// Push previous state id and container contents into the history
// cache. Should be called in conjunction with `pushState` to save the
// previous container contents.
//
// id - State ID Number
// value - DOM Element to cache
//
// Returns nothing.
function cachePush(id, value) {
cacheMapping[id] = value
cacheBackStack.push(id)
// Remove all entries in forward history stack after pushing a new page.
trimCacheStack(cacheForwardStack, 0)
// Trim back history stack to max cache length.
trimCacheStack(cacheBackStack, pjax.defaults.maxCacheLength)
}
// Shifts cache from directional history cache. Should be
// called on `popstate` with the previous state id and container
// contents.
//
// direction - "forward" or "back" String
// id - State ID Number
// value - DOM Element to cache
//
// Returns nothing.
function cachePop(direction, id, value) {
var pushStack, popStack
cacheMapping[id] = value
if (direction === 'forward') {
pushStack = cacheBackStack
popStack = cacheForwardStack
} else {
pushStack = cacheForwardStack
popStack = cacheBackStack
}
pushStack.push(id)
if (id = popStack.pop())
delete cacheMapping[id]
// Trim whichever stack we just pushed to to max cache length.
trimCacheStack(pushStack, pjax.defaults.maxCacheLength)
}
// Trim a cache stack (either cacheBackStack or cacheForwardStack) to be no
// longer than the specified length, deleting cached DOM elements as necessary.
//
// stack - Array of state IDs
// length - Maximum length to trim to
//
// Returns nothing.
function trimCacheStack(stack, length) {
while (stack.length > length)
delete cacheMapping[stack.shift()]
}
// Public: Find version identifier for the initial page load.
//
// Returns String version or undefined.
function findVersion() {
return $('meta').filter(function() {
var name = $(this).attr('http-equiv')
return name && name.toUpperCase() === 'X-PJAX-VERSION'
}).attr('content')
}
// Install pjax functions on $.pjax to enable pushState behavior.
//
// Does nothing if already enabled.
//
// Examples
//
// $.pjax.enable()
//
// Returns nothing.
function enable() {
$.fn.pjax = fnPjax
$.pjax = pjax
$.pjax.enable = $.noop
$.pjax.disable = disable
$.pjax.click = handleClick
$.pjax.submit = handleSubmit
$.pjax.reload = pjaxReload
$.pjax.defaults = {
timeout: 650,
push: true,
replace: false,
type: 'GET',
dataType: 'html',
scrollTo: 0,
maxCacheLength: 20,
version: findVersion
}
$(window).on('popstate.pjax', onPjaxPopstate)
}
// Disable pushState behavior.
//
// This is the case when a browser doesn't support pushState. It is
// sometimes useful to disable pushState for debugging on a modern
// browser.
//
// Examples
//
// $.pjax.disable()
//
// Returns nothing.
function disable() {
$.fn.pjax = function() { return this }
$.pjax = fallbackPjax
$.pjax.enable = enable
$.pjax.disable = $.noop
$.pjax.click = $.noop
$.pjax.submit = $.noop
$.pjax.reload = function() { window.location.reload() }
$(window).off('popstate.pjax', onPjaxPopstate)
}
// Add the state property to jQuery's event object so we can use it in
// $(window).bind('popstate')
if ( $.inArray('state', $.event.props) < 0 )
$.event.props.push('state')
// Is pjax supported by this browser?
$.support.pjax =
window.history && window.history.pushState && window.history.replaceState &&
// pushState isn't reliable on iOS until 5.
!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]\D|WebApps\/.+CFNetwork)/)
$.support.pjax ? enable() : disable()
})(jQuery);

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,7 @@
"grunt-contrib-concat": "^1.0.0",
"grunt-contrib-uglify": "^1.0.1",
"grunt-contrib-watch": "^1.0.0",
"grunt-contrib-cssmin": "^1.0.1",
"grunt-contrib-less": "^1.3.0"
}
}

34
protected/asset.php Normal file
View File

@ -0,0 +1,34 @@
<?php
/**
* Configuration file for the "yii asset" console command.
*/
// In the console environment, some path aliases may not exist. Please define these:
Yii::setAlias('@webroot', __DIR__ . '/..');
Yii::setAlias('@web', '/');
return [
// Adjust command/callback for JavaScript files compressing:
'jsCompressor' => 'grunt uglify:assets --from={from} --to={to} --mangle --compress',
// Adjust command/callback for CSS files compressing:
'cssCompressor' => 'grunt cssmin --from={from} --to={to}',
// The list of asset bundles to compress:
'bundles' => [
'humhub\assets\AppAsset',
],
// Asset bundle for compression output:
'targets' => [
'all' => [
'class' => 'yii\web\AssetBundle',
'basePath' => '@webroot',
'baseUrl' => '@web',
'js' => 'js/all-{hash}.js',
'css' => 'css/all-{hash}.css',
],
],
// Asset manager configuration:
'assetManager' => [
'basePath' => '@webroot/assets',
'baseUrl' => '@web/assets',
],
];

View File

@ -0,0 +1,36 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\assets;
use yii\web\AssetBundle;
/**
* animate.css
*
* @author buddha
*/
class AnimateCssAsset extends AssetBundle
{
/**
* @inheritdoc
*/
public $sourcePath = '@bower/animate.css';
/**
* @inheritdoc
*/
public $js = [];
/**
* @inheritdoc
*/
public $css = ['animate.min.css'];
}

View File

@ -10,45 +10,34 @@ namespace humhub\assets;
use yii\web\AssetBundle;
/**
* The AppAsset assets are included in the core layout.
* This Assetbundle includes some core dependencies and the humhub core api.
*/
class AppAsset extends AssetBundle
{
public $basePath = '@webroot';
public $baseUrl = '@web';
public $css = [
'css/animate.min.css',
'css/temp.css',
'css/bootstrap-wysihtml5.css',
'css/flatelements.css',
'resources/at/jquery.atwho.css',
];
public $jsOptions = ['position' => \yii\web\View::POS_BEGIN];
public $js = [
'js/ekko-lightbox-modified.js',
'js/modernizr.js',
'js/jquery.cookie.js',
//'js/modernizr.js', // In use???
'js/jquery.highlight.min.js',
'js/jquery.autosize.min.js',
'js/wysihtml5-0.3.0.js',
'js/bootstrap3-wysihtml5.js',
'js/jquery.color-2.1.0.min.js',
'js/jquery.flatelements.js',
'js/jquery.loader.js',
//'js/wysihtml5-0.3.0.js',
//'js/bootstrap3-wysihtml5.js',
'js/desktop-notify-min.js',
'js/desktop-notify-config.js',
'js/jquery.pjax.js',
'resources/at/jquery.caret.min.js',
'resources/at/jquery.atwho.min.js',
'resources/file/fileuploader.js',
'resources/user/userpicker.js',
'js/jquery.nicescroll.min.js',
'js/app.js',
'js/dist/humhub.all.js',
];
//TODO if !debug use humhub.all.min.js
public $depends = [
'yii\web\YiiAsset',
'yii\bootstrap\BootstrapAsset',
@ -60,11 +49,20 @@ class AppAsset extends AssetBundle
//'humhub\assets\JqueryNiceScrollAsset',
'humhub\assets\BluebirdAsset',
'humhub\assets\JqueryTimeAgoAsset',
'humhub\assets\JqueryKnobAsset',
'humhub\assets\JqueryWidgetAsset',
'humhub\assets\JqueryColorAsset', //TODO: only required for post/comment/stream
'humhub\assets\JqueryPlaceholderAsset',
'humhub\assets\FontAwesomeAsset',
'humhub\assets\BlueimpFileUploadAsset',
'humhub\assets\JqueryHighlightAsset',
'humhub\assets\JqueryCookieAsset',
'humhub\assets\JqueryAutosizeAsset',
'humhub\assets\JqueryPjaxAsset',
'humhub\assets\AtJsAsset',
'humhub\assets\AnimateCssAsset',
'humhub\assets\CoreApiAsset',
'humhub\modules\content\assets\ContentAsset'
];
}

View File

@ -0,0 +1,44 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\assets;
use yii\web\AssetBundle;
/**
* jquery-At.js
*
* @author buddha
*/
class AtJsAsset extends AssetBundle
{
/**
* @inheritdoc
*/
public $sourcePath = '@bower/At.js';
/**
* @inheritdoc
*/
public $js = ['dist/js/jquery.atwho.min.js'];
/**
* @inheritdoc
*/
public $css = [];
/**
* @inheritdoc
*/
public $depends = [
'humhub\assets\CaretJsAsset',
'humhub\assets\AtJsStyleAsset'
];
}

View File

@ -0,0 +1,30 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\assets;
use yii\web\AssetBundle;
/**
* jquery-At.js
*
* @author buddha
*/
class AtJsStyleAsset extends AssetBundle
{
/**
* @inheritdoc
*/
public $basePath = '@webroot';
public $baseUrl = '@web';
public $css = [
'css/jquery.atwho.modified.css',
];
}

View File

@ -0,0 +1,36 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\assets;
use yii\web\AssetBundle;
/**
* jquery-caretjs.js
*
* @author buddha
*/
class CaretjsAsset extends AssetBundle
{
/**
* @inheritdoc
*/
public $sourcePath = '@bower/caret.js';
/**
* @inheritdoc
*/
public $js = ['dist/jquery.caret.min.js'];
/**
* @inheritdoc
*/
public $css = [];
}

View File

@ -0,0 +1,65 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\assets;
use yii\web\AssetBundle;
/**
* HumHub Core Api Asset
*/
class CoreApiAsset extends AssetBundle
{
/**
* @inheritdoc
*/
public $basePath = '@webroot';
/**
* @inheritdoc
*/
public $baseUrl = '@web';
/**
* @inheritdoc
*/
public $css = [];
/**
* @inheritdoc
*/
public $jsOptions = ['position' => \yii\web\View::POS_BEGIN];
/**
* @inheritdoc
*/
public $js = [
'js/humhub/legacy/jquery.flatelements.js',
'js/humhub/legacy/jquery.loader.js',
'js/humhub/legacy/app.js',
'js/humhub/humhub.core.js',
'js/humhub/humhub.util.js',
'js/humhub/humhub.additions.js',
'js/humhub/humhub.client.js',
'js/humhub/humhub.ui.js',
'js/humhub/humhub.ui.modal.js',
'js/humhub/humhub.actions.js'
];
/**
* @inheritdoc
*/
public $depends = [
'yii\web\YiiAsset',
'yii\bootstrap\BootstrapAsset',
'yii\bootstrap\BootstrapPluginAsset',
'humhub\assets\BluebirdAsset',
];
}

View File

@ -0,0 +1,36 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\assets;
use yii\web\AssetBundle;
/**
* jquery-autosize
*
* @author buddha
*/
class JqueryAutosizeAsset extends AssetBundle
{
/**
* @inheritdoc
*/
public $sourcePath = '@bower/autosize';
/**
* @inheritdoc
*/
public $js = ['jquery.autosize.min.js'];
/**
* @inheritdoc
*/
public $css = [];
}

View File

@ -0,0 +1,36 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\assets;
use yii\web\AssetBundle;
/**
* jquery-color
*
* @author buddha
*/
class JqueryColorAsset extends AssetBundle
{
/**
* @inheritdoc
*/
public $sourcePath = '@bower/jquery-color';
/**
* @inheritdoc
*/
public $js = ['jquery.color.js'];
/**
* @inheritdoc
*/
public $css = [];
}

View File

@ -0,0 +1,31 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\assets;
use yii\web\AssetBundle;
/**
* jquery-cookie
*
* @author buddha
*/
class JqueryCookieAsset extends AssetBundle
{
/**
* @inheritdoc
*/
public $sourcePath = '@bower/jquery.cookie';
/**
* @inheritdoc
*/
public $js = ['jquery.cookie.js'];
}

View File

@ -0,0 +1,35 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\assets;
use yii\web\AssetBundle;
/**
* jquery-highlight
*
* @author buddha
*/
class JqueryHighlightAsset extends AssetBundle
{
/**
* @inheritdoc
*/
public $basePath = '@webroot';
/**
* @inheritdoc
*/
public $baseUrl = '@web';
/**
* @inheritdoc
*/
public $js = ['js/jquery.highlight.min.js'];
}

View File

@ -27,5 +27,7 @@ class JqueryKnobAsset extends AssetBundle
* @inheritdoc
*/
public $js = ['dist/jquery.knob.min.js'];
public $depends = ['humhub\assets\AppAsset'];
}

View File

@ -0,0 +1,36 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\assets;
use yii\web\AssetBundle;
/**
* jquery-pjax
*
* @author buddha
*/
class JqueryPjaxAsset extends AssetBundle
{
/**
* @inheritdoc
*/
public $sourcePath = '@bower/jquery-pjax';
/**
* @inheritdoc
*/
public $js = ['jquery.pjax.min.js'];
/**
* @inheritdoc
*/
public $css = [];
}

View File

@ -11,9 +11,9 @@ namespace humhub\assets;
use yii\web\AssetBundle;
/**
* jquery-knob
* select2
*
* @author luke
* @author buddha
*/
class Select2Asset extends AssetBundle
{

View File

@ -11,9 +11,9 @@ namespace humhub\assets;
use yii\web\AssetBundle;
/**
* jquery-knob
* select2 bootstrap asset
*
* @author luke
* @author buddha
*/
class Select2BootstrapAsset extends AssetBundle
{

View File

@ -11,9 +11,9 @@ namespace humhub\assets;
use yii\web\AssetBundle;
/**
* jquery-knob
* select2 extension asset
*
* @author luke
* @author buddha
*/
class Select2ExtensionAsset extends AssetBundle
{

View File

@ -11,9 +11,9 @@ namespace humhub\assets;
use yii\web\AssetBundle;
/**
* jquery-knob
* tabbed form asset
*
* @author luke
* @author buddha
*/
class TabbedFormAsset extends AssetBundle
{

View File

@ -0,0 +1,7 @@
<?php
/**
* This file is generated by the "yii asset" command.
* DO NOT MODIFY THIS FILE DIRECTLY.
* @version 2016-08-20 20:22:58
*/
return [];

View File

@ -0,0 +1,138 @@
<?php
/**
* This file is generated by the "yii asset" command.
* DO NOT MODIFY THIS FILE DIRECTLY.
* @version 2016-08-20 20:43:34
*/
return [
'all' => [
'class' => 'yii\\web\\AssetBundle',
'basePath' => '@webroot',
'baseUrl' => '@web',
'js' => [
'js/all-79ed2e2e2a9df9d9577d0964f06869e9.js',
],
'css' => [
'css/all-3df06579f2945d9873aa4ec9736097a2.css',
],
],
'yii\\web\\JqueryAsset' => [
'sourcePath' => null,
'js' => [],
'css' => [],
'depends' => [
'all',
],
],
'yii\\web\\YiiAsset' => [
'sourcePath' => null,
'js' => [],
'css' => [],
'depends' => [
'yii\\web\\JqueryAsset',
'all',
],
],
'yii\\bootstrap\\BootstrapAsset' => [
'sourcePath' => null,
'js' => [],
'css' => [],
'depends' => [
'all',
],
],
'yii\\bootstrap\\BootstrapPluginAsset' => [
'sourcePath' => null,
'js' => [],
'css' => [],
'depends' => [
'yii\\web\\JqueryAsset',
'yii\\bootstrap\\BootstrapAsset',
'all',
],
],
'humhub\\assets\\BluebirdAsset' => [
'sourcePath' => null,
'js' => [],
'css' => [],
'depends' => [
'all',
],
],
'humhub\\assets\\JqueryTimeAgoAsset' => [
'sourcePath' => null,
'js' => [],
'css' => [],
'depends' => [
'all',
],
],
'humhub\\assets\\JqueryWidgetAsset' => [
'sourcePath' => null,
'js' => [],
'css' => [],
'depends' => [
'all',
],
],
'humhub\\assets\\JqueryColorAsset' => [
'sourcePath' => null,
'js' => [],
'css' => [],
'depends' => [
'all',
],
],
'humhub\\assets\\JqueryPlaceholderAsset' => [
'sourcePath' => null,
'js' => [],
'css' => [],
'depends' => [
'all',
],
],
'humhub\\assets\\FontAwesomeAsset' => [
'sourcePath' => null,
'js' => [],
'css' => [],
'depends' => [
'all',
],
],
'humhub\\assets\\BlueimpFileUploadAsset' => [
'sourcePath' => null,
'js' => [],
'css' => [],
'depends' => [
'humhub\\assets\\JqueryWidgetAsset',
'all',
],
],
'humhub\\assets\\JqueryHighlightAsset' => [
'sourcePath' => null,
'js' => [],
'css' => [],
'depends' => [
'all',
],
],
'humhub\\assets\\AppAsset' => [
'sourcePath' => null,
'js' => [],
'css' => [],
'depends' => [
'yii\\web\\YiiAsset',
'yii\\bootstrap\\BootstrapAsset',
'yii\\bootstrap\\BootstrapPluginAsset',
'humhub\\assets\\BluebirdAsset',
'humhub\\assets\\JqueryTimeAgoAsset',
'humhub\\assets\\JqueryWidgetAsset',
'humhub\\assets\\JqueryColorAsset',
'humhub\\assets\\JqueryPlaceholderAsset',
'humhub\\assets\\FontAwesomeAsset',
'humhub\\assets\\BlueimpFileUploadAsset',
'humhub\\assets\\JqueryHighlightAsset',
'all',
],
],
];

View File

@ -85,6 +85,8 @@ $config = [
],
'assetManager' => [
'appendTimestamp' => true,
'bundles' => require(__DIR__ . '/' . (YII_ENV_PROD ? 'assets-prod.php' : 'assets-dev.php')),
#'bundles' => require(__DIR__ . '/' . 'assets-prod.php'),
],
'view' => [
'class' => '\humhub\components\View',

View File

@ -1,3 +1,21 @@
Build
========
## Setup
- Install NPM
- Install Grunt (http://gruntjs.com/getting-started)
- call npm update in humhub root
> Note: Since symlinks are not supported in some virtual machine shared folders the update command should be called from the host
## Assets
- Yii asset management http://www.yiiframework.com/doc-2.0/guide-structure-assets.html#combining-compressing-assets
- php yii asset asset.php humhub/config/assets-prod.php
### Grunt Tasks
- watch
- uglify
- cssmin
(TBD)

View File

@ -0,0 +1,26 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace humhub\modules\content\assets;
use yii\web\AssetBundle;
class ContentAsset extends AssetBundle
{
public $sourcePath = '@content/assets';
public $css = [];
public $js = [
'js/humhub.content.js'
];
public $depends = [
'humhub\assets\CoreApiAsset'
];
}

View File

@ -0,0 +1,24 @@
actor: Tester
namespace: dashboard
settings:
bootstrap: _bootstrap.php
suite_class: \PHPUnit_Framework_TestSuite
colors: true
shuffle: false
memory_limit: 1024M
log: true
# This value controls whether PHPUnit attempts to backup global variables
# See https://phpunit.de/manual/current/en/appendixes.annotations.html#appendixes.annotations.backupGlobals
backup_globals: true
paths:
tests: codeception
log: codeception/_output
data: codeception/_data
helpers: codeception/_support
envs: config/env
config:
# the entry script URL (with host info) for functional and acceptance tests
# PLEASE ADJUST IT TO THE ACTUAL ENTRY SCRIPT URL
test_entry_url: http://localhost:8080/index-test.php

View File

@ -0,0 +1,29 @@
<?php
/**
* This is the initial test bootstrap, which will load the default test bootstrap from the humhub core
*/
$testRoot = dirname(__DIR__);
\Codeception\Configuration::append(['test_root' => $testRoot]);
$humhubPath = getenv("HUMHUB_PATH");
// If no environment path was set, we assume residing in default the modules directory
if($humhubPath == null) {
$testCfg = require_once($testRoot.'/config/test.php');
if(isset($testCfg['humhub_root'])) {
$humhubPath = $testCfg['humhub_root'];
} else {
$humhubPath = dirname(__DIR__).'../../../../';
}
}
\Codeception\Configuration::append(['humhub_root' => $humhubPath]);
// Load test configuration (/config/test.php or /config/env/<environment>/test.php
$cfg = require($humhubPath . '/protected/humhub/tests/codeception/_loadConfig.php');
print_r('Using HumHub Root: ' . $cfg['humhub_root']);
// Load default test bootstrap (initialize Yii...)
require_once($cfg['humhub_root'] . '/protected/humhub/tests/codeception/_bootstrap.php');

View File

@ -0,0 +1,26 @@
<?php
namespace dashboard;
/**
* Inherited Methods
* @method void wantToTest($text)
* @method void wantTo($text)
* @method void execute($callable)
* @method void expectTo($prediction)
* @method void expect($prediction)
* @method void amGoingTo($argumentation)
* @method void am($role)
* @method void lookForwardTo($achieveValue)
* @method void comment($description)
* @method \Codeception\Lib\Friend haveFriend($name, $actorClass = null)
*
* @SuppressWarnings(PHPMD)
*/
class AcceptanceTester extends \AcceptanceTester
{
use _generated\AcceptanceTesterActions;
/**
* Define custom actions here
*/
}

View File

@ -0,0 +1,26 @@
<?php
namespace dashboard;
/**
* Inherited Methods
* @method void wantToTest($text)
* @method void wantTo($text)
* @method void execute($callable)
* @method void expectTo($prediction)
* @method void expect($prediction)
* @method void amGoingTo($argumentation)
* @method void am($role)
* @method void lookForwardTo($achieveValue)
* @method void comment($description)
* @method \Codeception\Lib\Friend haveFriend($name, $actorClass = null)
*
* @SuppressWarnings(PHPMD)
*/
class FunctionalTester extends \FunctionalTester
{
use _generated\FunctionalTesterActions;
/**
* Define custom actions here
*/
}

View File

@ -0,0 +1,26 @@
<?php
namespace dashboard;
/**
* Inherited Methods
* @method void wantToTest($text)
* @method void wantTo($text)
* @method void execute($callable)
* @method void expectTo($prediction)
* @method void expect($prediction)
* @method void amGoingTo($argumentation)
* @method void am($role)
* @method void lookForwardTo($achieveValue)
* @method void comment($description)
* @method \Codeception\Lib\Friend haveFriend($name, $actorClass = null)
*
* @SuppressWarnings(PHPMD)
*/
class UnitTester extends \UnitTester
{
use _generated\UnitTesterActions;
/**
* Define custom actions here
*/
}

View File

@ -0,0 +1,38 @@
<?php //[STAMP] 0468b7e2480518455b63a22d3aa6f7c2
namespace dashboard\_generated;
// This class was automatically generated by build task
// You should not change it manually as it will be overwritten on next build
// @codingStandardsIgnoreFile
use tests\codeception\_support\CodeHelper;
trait UnitTesterActions
{
/**
* @return \Codeception\Scenario
*/
abstract protected function getScenario();
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\CodeHelper::assertContainsError()
*/
public function assertContainsError($model, $message) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertContainsError', func_get_args()));
}
/**
* [!] Method is generated. Documentation taken from corresponding module.
*
*
* @see \tests\codeception\_support\CodeHelper::assertNotContainsError()
*/
public function assertNotContainsError($model, $message) {
return $this->getScenario()->runStep(new \Codeception\Step\Action('assertNotContainsError', func_get_args()));
}
}

View File

@ -0,0 +1,25 @@
# Codeception Test Suite Configuration
# suite for acceptance tests.
# perform tests in browser using the Selenium-like tools.
# powered by Mink (http://mink.behat.org).
# (tip: that's what your customer will see).
# (tip: test your ajax and javascript by one of Mink drivers).
# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
class_name: AcceptanceTester
modules:
enabled:
#- PhpBrowser
- WebDriver
- tests\codeception\_support\WebHelper
- tests\codeception\_support\DynamicFixtureHelper
config:
PhpBrowser:
url: http://localhost:8080/
WebDriver:
url: http://localhost:8080/
browser: phantomjs
window_size: 1024x768
lang: en

View File

@ -0,0 +1,32 @@
<?php
namespace dashboard\acceptance;
use dashboard\AcceptanceTester;
class DashboardCest
{
public function testDashboardInputBar(AcceptanceTester $I)
{
$I->amAdmin();
$I->wantToTest('the dashboard input bar is working');
$I->amGoingTo('activate the input bar');
$I->amOnPage('index-test.php?r=admin/setting/basic');
$I->jsClick('#basicsettingsform-dashboardshowprofilepostform'); // Active
$I->click('Save');
$I->amOnPage('index-test.php?r=dashboard/dashboard');
$I->expectTo('see the dashboard input');
$I->seeElement('#contentForm_message_contenteditable');
$I->fillField('#contentForm_message_contenteditable', 'My Test Post');
$I->click('#post_submit_button');
$I->wait(6);
$I->expectTo('see my newly created post');
$I->see('My Test Post', '.wall-entry');
}
}

View File

@ -0,0 +1,6 @@
<?php
/**
* Initialize the HumHub Application for functional testing. The default application configuration for this suite can be overwritten
* in @tests/config/functional.php
*/
require(Yii::getAlias('@humhubTests/codeception/acceptance/_bootstrap.php'));

View File

@ -0,0 +1,3 @@
<?php
return \tests\codeception\_support\HumHubTestConfiguration::getSuiteConfig('functional');

View File

@ -0,0 +1,3 @@
<?php
return \tests\codeception\_support\HumHubTestConfiguration::getSuiteConfig('unit');

View File

@ -0,0 +1,19 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace tests\codeception\fixtures\modules\custom_pages\template;
use yii\test\ActiveFixture;
class ContainerContentDefinitionFixture extends ActiveFixture
{
public $modelClass = 'humhub\modules\custom_pages\modules\template\models\ContainerContentDefinition';
public $dataFile = '@custom_pages/tests/codeception/fixtures/data/containerContentDefinition.php';
}

View File

@ -0,0 +1,19 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace tests\codeception\fixtures\modules\custom_pages\template;
use yii\test\ActiveFixture;
class ContainerContentFixture extends ActiveFixture
{
public $modelClass = 'humhub\modules\custom_pages\modules\template\models\ContainerContent';
public $dataFile = '@custom_pages/tests/codeception/fixtures/data/containerContent.php';
}

View File

@ -0,0 +1,19 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace tests\codeception\fixtures\modules\custom_pages\template;
use yii\test\ActiveFixture;
class ContainerContentItemFixture extends ActiveFixture
{
public $modelClass = 'humhub\modules\custom_pages\modules\template\models\ContainerContentItem';
public $dataFile = '@custom_pages/tests/codeception/fixtures/data/containerContentItem.php';
}

View File

@ -0,0 +1,19 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace tests\codeception\fixtures\modules\custom_pages\template;
use yii\test\ActiveFixture;
class OwnerContentFixture extends ActiveFixture
{
public $modelClass = 'humhub\modules\custom_pages\modules\template\models\OwnerContent';
public $dataFile = '@custom_pages/tests/codeception/fixtures/data/ownerContent.php';
}

View File

@ -0,0 +1,19 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace tests\codeception\fixtures\modules\custom_pages\template;
use yii\test\ActiveFixture;
class PageFixture extends ActiveFixture
{
public $modelClass = 'humhub\modules\custom_pages\models\Page';
public $dataFile = '@custom_pages/tests/codeception/fixtures/data/page.php';
}

View File

@ -0,0 +1,19 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace tests\codeception\fixtures\modules\custom_pages\template;
use yii\test\ActiveFixture;
class RichtextContentFixture extends ActiveFixture
{
public $modelClass = 'humhub\modules\custom_pages\modules\template\models\RichtextContent';
public $dataFile = '@custom_pages/tests/codeception/fixtures/data/richtextContent.php';
}

View File

@ -0,0 +1,32 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace tests\codeception\fixtures\modules\custom_pages\template;
use yii\test\ActiveFixture;
class TemplateElementFixture extends ActiveFixture
{
public $modelClass = 'humhub\modules\custom_pages\modules\template\models\TemplateElement';
public $dataFile = '@custom_pages/tests/codeception/fixtures/data/templateElement.php';
public $depends = ['tests\codeception\fixtures\modules\custom_pages\template\TemplateFixture'];
public function beforeLoad()
{
parent::beforeLoad();
$this->db->createCommand()->setSql('SET FOREIGN_KEY_CHECKS = 0')->execute();
}
public function afterLoad()
{
parent::afterLoad();
$this->db->createCommand()->setSql('SET FOREIGN_KEY_CHECKS = 1')->execute();
}
}

View File

@ -0,0 +1,25 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace tests\codeception\fixtures\modules\custom_pages\template;
use yii\test\ActiveFixture;
class TemplateFixture extends ActiveFixture
{
public $modelClass = 'humhub\modules\custom_pages\modules\template\models\Template';
public $dataFile = '@custom_pages/tests/codeception/fixtures/data/template.php';
public function afterLoad()
{
parent::afterLoad();
$this->db->createCommand()->setSql('SET FOREIGN_KEY_CHECKS = 1')->execute();
}
}

View File

@ -0,0 +1,32 @@
<?php
/**
* @link https://www.humhub.org/
* @copyright Copyright (c) 2015 HumHub GmbH & Co. KG
* @license https://www.humhub.com/licences
*/
namespace tests\codeception\fixtures\modules\custom_pages\template;
use yii\test\ActiveFixture;
class TemplateInstanceFixture extends ActiveFixture
{
public $modelClass = 'humhub\modules\custom_pages\modules\template\models\TemplateInstance';
public $dataFile = '@custom_pages/tests/codeception/fixtures/data/templateInstance.php';
public $depends = ['tests\codeception\fixtures\modules\custom_pages\template\TemplateFixture'];
public function beforeLoad()
{
parent::beforeLoad();
$this->db->createCommand()->setSql('SET FOREIGN_KEY_CHECKS = 0')->execute();
}
public function afterLoad()
{
parent::afterLoad();
$this->db->createCommand()->setSql('SET FOREIGN_KEY_CHECKS = 1')->execute();
}
}

View File

@ -0,0 +1,23 @@
<?php
/**
* HumHub
* Copyright © 2014 The HumHub Project
*
* The texts of the GNU Affero General Public License with an additional
* permission and of our proprietary license can be found at and
* in the LICENSE file you have received along with this program.
*
* According to our dual licensing model, this program can be used either
* under the terms of the GNU Affero General Public License, version 3,
* or under a proprietary license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*/
return [
['id' => 1, 'definition_id' => 1],
['id' => 2, 'definition_id' => 2]
];

View File

@ -0,0 +1,23 @@
<?php
/**
* HumHub
* Copyright © 2014 The HumHub Project
*
* The texts of the GNU Affero General Public License with an additional
* permission and of our proprietary license can be found at and
* in the LICENSE file you have received along with this program.
*
* According to our dual licensing model, this program can be used either
* under the terms of the GNU Affero General Public License, version 3,
* or under a proprietary license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*/
return [
['id' => 1, 'allow_multiple' => 0, 'is_default' => 1, 'is_inline' => 0],
['id' => 2, 'allow_multiple' => 1, 'is_default' => 1, 'is_inline' => 1]
];

View File

@ -0,0 +1,26 @@
<?php
/**
* HumHub
* Copyright © 2014 The HumHub Project
*
* The texts of the GNU Affero General Public License with an additional
* permission and of our proprietary license can be found at and
* in the LICENSE file you have received along with this program.
*
* According to our dual licensing model, this program can be used either
* under the terms of the GNU Affero General Public License, version 3,
* or under a proprietary license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*/
return [
['id' => 1, 'template_id' => 3, 'container_content_id' => 1, 'sort_order' => 0, 'title' => 'test1'],
['id' => 2, 'template_id' => 4, 'container_content_id' => 2, 'sort_order' => 0, 'title' => 'test2'],
['id' => 3, 'template_id' => 4, 'container_content_id' => 2, 'sort_order' => 1, 'title' => 'test3'],
['id' => 4, 'template_id' => 4, 'container_content_id' => 2, 'sort_order' => 2, 'title' => 'test4'],
];

View File

@ -0,0 +1,33 @@
<?php
/**
* HumHub
* Copyright © 2014 The HumHub Project
*
* The texts of the GNU Affero General Public License with an additional
* permission and of our proprietary license can be found at and
* in the LICENSE file you have received along with this program.
*
* According to our dual licensing model, this program can be used either
* under the terms of the GNU Affero General Public License, version 3,
* or under a proprietary license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*/
return [
['id' => 1, 'element_name' => 'test_content', 'owner_model' => 'humhub\modules\custom_pages\modules\template\models\Template', 'owner_id' => 1, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\models\\RichtextContent', 'content_id' => 1],
// Container of Layout1
['id' => 2, 'element_name' => 'container', 'owner_model' => 'humhub\modules\custom_pages\modules\template\models\TemplateInstance', 'owner_id' => 2, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\models\\ContainerContent', 'content_id' => 1],
// Sub Container
['id' => 3,'element_name' => 'container', 'owner_model' => 'humhub\modules\custom_pages\modules\template\models\ContainerContentItem', 'owner_id' => 1, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\models\\ContainerContent', 'content_id' => 2],
['id' => 4,'element_name' => 'text', 'owner_model' => 'humhub\modules\custom_pages\modules\template\models\ContainerContentItem', 'owner_id' => 1, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\models\\RichtextContent', 'content_id' => 2],
['id' => 5, 'element_name' => 'text', 'owner_model' => 'humhub\modules\custom_pages\modules\template\models\ContainerContentItem', 'owner_id' => 2, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\models\\RichtextContent', 'content_id' => 3],
['id' => 6, 'element_name' => 'text', 'owner_model' => 'humhub\modules\custom_pages\modules\template\models\ContainerContentItem', 'owner_id' => 3, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\models\\RichtextContent', 'content_id' => 4],
['id' => 7, 'element_name' => 'text', 'owner_model' => 'humhub\modules\custom_pages\modules\template\models\ContainerContentItem', 'owner_id' => 4, 'content_type' => 'humhub\\modules\\custom_pages\\modules\\template\\models\\RichtextContent', 'content_id' => 5],
];

View File

@ -0,0 +1,23 @@
<?php
/**
* HumHub
* Copyright © 2014 The HumHub Project
*
* The texts of the GNU Affero General Public License with an additional
* permission and of our proprietary license can be found at and
* in the LICENSE file you have received along with this program.
*
* According to our dual licensing model, this program can be used either
* under the terms of the GNU Affero General Public License, version 3,
* or under a proprietary license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*/
return [
['id' => 1, 'type' => '5', 'title' => 'test', 'navigation_class' => 'TopMenuWidget'],
['id' => 2, 'type' => '5', 'title' => 'test2', 'navigation_class' => 'TopMenuWidget']
];

View File

@ -0,0 +1,27 @@
<?php
/**
* HumHub
* Copyright © 2014 The HumHub Project
*
* The texts of the GNU Affero General Public License with an additional
* permission and of our proprietary license can be found at and
* in the LICENSE file you have received along with this program.
*
* According to our dual licensing model, this program can be used either
* under the terms of the GNU Affero General Public License, version 3,
* or under a proprietary license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*/
return [
['id' => 1, 'content' => '<p>Default</p>'],
['id' => 2, 'content' => '<p>ContainerText</p>'],
['id' => 3, 'content' => '<p>ContainerText</p>'],
['id' => 4, 'content' => '<p>ContainerText</p>'],
['id' => 5, 'content' => '<p>ContainerText</p>'],
];

View File

@ -0,0 +1,25 @@
<?php
/**
* HumHub
* Copyright © 2014 The HumHub Project
*
* The texts of the GNU Affero General Public License with an additional
* permission and of our proprietary license can be found at and
* in the LICENSE file you have received along with this program.
*
* According to our dual licensing model, this program can be used either
* under the terms of the GNU Affero General Public License, version 3,
* or under a proprietary license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*/
return [
['id' => 1, 'name' => 'testTemplate', 'source' => '<div>{{ test_content }}</div>{{ test_text }}', 'description' => 'Template with two richtext elements', 'type' => 'layout'],
['id' => 2, 'name' => 'layout1', 'source' => '<div>{{ container }}</div>', 'description' => 'Layout with one container element', 'type' => 'layout'],
['id' => 3, 'name' => 'containerText', 'source' => '{{ container }} {{ text }}', 'description' => 'Cotnainer template with other container inside', 'type' => 'container'],
['id' => 4, 'name' => 'simpleText', 'source' => '<div>{{ text }}</div>', 'description' => 'Simple text', 'type' => 'container'],
];

View File

@ -0,0 +1,27 @@
<?php
/**
* HumHub
* Copyright © 2014 The HumHub Project
*
* The texts of the GNU Affero General Public License with an additional
* permission and of our proprietary license can be found at and
* in the LICENSE file you have received along with this program.
*
* According to our dual licensing model, this program can be used either
* under the terms of the GNU Affero General Public License, version 3,
* or under a proprietary license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*/
return [
['id' => 1, 'name' => 'test_content', 'template_id' => 1, 'content_type' => 'humhub\modules\custom_pages\modules\template\models\RichtextContent'],
['id' => 2, 'name' => 'test_text', 'template_id' => 1, 'content_type' => 'humhub\modules\custom_pages\modules\template\models\RichtextContent'],
['id' => 3, 'name' => 'container', 'template_id' => 2, 'content_type' => 'humhub\modules\custom_pages\modules\template\models\ContainerContent'],
['id' => 4, 'name' => 'container', 'template_id' => 3, 'content_type' => 'humhub\modules\custom_pages\modules\template\models\ContainerContent'],
['id' => 5, 'name' => 'text', 'template_id' => 3, 'content_type' => 'humhub\modules\custom_pages\modules\template\models\RichtextContent'],
['id' => 6, 'name' => 'text', 'template_id' => 4, 'content_type' => 'humhub\modules\custom_pages\modules\template\models\RichtextContent'],
];

View File

@ -0,0 +1,23 @@
<?php
/**
* HumHub
* Copyright © 2014 The HumHub Project
*
* The texts of the GNU Affero General Public License with an additional
* permission and of our proprietary license can be found at and
* in the LICENSE file you have received along with this program.
*
* According to our dual licensing model, this program can be used either
* under the terms of the GNU Affero General Public License, version 3,
* or under a proprietary license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*/
return [
['id' => 1, 'object_model' => 'humhub\modules\custom_pages\models\Page', 'object_id' => 1, 'template_id' => 1],
['id' => 2, 'object_model' => 'humhub\modules\custom_pages\models\Page', 'object_id' => 2, 'template_id' => 2]
];

View File

@ -0,0 +1,18 @@
# Codeception Test Suite Configuration
# suite for functional (integration) tests.
# emulate web requests and make application process them.
# (tip: better to use with frameworks).
# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
class_name: FunctionalTester
modules:
enabled:
- Filesystem
- Yii2
- tests\codeception\_support\TestHelper
- tests\codeception\_support\DynamicFixtureHelper
- tests\codeception\_support\HumHubHelper
config:
Yii2:
configFile: 'codeception/config/functional.php'

View File

@ -0,0 +1,6 @@
<?php
/**
* Initialize the HumHub Application for functional testing. The default application configuration for this suite can be overwritten
* in @tests/config/functional.php
*/
require(Yii::getAlias('@humhubTests/codeception/functional/_bootstrap.php'));

View File

@ -0,0 +1,9 @@
# Codeception Test Suite Configuration
# suite for unit (internal) tests.
# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
class_name: UnitTester
modules:
enabled:
- tests\codeception\_support\CodeHelper

View File

@ -0,0 +1,6 @@
<?php
/**
* Initialize the HumHub Application for functional testing. The default application configuration for this suite can be overwritten
* in @tests/config/functional.php
*/
require(Yii::getAlias('@humhubTests/codeception/unit/_bootstrap.php'));

View File

@ -0,0 +1,142 @@
<?php
namespace tests\codeception\unit\modules\custom_page\template;
use tests\codeception\_support\HumHubDbTestCase;
use Codeception\Specify;
use humhub\modules\custom_pages\modules\template\models\OwnerContent;
use humhub\modules\custom_pages\modules\template\models\TemplateInstance;
use humhub\modules\custom_pages\modules\template\models\Template;
use humhub\modules\custom_pages\modules\template\models\RichtextContent;
use humhub\modules\custom_pages\models\Page;
use humhub\modules\custom_pages\modules\template\models\ContainerContent;
use humhub\modules\custom_pages\modules\template\models\ContainerContentItem;
use humhub\modules\custom_pages\modules\template\models\ContainerContentDefinition;
class TemplateInstanceTest extends HumHubDbTestCase
{
use Specify;
public $owner2;
public $owner1;
public $page;
public function setUp()
{
parent::setUp();
}
public function testDeleteContainerItem()
{
ContainerContentItem::findOne(['id' => 2])->delete();
ContainerContentItem::findOne(['id' => 3])->delete();
ContainerContentItem::findOne(['id' => 4])->delete();
$this->assertNull(OwnerContent::findOne(['id' => 5]));
$this->assertNull(OwnerContent::findOne(['id' => 6]));
$this->assertNull(OwnerContent::findOne(['id' => 7]));
$this->assertNull(RichtextContent::findOne(['id' => 3]));
$this->assertNull(RichtextContent::findOne(['id' => 4]));
$this->assertNull(RichtextContent::findOne(['id' => 5]));
}
public function testDeleteContainerContent()
{
$container = ContainerContent::findOne(['id' => 2]);
$this->assertEquals(3, $container->getItems()->count());
$container->delete();
$this->assertNull(ContainerContentDefinition::findOne(['id' => 2]));
$this->assertNull(ContainerContentItem::findOne(['id' => 2]));
$this->assertNull(ContainerContentItem::findOne(['id' => 3]));
$this->assertNull(ContainerContentItem::findOne(['id' => 4]));
$this->assertNull(OwnerContent::findOne(['id' => 5]));
$this->assertNull(OwnerContent::findOne(['id' => 6]));
$this->assertNull(OwnerContent::findOne(['id' => 7]));
$this->assertNull(RichtextContent::findOne(['id' => 3]));
$this->assertNull(RichtextContent::findOne(['id' => 4]));
$this->assertNull(RichtextContent::findOne(['id' => 5]));
}
public function testDeleteParentContainer()
{
$container = ContainerContent::findOne(['id' => 1]);
$container->delete();
$this->assertNull(ContainerContentItem::findOne(['id' => 2]));
$this->assertNull(ContainerContentItem::findOne(['id' => 3]));
$this->assertNull(ContainerContentItem::findOne(['id' => 4]));
$this->assertNull(OwnerContent::findOne(['id' => 5]));
$this->assertNull(OwnerContent::findOne(['id' => 6]));
$this->assertNull(OwnerContent::findOne(['id' => 7]));
$this->assertNull(RichtextContent::findOne(['id' => 3]));
$this->assertNull(RichtextContent::findOne(['id' => 4]));
$this->assertNull(RichtextContent::findOne(['id' => 5]));
}
public function testDeletePage()
{
$page = Page::findOne(['id' => 2]);
$page->delete();
$this->assertNull(ContainerContentItem::findOne(['id' => 2]));
$this->assertNull(ContainerContentItem::findOne(['id' => 3]));
$this->assertNull(ContainerContentItem::findOne(['id' => 4]));
$this->assertNull(OwnerContent::findOne(['id' => 5]));
$this->assertNull(OwnerContent::findOne(['id' => 6]));
$this->assertNull(OwnerContent::findOne(['id' => 7]));
$this->assertNull(RichtextContent::findOne(['id' => 3]));
$this->assertNull(RichtextContent::findOne(['id' => 4]));
$this->assertNull(RichtextContent::findOne(['id' => 5]));
}
public function testDeleteAll()
{
Page::findOne(['id' => 2])->delete();
Page::findOne(['id' => 1])->delete();
$this->assertEquals(0, OwnerContent::find()->where(['not', ['owner_model' => Template::className()]])->count());
$this->assertEquals(0, TemplateInstance::find()->count());
$this->assertEquals(1, RichtextContent::find()->count());
$this->assertEquals(0, ContainerContentItem::find()->count());
$this->assertEquals(0, ContainerContent::find()->count());
$this->assertEquals(0, ContainerContentDefinition::find()->count());
Template::findOne(['id' => 1])->delete();
Template::findOne(['id' => 2])->delete();
Template::findOne(['id' => 3])->delete();
Template::findOne(['id' => 4])->delete();
$this->assertEquals(0, RichtextContent::find()->count());
$this->assertEquals(0, \humhub\modules\custom_pages\modules\template\models\TemplateElement::find()->count());
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace tests\codeception\unit\modules\custom_page\template;
use tests\codeception\_support\HumHubDbTestCase;
use Codeception\Specify;
use humhub\modules\custom_pages\modules\template\models\OwnerContent;
use humhub\modules\custom_pages\modules\template\models\TemplateInstance;
class TemplateContentTest extends HumHubDbTestCase
{
use Specify;
public $owner;
public function setUp()
{
parent::setUp();
$this->owner = TemplateInstance::findOne(['id' => 1]);
}
public function testRenderHtml()
{
$content = new \humhub\modules\custom_pages\modules\template\models\RichtextContent();
$content->content = '<p>Test</p>';
$content->save();
$pageContent = new OwnerContent();
$pageContent->setOwner($this->owner);
$pageContent->setContent($content);
$pageContent->save();
$result = $pageContent->render([
'empty' => false,
'element_name' => 'test',
'owner_model' => $this->owner->className(),
'owner_id' => $this->owner->id
]);
$this->assertContains('<p>Test</p>', $result);
$this->assertContains('data-template-element="test"', $result);
$this->assertContains('data-template-owner="'.$this->owner->className().'"', $result);
$this->assertContains('data-template-content="'.$content->className().'"', $result);
$this->assertContains('data-template-empty="0"', $result);
}
}

View File

@ -0,0 +1,182 @@
<?php
namespace tests\codeception\unit\modules\custom_page\template;
use tests\codeception\_support\HumHubDbTestCase;
use Codeception\Specify;
use humhub\modules\custom_pages\modules\template\models\Template;
use humhub\modules\custom_pages\modules\template\models\RichtextContent;
use humhub\modules\custom_pages\modules\template\models\TemplateInstance;
use humhub\modules\custom_pages\modules\template\models\OwnerContent;
use humhub\modules\custom_pages\modules\template\models\TemplateElement;
class TemplateElementTest extends HumHubDbTestCase
{
use Specify;
public $template;
public $owner;
public $element;
public $element2;
public $defaultContent1;
public function setUp()
{
parent::setUp();
$this->template = Template::findOne(['id' => 1]);
$this->element = TemplateElement::findOne(['id' => 1]);
$this->element2 = TemplateElement::findOne(['id' => 2]);
$this->defaultContent1 = RichtextContent::findOne(['id' => 1]);
$this->owner = TemplateInstance::findOne(['id' => 1]);
}
public function testRenderDefaultContent()
{
$result = $this->template->render($this->owner);
$this->assertContains('<p>Default</p>', $result);
$this->assertContains('data-template-element="test_content"', $result);
$this->assertContains('data-template-owner="' . $this->owner->className() . '"', $result);
$this->assertContains('data-template-content="' . RichtextContent::className() . '"', $result);
$this->assertContains('data-template-empty="0"', $result);
}
public function testOverwriteDefaultContent()
{
$content = new RichtextContent();
$content->content = '<p>Non Default</p>';
$this->element->saveInstance($this->owner, $content);
$result = $this->template->render($this->owner);
$this->assertContains('<p>Non Default</p>', $result);
$this->assertContains('data-template-element="test_content"', $result);
$this->assertContains('data-template-owner="' . $this->owner->className() . '"', $result);
$this->assertContains('data-template-content="' . RichtextContent::className() . '"', $result);
$this->assertContains('data-template-empty="0"', $result);
// Test empty element
$this->assertContains('data-template-empty="1"', $result);
}
public function testOverwriteEmptyDefaultContent()
{
$content = new RichtextContent();
$content->content = '<p>Non Default2</p>';
$this->element2->saveInstance($this->owner, $content);
$result = $this->template->render($this->owner);
$this->assertContains('<p>Non Default2</p>', $result);
$this->assertContains('data-template-element="test_text"', $result);
$this->assertNotContains('data-template-empty="1"', $result);
}
public function testOverwriteOldContent()
{
$content = new RichtextContent();
$content->content = '<p>Non Default2</p>';
$this->element2->saveInstance($this->owner, $content);
$content2 = new RichtextContent();
$content2->content = '<p>Non Default New</p>';
$content2->save();
$this->element2->saveInstance($this->owner, $content2);
$result = $this->template->render($this->owner);
$this->assertContains('<p>Non Default New</p>', $result);
$this->assertNull(RichtextContent::findOne(['id' => $content->id]));
}
public function testSaveAsDefaultContent()
{
$content = new RichtextContent();
$content->content = '<p>Default2</p>';
$content->save();
$this->element->saveAsDefaultContent($content);
$result = $this->template->render($this->owner);
$this->assertContains('<p>Default2</p>', $result);
// Get sure the old default content was removed
$this->assertNull(RichtextContent::findOne(['id' => $this->defaultContent1->id]));
}
public function testUniqueTemplateElementName()
{
$newElement = new TemplateElement();
$newElement->scenario = 'create';
$newElement->name = 'test_content';
$newElement->content_type = RichtextContent::className();
$newElement->template_id = $this->template->id;
$newElement->save();
$this->assertTrue($newElement->hasErrors());
}
public function testGetDefaultContent()
{
$this->assertEquals($this->defaultContent1->id, $this->element->getDefaultContent()->getInstance()->id);
}
public function testDeleteElement()
{
$content = new RichtextContent();
$content->content = '<p>Non Default</p>';
$content->save();
$content2 = new RichtextContent();
$content2->content = '<p>Non Default2</p>';
$content2->save();
$defaultContent = $this->element->getDefaultContent();
$this->assertNotNull(OwnerContent::findOne(['id' => $defaultContent->id]));
$this->element->saveInstance($this->owner, $content);
$this->element2->saveInstance($this->owner, $content2);
$this->element->delete();
$this->assertNull(OwnerContent::findOne(['id' => $defaultContent->id]));
$this->assertNull(RichtextContent::findOne(['id' => $content->id]));
$this->assertNotNull(RichtextContent::findOne(['id' => $content2->id]));
$this->assertNull($this->template->getElement('test_content'));
$this->assertNotNull($this->template->getElement('test_text'));
}
public function testDeleteTemplate()
{
$content = new RichtextContent();
$content->content = '<p>Non Default</p>';
$content->save();
$content2 = new RichtextContent();
$content2->content = '<p>Non Default2</p>';
$content2->save();
$defaultContent = $this->element->getDefaultContent();
$this->assertNotNull(OwnerContent::findOne(['id' => $defaultContent->id]));
$this->element->saveInstance($this->owner, $content);
$this->element2->saveInstance($this->owner, $content2);
$this->assertFalse($this->template->delete());
$this->owner->delete();
$this->assertEquals('1', $this->template->delete());
$this->assertNull(OwnerContent::findOne(['id' => $defaultContent->id]));
$this->assertNull(RichtextContent::findOne(['id' => $content->id]));
}
}

View File

@ -0,0 +1,135 @@
<?php
namespace tests\codeception\unit\modules\custom_page\template;
use tests\codeception\_support\HumHubDbTestCase;
use Codeception\Specify;
use humhub\modules\custom_pages\modules\template\models\OwnerContent;
use humhub\modules\custom_pages\modules\template\models\TemplateInstance;
use humhub\modules\custom_pages\modules\template\models\Template;
use humhub\modules\custom_pages\modules\template\models\RichtextContent;
use humhub\modules\custom_pages\models\Page;
class TemplateInstanceTest extends HumHubDbTestCase
{
use Specify;
public $owner2;
public $owner1;
public $page;
public function setUp()
{
parent::setUp();
$this->page = new Page(['id' => 2, 'type' => '5', 'title' => 'test2', 'navigation_class' => 'TopMenuWidget']);
$this->page->save();
$this->owner1 = new Template();
$this->owner1->scenario = 'edit';
$this->owner1->name = 'containerTestTmpl';
$this->owner1->description = 'My Test Template';
$this->owner1->save();
$this->owner2 = new TemplateInstance();
$this->owner2->object_model = Page::className();
$this->owner2->object_id = $this->page->id;
$this->owner2->template_id = $this->owner1->id;
$this->owner2->save();
}
public function testDeleteOwner()
{
$template = Template::findOne(['id' => 1]);
$element = $template->getElement('test_content');
$richtext = new RichtextContent(['content' => 'testContent']);
$ownerContent = $element->saveInstance($this->owner2, $richtext);
$this->assertFalse($richtext->isNewRecord);
$this->assertFalse($ownerContent->isNewRecord);
$ownerTestContent = $element->getOwnerContent($this->owner2);
$content = $ownerContent->instance;
$this->assertNotNull($content);
$this->assertEquals($ownerContent->id, $ownerTestContent->id);
$this->owner2->delete();
$this->assertNull(OwnerContent::findOne(['id' => $ownerContent->id]));
$this->assertNull(RichtextContent::findOne(['id' => $richtext->id]));
}
public function testDeleteByOwner()
{
$template = Template::findOne(['id' => 1]);
$element = $template->getElement('test_content');
$richtext = new RichtextContent(['content' => 'testContent']);
$ownerContent = $element->saveInstance($this->owner2, $richtext);
$this->assertFalse($richtext->isNewRecord);
$this->assertFalse($ownerContent->isNewRecord);
TemplateInstance::deleteByOwner($this->page);
$this->assertNull(TemplateInstance::findOne(['id' => $this->owner2->id]));
$this->assertNull(OwnerContent::findOne(['id' => $ownerContent->id]));
$this->assertNull(RichtextContent::findOne(['id' => $richtext->id]));
}
public function testDeletePage()
{
$template = Template::findOne(['id' => 1]);
$element = $template->getElement('test_content');
$richtext = new RichtextContent(['content' => 'testContent']);
$ownerContent = $element->saveInstance($this->owner2, $richtext);
$this->assertFalse($richtext->isNewRecord);
$this->assertFalse($ownerContent->isNewRecord);
$this->page->delete();
$this->assertNull(TemplateInstance::findOne(['id' => $this->owner2->id]));
$this->assertNull(OwnerContent::findOne(['id' => $ownerContent->id]));
$this->assertNull(RichtextContent::findOne(['id' => $richtext->id]));
}
public function testFindByOwner()
{
$content = new \humhub\modules\custom_pages\modules\template\models\RichtextContent();
$content->content = '<p>Test</p>';
$content->save();
$content2 = new \humhub\modules\custom_pages\modules\template\models\RichtextContent();
$content2->content = '<p>Test</p>';
$content2->save();
$content3 = new \humhub\modules\custom_pages\modules\template\models\RichtextContent();
$content3->content = '<p>Test</p>';
$content3->save();
$instance = new OwnerContent();
$instance->element_name = 'test';
$instance->setOwner($this->owner1);
$instance->setContent($content);
$instance->save();
$instance2 = new OwnerContent();
$instance2->element_name = 'test_2';
$instance2->setOwner($this->owner1);
$instance2->setContent($content2);
$instance2->save();
$instance3 = new OwnerContent();
$instance3->element_name = 'test';
$instance3->setOwner($this->owner2);
$instance3->setContent($content3);
$instance3->save();
$contentOwner1 = OwnerContent::findByOwner($this->owner1)->all();
$contentOwner2 = OwnerContent::findByOwner($this->owner2)->all();
$this->assertEquals(2, count($contentOwner1));
$this->assertEquals(1, count($contentOwner2));
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace tests\codeception\unit\modules\custom_page\template;
use Yii;
use tests\codeception\_support\HumHubDbTestCase;
use Codeception\Specify;
use humhub\modules\custom_pages\modules\template\models\Template;
class TemplateTest extends HumHubDbTestCase
{
#use Specify;
public function testUniqueTemplateName()
{
$template = new Template();
$template->scenario = 'edit';
$template->name = 'testTemplate';
$template->description = 'My Test Template';
$this->assertFalse($template->save());
}
public function testEmptySource()
{
$template = new Template();
$template->scenario = 'source';
$template->name = 'testTemplate2';
$template->description = 'My Test Template';
$this->assertFalse($template->save());
$template->source = "Whatever";
$this->assertTrue($template->save());
}
public function testDefaultValues()
{
$template = new Template();
$template->scenario = 'edit';
$template->name = 'testTemplate2';
$template->description = 'My Test Template';
$this->assertTrue($template->save());
$template = Template::findOne(['id' => $template->id]);
$this->assertEquals('0', $template->allow_for_spaces);
}
public function testDefaultRender()
{
$template = new Template();
$template->scenario = 'source';
$template->name = 'testTemplate2';
$template->description = 'My Test Template';
$template->source = "Whatever";
$template->save();
$this->assertEquals('Whatever', $template->render());
}
}

View File

@ -0,0 +1,9 @@
<?php
/**
* This config is shared by all suites (unit/functional/acceptance) and can be overwritten by a suite config (e.g. functional.php)
*/
return [
'params' => [
'allowedLanguages' => ['en']
]
];

View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@ -0,0 +1,5 @@
<?php
/**
* Here you can overwrite the default config for the functional suite. The default config resides in @humhubTests/codeception/config/config.php
*/
return [];

View File

@ -0,0 +1,6 @@
<?php
return [];

View File

@ -0,0 +1,5 @@
<?php
/**
* Here you can overwrite your functional humhub config. The default config resiedes in @humhubTests/codeception/config/config.php
*/
return [];

View File

@ -1,3 +1,7 @@
<?php
\humhub\assets\JqueryKnobAsset::register($this);
?>
<div class="container">
<div class="row">
<div class="col-md-2">

View File

@ -5,6 +5,7 @@ use yii\helpers\Html;
use humhub\modules\search\controllers\SearchController;
use humhub\modules\content\components\ContentActiveRecord;
use humhub\modules\content\components\ContentContainerActiveRecord;
?>
<div class="container">
<div class="row">

Some files were not shown because too many files have changed in this diff Show More