diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 43cbad1fb..775e2a984 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -312,6 +312,16 @@ class UserController extends Controller return $this->changeListSort($id, $request, $type); } + /** + * Toggle dark mode for the current user. + */ + public function toggleDarkMode() + { + $enabled = setting()->getForCurrentUser('dark-mode-enabled', false); + setting()->putUser(user(), 'dark-mode-enabled', $enabled ? 'false' : 'true'); + return redirect()->back(); + } + /** * Update the stored section expansion preference for the given user. */ diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 4517deb90..a0c45ea89 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -26,7 +26,6 @@ class Kernel extends HttpKernel \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \Illuminate\Routing\Middleware\ThrottleRequests::class, \BookStack\Http\Middleware\VerifyCsrfToken::class, \BookStack\Http\Middleware\Localization::class, \BookStack\Http\Middleware\GlobalViewData::class, diff --git a/public/libs/tinymce/plugins/help/plugin.min.js b/public/libs/tinymce/plugins/help/plugin.min.js index a598b6c4a..67cde482a 100644 --- a/public/libs/tinymce/plugins/help/plugin.min.js +++ b/public/libs/tinymce/plugins/help/plugin.min.js @@ -1 +1 @@ -!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=function(e){return function(){return e}};function c(r){for(var o=[],e=1;e'+C.translate(e.action)+""+e.shortcut+"";var t}).join("");return{title:"Handy Shortcuts",type:"container",style:"overflow-y: auto; overflow-x: hidden; max-height: 250px",items:[{type:"container",html:'
"+e+"
'+C.translate("Action")+""+C.translate("Shortcut")+"
"}]}},P=Object.keys,_=[{key:"advlist",name:"Advanced List"},{key:"anchor",name:"Anchor"},{key:"autolink",name:"Autolink"},{key:"autoresize",name:"Autoresize"},{key:"autosave",name:"Autosave"},{key:"bbcode",name:"BBCode"},{key:"charmap",name:"Character Map"},{key:"code",name:"Code"},{key:"codesample",name:"Code Sample"},{key:"colorpicker",name:"Color Picker"},{key:"compat3x",name:"3.x Compatibility"},{key:"contextmenu",name:"Context Menu"},{key:"directionality",name:"Directionality"},{key:"emoticons",name:"Emoticons"},{key:"fullpage",name:"Full Page"},{key:"fullscreen",name:"Full Screen"},{key:"help",name:"Help"},{key:"hr",name:"Horizontal Rule"},{key:"image",name:"Image"},{key:"imagetools",name:"Image Tools"},{key:"importcss",name:"Import CSS"},{key:"insertdatetime",name:"Insert Date/Time"},{key:"legacyoutput",name:"Legacy Output"},{key:"link",name:"Link"},{key:"lists",name:"Lists"},{key:"media",name:"Media"},{key:"nonbreaking",name:"Nonbreaking"},{key:"noneditable",name:"Noneditable"},{key:"pagebreak",name:"Page Break"},{key:"paste",name:"Paste"},{key:"preview",name:"Preview"},{key:"print",name:"Print"},{key:"save",name:"Save"},{key:"searchreplace",name:"Search and Replace"},{key:"spellchecker",name:"Spell Checker"},{key:"tabfocus",name:"Tab Focus"},{key:"table",name:"Table"},{key:"template",name:"Template"},{key:"textcolor",name:"Text Color"},{key:"textpattern",name:"Text Pattern"},{key:"toc",name:"Table of Contents"},{key:"visualblocks",name:"Visual Blocks"},{key:"visualchars",name:"Visual Characters"},{key:"wordcount",name:"Word Count"}],H=c(function(e,o){return e.replace(/\$\{([^{}]*)\}/g,function(e,t){var n,r=o[t];return"string"==(n=typeof r)||"number"===n?r.toString():e})},'${name}'),F=function(t,n){return function(e,t){for(var n=0,r=e.length;n"+F(t,e)+""}),i=a.length,l=a.join("");return"

"+C.translate(["Plugins installed ({0}):",i])+"

"},E=function(e){return{title:"Plugins",type:"container",style:"overflow-y: auto; overflow-x: hidden;",layout:"flex",padding:10,spacing:10,items:[(t=e,{type:"container",html:'
'+M(t)+"
",flex:1}),{type:"container",html:'

'+C.translate("Premium plugins:")+'

  • PowerPaste
  • Spell Checker Pro
  • Accessibility Checker
  • Advanced Code Editor
  • Enhanced Media Embed
  • Link Checker

'+C.translate("Learn more...")+"

",flex:1}]};var t},I=tinymce.util.Tools.resolve("tinymce.EditorManager"),j=function(){var e,t,n='TinyMCE '+(e=I.majorVersion,t=I.minorVersion,0===e.indexOf("@")?"X.X.X":e+"."+t)+"";return[{type:"label",html:C.translate(["You are using {0}",n])},{type:"spacer",flex:1},{text:"Close",onclick:function(){this.parent().parent().close()}}]},L=function(e,t){return function(){e.windowManager.open({title:"Help",bodyType:"tabpanel",layout:"flex",body:[T(),E(e)],buttons:j(),onPostRender:function(){this.getEl("title").innerHTML='TinyMCE Logo'}})}},B=function(e,t){e.addCommand("mceHelp",L(e,t))},N=function(e,t){e.addButton("help",{icon:"help",onclick:L(e,t)}),e.addMenuItem("help",{text:"Help",icon:"help",context:"help",onclick:L(e,t)})};e.add("help",function(e,t){N(e,t),B(e,t),e.shortcuts.add("Alt+0","Open help dialog","mceHelp")})}(); \ No newline at end of file +!function(){"use strict";var e=tinymce.util.Tools.resolve("tinymce.PluginManager"),t=function(){},a=function(e){return function(){return e}};function l(r){for(var o=[],e=1;e'+v.translate(e.action)+""+e.shortcut+"";var t}).join("");return{title:"Handy Shortcuts",type:"container",style:"overflow-y: auto; overflow-x: hidden; max-height: 250px",items:[{type:"container",html:'
"+e+"
'+v.translate("Action")+""+v.translate("Shortcut")+"
"}]}},S=Object.keys,O=[{key:"advlist",name:"Advanced List"},{key:"anchor",name:"Anchor"},{key:"autolink",name:"Autolink"},{key:"autoresize",name:"Autoresize"},{key:"autosave",name:"Autosave"},{key:"bbcode",name:"BBCode"},{key:"charmap",name:"Character Map"},{key:"code",name:"Code"},{key:"codesample",name:"Code Sample"},{key:"colorpicker",name:"Color Picker"},{key:"compat3x",name:"3.x Compatibility"},{key:"contextmenu",name:"Context Menu"},{key:"directionality",name:"Directionality"},{key:"emoticons",name:"Emoticons"},{key:"fullpage",name:"Full Page"},{key:"fullscreen",name:"Full Screen"},{key:"help",name:"Help"},{key:"hr",name:"Horizontal Rule"},{key:"image",name:"Image"},{key:"imagetools",name:"Image Tools"},{key:"importcss",name:"Import CSS"},{key:"insertdatetime",name:"Insert Date/Time"},{key:"legacyoutput",name:"Legacy Output"},{key:"link",name:"Link"},{key:"lists",name:"Lists"},{key:"media",name:"Media"},{key:"nonbreaking",name:"Nonbreaking"},{key:"noneditable",name:"Noneditable"},{key:"pagebreak",name:"Page Break"},{key:"paste",name:"Paste"},{key:"preview",name:"Preview"},{key:"print",name:"Print"},{key:"save",name:"Save"},{key:"searchreplace",name:"Search and Replace"},{key:"spellchecker",name:"Spell Checker"},{key:"tabfocus",name:"Tab Focus"},{key:"table",name:"Table"},{key:"template",name:"Template"},{key:"textcolor",name:"Text Color"},{key:"textpattern",name:"Text Pattern"},{key:"toc",name:"Table of Contents"},{key:"visualblocks",name:"Visual Blocks"},{key:"visualchars",name:"Visual Characters"},{key:"wordcount",name:"Word Count"}],T=l(function(e,o){return e.replace(/\$\{([^{}]*)\}/g,function(e,t){var n,r=o[t];return"string"==(n=typeof r)||"number"===n?r.toString():e})},'${name}'),P=function(t,n){return function(e,t){for(var n=0,r=e.length;n"+P(t,e)+""}),i=a.length,c=a.join("");return"

"+v.translate(["Plugins installed ({0}):",i])+"

    "+c+"
"},H=function(e){return{title:"Plugins",type:"container",style:"overflow-y: auto; overflow-x: hidden;",layout:"flex",padding:10,spacing:10,items:[(t=e,{type:"container",html:'
'+_(t)+"
",flex:1}),{type:"container",html:'

'+v.translate("Premium plugins:")+'

  • PowerPaste
  • Spell Checker Pro
  • Accessibility Checker
  • Advanced Code Editor
  • Enhanced Media Embed
  • Link Checker

'+v.translate("Learn more...")+"

",flex:1}]};var t},F=tinymce.util.Tools.resolve("tinymce.EditorManager"),M=function(){var e,t,n='TinyMCE '+(e=F.majorVersion,t=F.minorVersion,0===e.indexOf("@")?"X.X.X":e+"."+t)+"";return[{type:"label",html:v.translate(["You are using {0}",n])},{type:"spacer",flex:1},{text:"Close",onclick:function(){this.parent().parent().close()}}]},E=function(e,t){return function(){e.windowManager.open({title:"Help",bodyType:"tabpanel",layout:"flex",body:[w(),H(e)],buttons:M(),onPostRender:function(){this.getEl("title").innerHTML='TinyMCE Logo'}})}},I=function(e,t){e.addCommand("mceHelp",E(e,t))},j=function(e,t){e.addButton("help",{icon:"help",onclick:E(e,t)}),e.addMenuItem("help",{text:"Help",icon:"help",context:"help",onclick:E(e,t)})};e.add("help",function(e,t){j(e,t),I(e,t),e.shortcuts.add("Alt+0","Open help dialog","mceHelp")})}(); \ No newline at end of file diff --git a/public/libs/tinymce/plugins/image/plugin.min.js b/public/libs/tinymce/plugins/image/plugin.min.js index d4764ad62..23473aa76 100644 --- a/public/libs/tinymce/plugins/image/plugin.min.js +++ b/public/libs/tinymce/plugins/image/plugin.min.js @@ -1 +1 @@ -!function(l){"use strict";var i,e=tinymce.util.Tools.resolve("tinymce.PluginManager"),d=function(e){return!1!==e.settings.image_dimensions},u=function(e){return!0===e.settings.image_advtab},m=function(e){return e.getParam("image_prepend_url","")},n=function(e){return e.getParam("image_class_list")},r=function(e){return!1!==e.settings.image_description},a=function(e){return!0===e.settings.image_title},o=function(e){return!0===e.settings.image_caption},c=function(e){return e.getParam("image_list",!1)},s=function(e){return e.getParam("images_upload_url",!1)},g=function(e){return e.getParam("images_upload_handler",!1)},f=function(e){return e.getParam("images_upload_url")},p=function(e){return e.getParam("images_upload_handler")},h=function(e){return e.getParam("images_upload_base_path")},v=function(e){return e.getParam("images_upload_credentials")},b="undefined"!=typeof l.window?l.window:Function("return this;")(),y=function(e,t){return function(e,t){for(var n=t!==undefined&&null!==t?t:b,r=0;rthis.length())return null;for(var n=this.littleEndian?0:-8*(e-1),r=0,o=0;r=s.length())throw new Error("Invalid Exif data.");"ASCII"!==i?(f=s.asArray(i,c,a),l=1==a?f[0]:f,at.hasOwnProperty(o)&&"object"!=typeof l?d[o]=at[o][l]:d[o]=l):d[o]=s.STRING(c,a).replace(/\0$/,"").trim()}return d},t}(),ct=function(t){var e,n,r=[],o=0;for(e=2;e<=t.length();)if(65488<=(n=t.SHORT(e))&&n<=65495)e+=2;else{if(65498===n||65497===n)break;o=t.SHORT(e+2)+2,65505<=n&&n<=65519&&r.push({hex:n,name:"APP"+(15&n),start:e,length:o,segment:t.SEGMENT(e,o)}),e+=o}return r},lt=function(u){return L.blobToArrayBuffer(u).then(function(t){try{var e=new et(t);if(65496===e.SHORT(0)){var n=ct(e),r=n.filter(function(t){return"APP1"===t.name}),o={};if(!r.length)return g.reject("Headers did not include required information");var i=new ut(r[0].segment);return(o={tiff:i.TIFF(),exif:i.EXIF(),gps:i.GPS(),thumb:i.thumb()}).rawHeaders=n,o}return g.reject("Image was not a jpeg")}catch(a){return g.reject("Unsupported format or not an image: "+u.type+" (Exception: "+a.message+")")}})},st=function(t,e){return tt.rotate(t,e)},ft={invert:function(t){return Z.invert(t)},sharpen:function(t){return Z.sharpen(t)},emboss:function(t){return Z.emboss(t)},brightness:function(t,e){return Z.brightness(t,e)},hue:function(t,e){return Z.hue(t,e)},saturate:function(t,e){return Z.saturate(t,e)},contrast:function(t,e){return Z.contrast(t,e)},grayscale:function(t,e){return Z.grayscale(t,e)},sepia:function(t,e){return Z.sepia(t,e)},colorize:function(t,e,n,r){return Z.colorize(t,e,n,r)},gamma:function(t,e){return Z.gamma(t,e)},exposure:function(t,e){return Z.exposure(t,e)},flip:function(t,e){return tt.flip(t,e)},crop:function(t,e,n,r,o){return tt.crop(t,e,n,r,o)},resize:function(t,e,n){return tt.resize(t,e,n)},rotate:st,exifRotate:function(e){return e.toBlob().then(lt).then(function(t){switch(t.tiff.Orientation){case 6:return st(e,90);case 3:return st(e,180);case 8:return st(e,270);default:return e}},function(){return e})}},dt=function(t){return t.toBlob()},ht={blobToImageResult:function(t){return N.fromBlob(t)},fromBlobAndUrlSync:function(t,e){return N.fromBlobAndUrlSync(t,e)},imageToImageResult:function(t){return N.fromImage(t)},imageResultToBlob:function(t,e,n){return e===undefined&&n===undefined?dt(t):t.toAdjustedBlob(e,n)},imageResultToOriginalBlob:dt,imageResultToDataURL:function(t){return t.toDataURL()}},pt=function(){return O.getOrDie("URL")},gt={createObjectURL:function(t){return pt().createObjectURL(t)},revokeObjectURL:function(t){pt().revokeObjectURL(t)}},mt=tinymce.util.Tools.resolve("tinymce.util.Delay"),yt=tinymce.util.Tools.resolve("tinymce.util.Promise"),vt=tinymce.util.Tools.resolve("tinymce.util.URI"),bt=tinymce.util.Tools.resolve("tinymce.dom.DOMUtils"),wt=tinymce.util.Tools.resolve("tinymce.ui.Factory"),xt=tinymce.util.Tools.resolve("tinymce.geom.Rect"),It=function(n){return new yt(function(t){var e=function(){n.removeEventListener("load",e),t(n)};n.complete?t(n):n.addEventListener("load",e)})},Tt=tinymce.util.Tools.resolve("tinymce.dom.DomQuery"),Rt=tinymce.util.Tools.resolve("tinymce.util.Observable"),St=tinymce.util.Tools.resolve("tinymce.util.VK"),Ot=0,Ft={create:function(t){return new(wt.get("Control").extend({Defaults:{classes:"imagepanel"},selection:function(t){return arguments.length?(this.state.set("rect",t),this):this.state.get("rect")},imageSize:function(){var t=this.state.get("viewRect");return{w:t.w,h:t.h}},toggleCropRect:function(t){this.state.set("cropEnabled",t)},imageSrc:function(t){var o=this,i=new s.Image;i.src=t,It(i).then(function(){var t,e,n=o.state.get("viewRect");if((e=o.$el.find("img"))[0])e.replaceWith(i);else{var r=s.document.createElement("div");r.className="mce-imagepanel-bg",o.getEl().appendChild(r),o.getEl().appendChild(i)}t={x:0,y:0,w:i.naturalWidth,h:i.naturalHeight},o.state.set("viewRect",t),o.state.set("rect",xt.inflate(t,-20,-20)),n&&n.w===t.w&&n.h===t.h||o.zoomFit(),o.repaintImage(),o.fire("load")})},zoom:function(t){return arguments.length?(this.state.set("zoom",t),this):this.state.get("zoom")},postRender:function(){return this.imageSrc(this.settings.imageSrc),this._super()},zoomFit:function(){var t,e,n,r,o,i;t=this.$el.find("img"),e=this.getEl().clientWidth,n=this.getEl().clientHeight,r=t[0].naturalWidth,o=t[0].naturalHeight,1<=(i=Math.min((e-10)/r,(n-10)/o))&&(i=1),this.zoom(i)},repaintImage:function(){var t,e,n,r,o,i,a,u,c,l,s;s=this.getEl(),c=this.zoom(),l=this.state.get("rect"),a=this.$el.find("img"),u=this.$el.find(".mce-imagepanel-bg"),o=s.offsetWidth,i=s.offsetHeight,n=a[0].naturalWidth*c,r=a[0].naturalHeight*c,t=Math.max(0,o/2-n/2),e=Math.max(0,i/2-r/2),a.css({left:t,top:e,width:n,height:r}),u.css({left:t,top:e,width:n,height:r}),this.cropRect&&(this.cropRect.setRect({x:l.x*c+t,y:l.y*c+e,w:l.w*c,h:l.h*c}),this.cropRect.setClampRect({x:t,y:e,w:n,h:r}),this.cropRect.setViewPortRect({x:0,y:0,w:o,h:i}))},bindStates:function(){var r=this;function n(t){r.cropRect=function(l,n,s,r,o){var f,a,t,i,e="mce-",u=e+"crid-"+Ot++;function d(t,e){return{x:e.x-t.x,y:e.y-t.y,w:e.w,h:e.h}}function c(t,e,n,r){var o,i,a,u,c;o=e.x,i=e.y,a=e.w,u=e.h,o+=n*t.deltaX,i+=r*t.deltaY,(a+=n*t.deltaW)<20&&(a=20),(u+=r*t.deltaH)<20&&(u=20),c=l=xt.clamp({x:o,y:i,w:a,h:u},s,"move"===t.name),c=d(s,c),f.fire("updateRect",{rect:c}),g(c)}function h(e){function t(t,e){e.h<0&&(e.h=0),e.w<0&&(e.w=0),Tt("#"+u+"-"+t,r).css({left:e.x,top:e.y,width:e.w,height:e.h})}$.each(a,function(t){Tt("#"+u+"-"+t.name,r).css({left:e.w*t.xMul+e.x,top:e.h*t.yMul+e.y})}),t("top",{x:n.x,y:n.y,w:n.w,h:e.y-n.y}),t("right",{x:e.x+e.w,y:e.y,w:n.w-e.x-e.w+n.x,h:e.h}),t("bottom",{x:n.x,y:e.y+e.h,w:n.w,h:n.h-e.y-e.h+n.y}),t("left",{x:n.x,y:e.y,w:e.x-n.x,h:e.h}),t("move",e)}function p(t){h(l=t)}function g(t){var e,n;p((e=s,{x:(n=t).x+e.x,y:n.y+e.y,w:n.w,h:n.h}))}return a=[{name:"move",xMul:0,yMul:0,deltaX:1,deltaY:1,deltaW:0,deltaH:0,label:"Crop Mask"},{name:"nw",xMul:0,yMul:0,deltaX:1,deltaY:1,deltaW:-1,deltaH:-1,label:"Top Left Crop Handle"},{name:"ne",xMul:1,yMul:0,deltaX:0,deltaY:1,deltaW:1,deltaH:-1,label:"Top Right Crop Handle"},{name:"sw",xMul:0,yMul:1,deltaX:1,deltaY:0,deltaW:-1,deltaH:1,label:"Bottom Left Crop Handle"},{name:"se",xMul:1,yMul:1,deltaX:0,deltaY:0,deltaW:1,deltaH:1,label:"Bottom Right Crop Handle"}],i=["top","right","bottom","left"],Tt('
').appendTo(r),$.each(i,function(t){Tt("#"+u,r).append(' @endif diff --git a/resources/views/components/image-manager.blade.php b/resources/views/components/image-manager.blade.php index 0971c3ed9..3eed2fdb7 100644 --- a/resources/views/components/image-manager.blade.php +++ b/resources/views/components/image-manager.blade.php @@ -26,7 +26,7 @@
diff --git a/resources/views/settings/index.blade.php b/resources/views/settings/index.blade.php index 07ca1fcc1..6ccb8d8f9 100644 --- a/resources/views/settings/index.blade.php +++ b/resources/views/settings/index.blade.php @@ -92,7 +92,7 @@

{{ trans('settings.app_name_desc') }}

-
+
@include('components.toggle-switch', [ 'name' => 'setting-app-name-header', @@ -107,7 +107,7 @@

{{ trans('settings.app_editor_desc') }}

-
+
@@ -174,7 +174,7 @@

{{ trans('settings.app_homepage_desc') }}

-
+
diff --git a/routes/web.php b/routes/web.php index 4dfccdf36..3e05e394d 100644 --- a/routes/web.php +++ b/routes/web.php @@ -183,6 +183,7 @@ Route::group(['middleware' => 'auth'], function () { Route::patch('/users/{id}/switch-shelf-view', 'UserController@switchShelfView'); Route::patch('/users/{id}/change-sort/{type}', 'UserController@changeSort'); Route::patch('/users/{id}/update-expansion-preference/{key}', 'UserController@updateExpansionPreference'); + Route::patch('/users/toggle-dark-mode', 'UserController@toggleDarkMode'); Route::post('/users/create', 'UserController@store'); Route::get('/users/{id}', 'UserController@edit'); Route::put('/users/{id}', 'UserController@update'); diff --git a/tests/User/UserPreferencesTest.php b/tests/User/UserPreferencesTest.php index b70d52dfa..0db4f803a 100644 --- a/tests/User/UserPreferencesTest.php +++ b/tests/User/UserPreferencesTest.php @@ -75,4 +75,21 @@ class UserPreferencesTest extends TestCase $invalidKeyRequest = $this->patch('/settings/users/' . $editor->id.'/update-expansion-preference/my-home-details', ['expand' => 'true']); $invalidKeyRequest->assertStatus(500); } + + public function test_toggle_dark_mode() + { + $home = $this->actingAs($this->getEditor())->get('/'); + $home->assertElementNotExists('.dark-mode'); + $home->assertSee('Dark Mode'); + + $this->assertEquals(false, setting()->getForCurrentUser('dark-mode-enabled', false)); + $prefChange = $this->patch('/settings/users/toggle-dark-mode'); + $prefChange->assertRedirect(); + $this->assertEquals(true, setting()->getForCurrentUser('dark-mode-enabled')); + + $home = $this->actingAs($this->getEditor())->get('/'); + $home->assertElementExists('.dark-mode'); + $home->assertDontSee('Dark Mode'); + $home->assertSee('Light Mode'); + } } \ No newline at end of file