From a8f2e6914d15e778889c65a4bff6441ead04ee13 Mon Sep 17 00:00:00 2001
From: Christoph Wurst <christoph@winzerhof-wurst.at>
Date: Tue, 26 Nov 2019 18:15:10 +0100
Subject: [PATCH] Add checkbox to install recommended apps during setup

Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Signed-off-by: npmbuildbot[bot] <npmbuildbot[bot]@users.noreply.github.com>
---
 apps/settings/js/vue-4.js                     | Bin 28859 -> 29720 bytes
 apps/settings/js/vue-4.js.map                 | Bin 92145 -> 94442 bytes
 apps/settings/js/vue-5.js                     | Bin 44325 -> 44325 bytes
 apps/settings/js/vue-5.js.map                 | Bin 155182 -> 155182 bytes
 .../js/vue-settings-apps-users-management.js  | Bin 249877 -> 249877 bytes
 .../vue-settings-apps-users-management.js.map | Bin 1202811 -> 1202811 bytes
 apps/settings/src/components/AppList.vue      |  52 ++++++++++++------
 apps/settings/src/recommendedApps.js          |  26 +++++++++
 apps/settings/src/views/Apps.vue              |  15 ++++-
 core/Controller/SetupController.php           |  11 +++-
 core/js/setup.js                              |   2 -
 core/templates/installation.php               |   9 +++
 12 files changed, 93 insertions(+), 22 deletions(-)
 create mode 100644 apps/settings/src/recommendedApps.js

diff --git a/apps/settings/js/vue-4.js b/apps/settings/js/vue-4.js
index be21d10b35e1c81cbd6ac945dcb729e6a53222e0..b2e85769945182a862fdcfa3765efb9df5195c89 100644
GIT binary patch
delta 1410
zcmZWp&u`pB6qXZ`k{@K7EM)^qg=Nz1Ofns|c7Bn{sX`Ko2vi`A#359xu|2zX>m9rH
zIGX@b#R*O*(Fh47K&lWYq+ZO81AhPq#ElaYLP8w4aI8=TV{d-g<Yeo;H}9M8d*7Sy
ze^WmDQ#oua#*zC((FN)U;uEE!80lBa$Nl3B7<vPu$Yhv-7@|O28ThmonhfX*5ZUIN
z)?I3I5MlyBAbuYBTJh6laBE~ZIZcVl>QXv1F`!i1r?ITu5tpkoiYs2L*2nAv)baK_
z7BBB4sGWXL{jk5bYk~}2ho}hRiigJQX|wk9MWrR&s~eS0sc9=~sx{3W%?=nRHjg!=
z*~F*JA(8y;5YN}{AWJ;HdS!I?47HQafJc4T;;zSS7jV~$u}wH_2hl!!-zG8D<mEW=
zed=h-5#>q5RB<#rC7zqDyl^8gh^QTOIuhuAB{OOhd(8F|hsJ<o&w&RJ_Kr|StaBaT
zoqfL6OFLeCE<b;aM#Au#l`zMrap^)YsT?22PhpbKkzC4kQ?jlX&&nLyB8krn%&OuE
z91;v!=}%~@Z+54^A8AFZNIPMHwEV*2BM8&Om)}vurQr!N<&3=TwLs~JX__U4>vhcu
zy37xVBVp7$Hys5Dr}%zCvkB~yNTp^3Ah-?@F~z_7v{-2jjn(U1&WUaIW+RZXE`A-I
zctE7es4Y8$(olB}GQo2(0u~28#SXQSHaKDAv9_vVX%{(gK(Mq*o4ZI;3nW#kcC0~&
z-im^b7gH>a00_j~r3`>p!ffsZ3`nQs2zRL*cq=H8&ReKwCW4uYnT@uCoHH&GuGc}=
zV^S0B2l7_3x5-Nd_~fiO8lMz5(S@ZRGQ~8Cf(S_WC%&VyfS*8~r-OJVO8R@_QlAou
z$NJ}${n9E~=|khf@XDIp^*M-E<;S?b2vIkG3RWSymqm1$+RG#NAX?8O^AJtvk#&f+
z#UJL{#nN_n<bJ=hved8arP1nrWo#M~924eo?>!3k(!XC_RBE?-t1n-e;_7c_t1p_h
z!Srzb%gd8%a*nxcaS~I|l~2lz%qF+QhV|{pZL}JOZ+J0Z-cSFrMxUw`rl}#@Cvi+O
zZ-^h=#xup-f%vX9H?noM%jsXO2ZNQ(k~FvEO*YaWI^QTSaycU6NDpOTH#u%n0GA2S
lj^V|4h{YGdFUqD!LjBqK#Rc847Z<FCzOXcB>(;zs{Ra=b--G}F

delta 899
zcmaJ<O-vI}7|d&>q*7?H60M>&8<h9pv2^<b?KZ1mfeQwrkeJ|s>~^>9(ssM-PoV@1
zT+~EEG*1&x-aMI@n0M5JUOgEvCSHgqJ$UF@W8$(Hq>1(V&E%Vz%sl-DUVi}(i-3Dn
z`UPSHdm-5cLBP>x;GUPHJ03?2L4)xzglodQw4&rKXs{xJCWfTx>mxrp9sr%*_1)+S
z=359l84VAC3Jss=20^<8s<@C$mco~MYFoIfuB)2K-^yc~?uK`~k)i-Kn25&^KB_4H
zQEbleWINkQZ=d=Z1SPUDI329Fyf5v)4;LKUiFh30s-UqNEFip0&cKU-;$Oi!orNzw
zjG<Vzp)rmJORQ8BnGpoRDyb%$7cHe&Ga6UOtKsP2A-;tW&nTKGwd1Dxc}7;v_Jn@&
zf$JuphXYZb5p{h;vNgG)nEw_gWqkZB&)B-`*lEXkoALwRu^A@khx}v@OZ4i<b<fg&
zLTzrg&EP<7?K_z3goI*!^y}zNKz9<op3W*nkV9F*B96RINxf1_Bg==?CFz~9H~z~F
zr<^3hx&4l~afBz`j&caExve=+*}beGyzX972v50}b%Ynl_Czk!GMqZL$t;Y}?-Nlk
zDAT~yD(IPFMb;EG)lxSUSf=-<$3bN7U|G(Zk1lKBz#eVRd^&f!!OBWOv?~^Cn+lwC
zww|qv#)U<)C%@>L!;`vxO*JiEqED2-F*?5@`+5gj!Y&?$WohT?b3or2Api!*M>Bqs
VlQLpPOsA4uI#Y;^rSftj{~HmuD^maf

diff --git a/apps/settings/js/vue-4.js.map b/apps/settings/js/vue-4.js.map
index d78ea55117994640840113ac4e5f206d6defba58..d9b80b7ad159a28f084d023b5820a6e481b3a0a7 100644
GIT binary patch
delta 2024
zcma)7Z)_7~7|*-0KQM*44P}7vZkGO|T-SE%1`*ix*vc4W>oSD03cKE2+Z*j&xx20u
zvh;)TgAfwrRnJ5TLgE)c7|4BC;s+(s1QSg}O#~7~hBFzG2;q~7`oZ_TYw0O|FioC3
z|K8vGJiqsOu1~&eczD0z>QA7a3-+O|#y(?pU3f*CB5-mpCrG@&)A_tg&8UcN?nZ-k
zZRoYet1E;7Hf%Z;n>HBHrYp^L!HDs2joh&*#*DGPD9!M`Lo`EMBF3DI96#V2Vv2}!
zd^oK}NF;5_+#aGIH}#^abHougRiUO@8DcFXZK^SP2q&ATOAN^thv*SBy5&q=&oH{R
z<>+LL&O}KSX;YG`hDaJqM4X5dCQb=gT#cExiY8`|A@~EP-pkM!<HRr>MIq-$b=ff#
zXzer}IZwJfdYEBfE<#dAn9!ay?sQMkI5k!V>#U<D69eW&TTQG2dOYp`L#QKm$2Yr=
z)K|n(H9N?e(ke?HCZI98-^>w5QRl>H>&)1QZ-}Pjq<-EP?KiZEAHnA6sVMP|l%m*A
z2dm~m-zZIIqdeL-IRQ%O;^Y^Np`M6uB#e>={T+S%;b4gC4<~zq;elQ*nCuHBjgJpH
zUubuC9h=QjS()WUDZO7*HP3GJO^QR2`HlLZA2b-l>A#MZ{oaHGy>O6|^Cd-0XEYe>
z32cW<M!_fKd<GsWD5?fu<*JUsq9~mbl#HC(RwN;c)pijG3x+Ld8Cm)N7;VpjAbO^^
z!GuGX&+G+W{rV_~059^N9R}VDUxq5NYiAkk{Qfu&>1z%E8XMQ%>9>OV`2%1-b_8bE
zqx*mOX|F#aB_!cwURE^73n{jc)!-ENX)aEdG?vp;m(McUEZ!JashBxd%(@cN>w1|7
z@1YOAeg$2;eg)mU;WQTCD%Yb;e{L`ie;aK<Pi}Rh_ix2rww;41Mb1GNMfq(`vWOTD
zy`6)G`}U{x0ktS<Tn4(ejHpr^s|jgYDS6;&QVjWVtSUg4D5)Bo%?iA02Re4wjo!W6
z)^0^?*k;VpYx7W|#AOS-pt{+7o)USF=bx~G#3rktds{6)UQ}yN2im>Vg6bC5S8l^F
z+mZ!|&kCwl$>xgnFW^F+$E{Ry(BkbTlwWRsu0s3{QUrRi;6v%V8_}i3=h363CNy|2
zjFfv*^$BeboiDCK@=~L*bg!|VnzD88gLZ~p*sQjIRkL!md?##ZKR*Wg&2G?%sD;&b
z`d!fJ#EEiIB5UD-q6AIi9g1Ag1nNXVP)ewC+1rk%0uOzKcDz1tCq&G0H=(1AU=Thz
zRvNM11<A|XFTA-9eKprswsDLx<g7sPLb8x{Pusv8r)vg<=Pj*e=AiA}2)sJ|l+$rM
zNTMoGcmmwDgHOW=jnXoL<aT@D&Ryl&=$PYEsX>%dvfIv`c}2BBJXZv*GIHdcn1Uo-
zD=5;W{NFs;hSVBW{9WMXtwsFB!E-Fi66Wj~a;YjNxlF%&7_^Vg*&D$pxr|%DjX?4Q
zgg!x0WW|m5w2<YYBtLzXX9&aNAb31R?3dr_n}dC9swWi)qUldI8^VLn(l5<!6H_}A
z-CeftVWJyvV)=(ncp<G0650;TJkH_@iXuoFozIh;YH)tvIQVsCi|wKG#RPEb=i*?k
ze(4D4T5Air;2fMYzbX3oQSh>UBMy8m6ScqO`QPK973iyu0FOR11zyx2C4fW!JPuaQ
YV{>6ub0`oR2n=v3(!#uy0&jr70rA7hD*ylh

delta 771
zcmZ{gUr1A77{>Xw>1I;1xuI#{DRZkh>2c2OjIcU8t~JZjE&qXk*!kmeq-W{bsF6eH
zs=SEeg$_}87X?KM--XakS6xJ!7g7-D#Wb0#D7`P}%*=V?)%W9lp7(tozHjM*&4q%Q
z&&V+7;b}ld7e1hK8{oXo0+WR^2HM%cX;TWz#%U7@<EoOFbsyzpg=j13XBBvL{ORTE
zEW<LSi)H)_N%?ZDH0Rj4i|H+<c6pL46VRMnbB(nv$<GAfMa`sc^dh+I{jhGI(s^6)
zUB|-{)&zTj9P#C@2H9K9k`~^46Q6FIK=84>0<n;7|6;5QGGrGc!TZn=(^yN6lP&Nw
zbg3kMB}n=iribidSb+>Q;$V0Y=~+(z&UA#31ou1M7S<(wWY7iS&N^#7=Z$zHbfc@D
z_6}0+MqX&(@uN=rL0FJ@eIHq`9(5uc4iEhvfX5FB*!VLF{qxmot&E=G{&|BL?kt6X
zTRH^qKA2%zHsi|W*Lt{?Eh~!3N<1<=EDFG^*<dDJ2A|TFf>f{E8;7=RrBRe4Twe9$
z6_XHEc1xu|tQUiQr6O;Edzt;fu9a?`y}eSBSB!}pLL@FKJE4bvne|#IEU%DYNLQ%N
zYGeWLsvDHmE|h|SuQvRBwNMX<jCEqCvV#~OC*ng<rIy!@NwKjc(bUvLIJP_QCI2tJ
zm%iQ2aWeM_z3~+J?@cJ8FCh!VxG$4Ia4KcSH9zL`CYq)=+QqqH<Yg5u-*_Xz=3<#T
z*Nv>RwcRLNq>4PUsT7CG)ixeIN5m`=(79rh+dW8&5z&*|Q>S9+7@U1znSByNSJ7Ye
C%Nbn&

diff --git a/apps/settings/js/vue-5.js b/apps/settings/js/vue-5.js
index 8eb491497f46c411f12a9d9aa142e2dd6d51631a..98317284ec1e54385e20c5871e513deb7359d6eb 100644
GIT binary patch
delta 66
zcmZ2_i)raCrVYz^84V`-$W0CqVqq_`&C@V8GTeNTH=UW$5GuMkT5#KJ5wpb9l;l*y
R#55D*L`#cQb7MmzGXUqk6x09!

delta 64
zcmZ2_i)raCrVYz^Cwj|I4iI8tFS5<kFg7yYe33VunbCNnj~qyJbF|>L*&-=MW~r8@
SW+{fI=E(-hCPwC|7O4Qr{1j0D

diff --git a/apps/settings/js/vue-5.js.map b/apps/settings/js/vue-5.js.map
index 9f67448e32291599e495d78b269469de3c34ae21..d2fa0760e74879ab5c9f644dc70967065f332a3e 100644
GIT binary patch
delta 39
vcmZ3thjZN?&J9V&Ma&XYQ<75+6VptL6D=)L&5aF>%$m!NZ!bH}xMLasH}Vgd

delta 39
vcmZ3thjZN?&J9V&MN*8+QY}r*QVdPalMRwhjLcIlQk%<;Z!bH}xMLasD;*C)

diff --git a/apps/settings/js/vue-settings-apps-users-management.js b/apps/settings/js/vue-settings-apps-users-management.js
index 2bdf60ed261bac37e8ce7a3e8c30424c2ac9ac16..4d813889657f88949f96834e2479ad736f7b90b9 100644
GIT binary patch
delta 93
zcmbQbfPd-&{tY*nbWP38jEs}b&5{g^%q&cjjgm}_lazE!t(43XQ&W;t4HMH$j1w&_
vQq7GGjm(-^ncG>J8G)E-J1aBupEDwsNhwK*DW>KomL{o5hG}L=7DlE3@TnQ_

delta 93
zcmbQbfPd-&{tY*nbPba%6D<=hOic_eEYl3kOp{ZLla+K#t&~!X%u+2)%~A|a&65q1
vO^nP_EmE6VncG>J8G)E-J1aBupEDvxhDnA=2Fb>TW~qi|hK44oN#=$C?>`w*

diff --git a/apps/settings/js/vue-settings-apps-users-management.js.map b/apps/settings/js/vue-settings-apps-users-management.js.map
index c5da053eacbba8a78be9158b624adb0fc7d3dc67..25bdfba8f2ead84d74bd25d6d666624bf1236cbc 100644
GIT binary patch
delta 154
zcmWN=yA8rH6aYX0j)Cw;OyCkx5dXsl=}7O!&vurep|C`mWh`a5G`y2;F5O(ZZ(ihO
zUS*Q2LO<lzLIfX`k*!S73>qv~(@5x831u`F!Dx7LZDrt~1UU9^OgZ*)O#2i=YWyID
iZ*4N7;riFt6tD3HZ*h%xc#jYGh)?*8FH>J%@9#hSJt|TF

delta 154
zcmWN=xedZF6aY{GjsbHcCUA);^5ZLJgLI^K6CW%=Lt%+B%UH^AY4}fibLr;Nee)tO
z^D2{E75X8!20ZvW2+#>MfK1-NG!i;i0=ymU&?*V7eb-urD1ze;$Ccwx$77?DRXTz)
ip$@V!sZGD0QM|?*yu}Rf@E#xV5uflGU#6a2@2@|wuPL+u

diff --git a/apps/settings/src/components/AppList.vue b/apps/settings/src/components/AppList.vue
index a406f6b8ff6..f03d7c5a694 100644
--- a/apps/settings/src/components/AppList.vue
+++ b/apps/settings/src/components/AppList.vue
@@ -96,9 +96,11 @@
 </template>
 
 <script>
+import pLimit from 'p-limit'
+
 import AppItem from './AppList/AppItem'
 import PrefixMixin from './PrefixMixin'
-import pLimit from 'p-limit'
+import recommended from '../recommendedApps'
 
 export default {
 	name: 'AppList',
@@ -129,26 +131,26 @@ export default {
 					return OC.Util.naturalSortCompare(sortStringA, sortStringB)
 				})
 
-			if (this.category === 'installed') {
+			switch (this.category) {
+			case 'installed':
 				return apps.filter(app => app.installed)
-			}
-			if (this.category === 'enabled') {
+			case 'recommended':
+				return apps.filter(app => recommended.includes(app.id))
+			case 'enabled':
 				return apps.filter(app => app.active && app.installed)
-			}
-			if (this.category === 'disabled') {
+			case 'disabled':
 				return apps.filter(app => !app.active && app.installed)
-			}
-			if (this.category === 'app-bundles') {
+			case 'app-bundles':
 				return apps.filter(app => app.bundles)
-			}
-			if (this.category === 'updates') {
+			case 'updates':
 				return apps.filter(app => app.update)
+			default:
+				// filter app store categories
+				return apps.filter(app => {
+					return app.appstore && app.category !== undefined
+							&& (app.category === this.category || app.category.indexOf(this.category) > -1)
+				})
 			}
-			// filter app store categories
-			return apps.filter(app => {
-				return app.appstore && app.category !== undefined
-					&& (app.category === this.category || app.category.indexOf(this.category) > -1)
-			})
 		},
 		bundles() {
 			return this.$store.getters.getServerData.bundles.filter(bundle => this.bundleApps(bundle.id).length > 0)
@@ -175,7 +177,7 @@ export default {
 			return !this.useListView && !this.useBundleView
 		},
 		useListView() {
-			return (this.category === 'installed' || this.category === 'enabled' || this.category === 'disabled' || this.category === 'updates')
+			return ['installed', 'recommended', 'enabled', 'disabled', 'updates'].includes(this.category)
 		},
 		useBundleView() {
 			return (this.category === 'app-bundles')
@@ -196,6 +198,24 @@ export default {
 			}
 		}
 	},
+	mounted() {
+		if (this.category === 'recommended' && 'download' in this.$route.query) {
+			const limit = pLimit(1)
+			const installing = this.apps
+				.filter(app => !app.active && app.canInstall)
+				.map(app => limit(() => this.$store.dispatch('enableApp', { appId: app.id, groups: [] })))
+			console.debug(`installing ${installing.length} recommended apps`)
+			Promise.all(installing)
+				.then(() => {
+					console.info('recommended apps installed')
+
+					if ('returnTo' in this.$route.query) {
+						window.location = this.$route.query.returnTo
+					}
+				})
+				.catch(e => console.error('could not install recommended apps', e))
+		}
+	},
 	methods: {
 		toggleBundle(id) {
 			if (this.allBundlesEnabled(id)) {
diff --git a/apps/settings/src/recommendedApps.js b/apps/settings/src/recommendedApps.js
new file mode 100644
index 00000000000..d2868b8728b
--- /dev/null
+++ b/apps/settings/src/recommendedApps.js
@@ -0,0 +1,26 @@
+/*
+ * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+export default [
+	'contacts',
+	'calendar',
+	'mail'
+]
diff --git a/apps/settings/src/views/Apps.vue b/apps/settings/src/views/Apps.vue
index 2e0649f7f56..a2c4cae825d 100644
--- a/apps/settings/src/views/Apps.vue
+++ b/apps/settings/src/views/Apps.vue
@@ -31,7 +31,10 @@
 			</ul>
 		</AppNavigation>
 		<AppContent class="app-settings-content" :class="{ 'icon-loading': loadingList }">
-			<AppList :category="category" :app="currentApp" :search="searchQuery" />
+			<AppList v-if="!loadingList"
+				:category="category"
+				:app="currentApp"
+				:search="searchQuery" />
 		</AppContent>
 		<AppSidebar v-if="id && currentApp" @close="hideAppDetails">
 			<AppDetails :category="category" :app="currentApp" />
@@ -133,13 +136,21 @@ export default {
 					icon: 'icon-category-installed',
 					text: t('settings', 'Your apps')
 				},
+				{
+					id: 'app-category-recommended',
+					classes: [],
+					router: { name: 'apps-category', params: { category: 'recommended' } },
+					icon: 'icon-category-installed',
+					text: t('settings', 'Recommended apps')
+				},
 				{
 					id: 'app-category-enabled',
 					classes: [],
 					icon: 'icon-category-enabled',
 					router: { name: 'apps-category', params: { category: 'enabled' } },
 					text: t('settings', 'Active apps')
-				}, {
+				},
+				{
 					id: 'app-category-disabled',
 					classes: [],
 					icon: 'icon-category-disabled',
diff --git a/core/Controller/SetupController.php b/core/Controller/SetupController.php
index 3dd02fc31dc..fd0a6895471 100644
--- a/core/Controller/SetupController.php
+++ b/core/Controller/SetupController.php
@@ -31,6 +31,7 @@ namespace OC\Core\Controller;
 
 use OC\Setup;
 use OCP\ILogger;
+use function urlencode;
 
 class SetupController {
 	/** @var Setup */
@@ -76,7 +77,7 @@ class SetupController {
 				$options = array_merge($opts, $post, $errors);
 				$this->display($options);
 			} else {
-				$this->finishSetup();
+				$this->finishSetup(isset($post['install-recommended-apps']));
 			}
 		} else {
 			$options = array_merge($opts, $post);
@@ -105,7 +106,7 @@ class SetupController {
 		\OC_Template::printGuestPage('', 'installation', $parameters);
 	}
 
-	public function finishSetup() {
+	private function finishSetup(bool $installRecommended) {
 		if( file_exists( $this->autoConfigFile )) {
 			unlink($this->autoConfigFile);
 		}
@@ -117,6 +118,12 @@ class SetupController {
 			}
 		}
 
+		if ($installRecommended) {
+			$urlGenerator = \OC::$server->getURLGenerator();
+			$location = $urlGenerator->getAbsoluteURL('/index.php/settings/apps/recommended?download&returnTo=' . urlencode(\OC_Util::getDefaultPageUrl()));
+			header('Location: ' . $location);
+			exit();
+		}
 		\OC_Util::redirectToDefaultPage();
 	}
 
diff --git a/core/js/setup.js b/core/js/setup.js
index 5b0bd679dbe..eb41795e427 100644
--- a/core/js/setup.js
+++ b/core/js/setup.js
@@ -41,8 +41,6 @@ $(document).ready(function() {
 		$('#dbname').attr('pattern','[0-9a-zA-Z$_-.]+');
 	});
 
-	$('input[checked]').trigger('click');
-
 	$('#showAdvanced').click(function(e) {
 		e.preventDefault();
 		$('#datadirContent').slideToggle(250);
diff --git a/core/templates/installation.php b/core/templates/installation.php
index de9427a74d8..bdc06c54086 100644
--- a/core/templates/installation.php
+++ b/core/templates/installation.php
@@ -158,6 +158,15 @@ script('core', [
 		</fieldset>
 	<?php endif ?>
 
+	<fieldset>
+		<p class="info">
+			<input type="checkbox" id="install-recommended-apps" name="install-recommended-apps" class="checkbox checkbox--white" checked>
+			<label for="install-recommended-apps">
+				<?php p($l->t( 'Install recommended apps' )); ?>
+			</label>
+		</p>
+	</fieldset>
+
 	<div class="icon-loading-dark float-spinner">&nbsp;</div>
 
 	<div class="buttons"><input type="submit" class="primary" value="<?php p($l->t( 'Finish setup' )); ?>" data-finishing="<?php p($l->t( 'Finishing …' )); ?>"></div>
-- 
GitLab