Merge remote-tracking branch 'origin/develop' into new-mapped-collection

# Conflicts:
#	src/admin/pages/lists/collections-page.vue
This commit is contained in:
Jacson Passold 2018-05-29 20:19:56 -03:00
commit a9698cb3f6
63 changed files with 1945 additions and 908 deletions

View File

@ -67,7 +67,9 @@ rm -rf $wp_plugin_dir
mkdir $wp_plugin_dir
rsync -axz --exclude='vendor/bin/phpc*' --exclude='vendor/squizlabs' --exclude='vendor/wimg' src/* $wp_plugin_dir/
rsync -axz --exclude='vendor/bin/phpc*' --exclude='vendor/squizlabs' --exclude='vendor/wimg' \
--exclude='vendor/respect/validation/.git' --exclude='pdf-viewer/pdfjs-dist/web/compressed.tracemonkey-pldi-09.pdf' \
src/* $wp_plugin_dir/
rm -rf $wp_plugin_dir/scss

255
package-lock.json generated
View File

@ -109,9 +109,9 @@
}
},
"@vue/component-compiler-utils": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-1.2.1.tgz",
"integrity": "sha512-l3GdahBgXlp/SoY5KU7mqMqg/BNiJAndpw/6nMfGzFooCGUkq49CT3pCMiSYy0g/2a6iBD37cHBYlsg4nulIwQ==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-1.3.0.tgz",
"integrity": "sha512-CerIPMzE6y4J4v68nUh2NFPjOO8zr2r8UDh/yi63E4bf7YB5Aqhm7Fv90zzTF+sOQgsqTb9dgryvpeDY1/7m9g==",
"dev": true,
"requires": {
"consolidate": "0.15.1",
@ -430,7 +430,6 @@
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
"dev": true,
"requires": {
"co": "4.6.0",
"fast-deep-equal": "1.0.0",
@ -471,12 +470,14 @@
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true
},
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
"dev": true
},
"any-observable": {
"version": "0.2.0",
@ -620,7 +621,8 @@
"assert-plus": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
"integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ="
"integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=",
"dev": true
},
"assign-symbols": {
"version": "1.0.0",
@ -731,7 +733,8 @@
"aws-sign2": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
"integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8="
"integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=",
"dev": true
},
"aws4": {
"version": "1.6.0",
@ -1774,6 +1777,7 @@
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
"integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
"dev": true,
"requires": {
"hoek": "2.16.3"
}
@ -2166,14 +2170,15 @@
"dev": true
},
"caseless": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz",
"integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c="
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
"ansi-styles": "2.2.1",
"escape-string-regexp": "1.0.5",
@ -2460,8 +2465,7 @@
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
"dev": true
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
},
"coa": {
"version": "1.0.4",
@ -2549,9 +2553,10 @@
}
},
"commander": {
"version": "2.14.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.14.1.tgz",
"integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw=="
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
"dev": true
},
"common-tags": {
"version": "1.4.0",
@ -2805,6 +2810,7 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
"integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
"dev": true,
"requires": {
"boom": "2.10.1"
}
@ -3219,7 +3225,7 @@
"performance-now": "0.2.0",
"qs": "6.4.0",
"safe-buffer": "5.1.1",
"stringstream": "0.0.5",
"stringstream": "0.0.6",
"tough-cookie": "2.3.3",
"tunnel-agent": "0.6.0",
"uuid": "3.2.1"
@ -3824,7 +3830,8 @@
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
"dev": true
},
"eslint": {
"version": "4.19.1",
@ -4425,8 +4432,7 @@
"fast-deep-equal": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
"integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=",
"dev": true
"integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8="
},
"fast-glob": {
"version": "2.2.1",
@ -4444,8 +4450,7 @@
"fast-json-stable-stringify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
"dev": true
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
},
"fast-levenshtein": {
"version": "2.0.6",
@ -4694,6 +4699,7 @@
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz",
"integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=",
"dev": true,
"requires": {
"asynckit": "0.4.0",
"combined-stream": "1.0.6",
@ -5341,12 +5347,14 @@
"generate-function": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz",
"integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ="
"integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=",
"dev": true
},
"generate-object-property": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
"integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=",
"dev": true,
"requires": {
"is-property": "1.0.2"
}
@ -5705,14 +5713,19 @@
"dev": true
},
"har-validator": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz",
"integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=",
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
"integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
"requires": {
"chalk": "1.1.3",
"commander": "2.14.1",
"is-my-json-valid": "2.17.2",
"pinkie-promise": "2.0.1"
"ajv": "5.5.2",
"har-schema": "2.0.0"
},
"dependencies": {
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
}
}
},
"has": {
@ -5728,6 +5741,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
"dev": true,
"requires": {
"ansi-regex": "2.1.1"
}
@ -5833,6 +5847,7 @@
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
"integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=",
"dev": true,
"requires": {
"boom": "2.10.1",
"cryptiles": "2.0.5",
@ -5860,7 +5875,8 @@
"hoek": {
"version": "2.16.3",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
"integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0="
"integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
"dev": true
},
"home-or-tmp": {
"version": "2.0.0",
@ -5919,7 +5935,7 @@
"bluebird": "3.5.1",
"cheerio": "0.19.0",
"lodash": "3.10.1",
"request": "2.79.0"
"request": "2.87.0"
},
"dependencies": {
"lodash": {
@ -6035,6 +6051,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
"integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
"dev": true,
"requires": {
"assert-plus": "0.2.0",
"jsprim": "1.4.1",
@ -6523,12 +6540,14 @@
"is-my-ip-valid": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz",
"integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ=="
"integrity": "sha512-gmh/eWXROncUzRnIa1Ubrt5b8ep/MGSnfAUI3aRp+sqTCs1tv1Isl8d8F6JmkN3dXKc3ehZMrtiPN9eL03NuaQ==",
"dev": true
},
"is-my-json-valid": {
"version": "2.17.2",
"resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz",
"integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==",
"dev": true,
"requires": {
"generate-function": "2.0.0",
"generate-object-property": "1.2.0",
@ -6663,7 +6682,8 @@
"is-property": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
"integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ="
"integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=",
"dev": true
},
"is-regex": {
"version": "1.0.4",
@ -6963,8 +6983,7 @@
"json-schema-traverse": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=",
"dev": true
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
},
"json-stable-stringify": {
"version": "1.0.1",
@ -7016,7 +7035,8 @@
"jsonpointer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
"integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk="
"integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=",
"dev": true
},
"jsprim": {
"version": "1.4.1",
@ -7906,7 +7926,7 @@
"nopt": "3.0.6",
"npmlog": "4.1.2",
"osenv": "0.1.5",
"request": "2.79.0",
"request": "2.87.0",
"rimraf": "2.6.2",
"semver": "5.3.0",
"tar": "2.2.1",
@ -7979,6 +7999,12 @@
"true-case-path": "1.0.2"
},
"dependencies": {
"caseless": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz",
"integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=",
"dev": true
},
"cross-spawn": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
@ -7988,6 +8014,58 @@
"lru-cache": "4.1.1",
"which": "1.3.0"
}
},
"har-validator": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz",
"integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=",
"dev": true,
"requires": {
"chalk": "1.1.3",
"commander": "2.15.1",
"is-my-json-valid": "2.17.2",
"pinkie-promise": "2.0.1"
}
},
"qs": {
"version": "6.3.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz",
"integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=",
"dev": true
},
"request": {
"version": "2.79.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz",
"integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=",
"dev": true,
"requires": {
"aws-sign2": "0.6.0",
"aws4": "1.6.0",
"caseless": "0.11.0",
"combined-stream": "1.0.6",
"extend": "3.0.1",
"forever-agent": "0.6.1",
"form-data": "2.1.4",
"har-validator": "2.0.6",
"hawk": "3.1.3",
"http-signature": "1.1.1",
"is-typedarray": "1.0.0",
"isstream": "0.1.2",
"json-stringify-safe": "5.0.1",
"mime-types": "2.1.17",
"oauth-sign": "0.8.2",
"qs": "6.3.2",
"stringstream": "0.0.6",
"tough-cookie": "2.3.3",
"tunnel-agent": "0.4.3",
"uuid": "3.2.1"
}
},
"tunnel-agent": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz",
"integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=",
"dev": true
}
}
},
@ -8594,12 +8672,14 @@
"pinkie": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
"integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
"integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
"dev": true
},
"pinkie-promise": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
"integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
"dev": true,
"requires": {
"pinkie": "2.0.4"
}
@ -10321,36 +10401,66 @@
"dev": true
},
"request": {
"version": "2.79.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz",
"integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=",
"version": "2.87.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz",
"integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==",
"requires": {
"aws-sign2": "0.6.0",
"aws-sign2": "0.7.0",
"aws4": "1.6.0",
"caseless": "0.11.0",
"caseless": "0.12.0",
"combined-stream": "1.0.6",
"extend": "3.0.1",
"forever-agent": "0.6.1",
"form-data": "2.1.4",
"har-validator": "2.0.6",
"hawk": "3.1.3",
"http-signature": "1.1.1",
"form-data": "2.3.2",
"har-validator": "5.0.3",
"http-signature": "1.2.0",
"is-typedarray": "1.0.0",
"isstream": "0.1.2",
"json-stringify-safe": "5.0.1",
"mime-types": "2.1.17",
"oauth-sign": "0.8.2",
"qs": "6.3.2",
"stringstream": "0.0.5",
"performance-now": "2.1.0",
"qs": "6.5.2",
"safe-buffer": "5.1.1",
"tough-cookie": "2.3.3",
"tunnel-agent": "0.4.3",
"tunnel-agent": "0.6.0",
"uuid": "3.2.1"
},
"dependencies": {
"qs": {
"version": "6.3.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz",
"integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw="
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
},
"form-data": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz",
"integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=",
"requires": {
"asynckit": "0.4.0",
"combined-stream": "1.0.6",
"mime-types": "2.1.17"
}
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"requires": {
"assert-plus": "1.0.0",
"jsprim": "1.4.1",
"sshpk": "1.14.1"
}
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
}
}
},
@ -10534,8 +10644,7 @@
"safe-buffer": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
"dev": true
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
},
"safe-regex": {
"version": "1.1.0",
@ -10992,6 +11101,7 @@
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
"integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
"dev": true,
"requires": {
"hoek": "2.16.3"
}
@ -11327,14 +11437,16 @@
}
},
"stringstream": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
"integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg="
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz",
"integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==",
"dev": true
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
"ansi-regex": "2.1.1"
}
@ -11392,7 +11504,8 @@
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
"dev": true
},
"svgo": {
"version": "0.7.2",
@ -11710,9 +11823,12 @@
"dev": true
},
"tunnel-agent": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz",
"integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us="
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"requires": {
"safe-buffer": "5.1.1"
}
},
"tweetnacl": {
"version": "0.14.5",
@ -12214,12 +12330,12 @@
"dev": true
},
"vue-loader": {
"version": "15.0.11",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.0.11.tgz",
"integrity": "sha512-wyCwG/mbMWfW4hIYJbUWEkfPTVj4aMp/8G8qqnbnIsEZJqqCVBQ+pOO59NPQWjub8VAYMBeIB7crHlWsWS2Ufw==",
"version": "15.2.0",
"resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.2.0.tgz",
"integrity": "sha512-vJuybfEVtYU2HZUqUHMiqBU1/9K2HFplbsRJMca6B2hPNQt5Kx24mrVK9Ej+vlgM+3zf+7WIKKQPjJwvdHbkdA==",
"dev": true,
"requires": {
"@vue/component-compiler-utils": "1.2.1",
"@vue/component-compiler-utils": "1.3.0",
"hash-sum": "1.0.2",
"loader-utils": "1.1.0",
"vue-hot-reload-api": "2.3.0",
@ -13249,7 +13365,8 @@
"xtend": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
"dev": true
},
"y18n": {
"version": "3.2.1",

View File

@ -41,7 +41,7 @@
"style-loader": "^0.21.0",
"uglifyjs-webpack-plugin": "^1.2.5",
"vue-custom-element": "^3.0.6",
"vue-loader": "^15.0.11",
"vue-loader": "^15.1.0",
"vue-template-compiler": "^2.5.16",
"webpack": "^4.8.3",
"webpack-cli": "^2.1.3",

View File

@ -177,6 +177,7 @@ class Admin {
'base_url' => $TAINACAN_BASE_URL,
'admin_url' => admin_url(),
'custom_header_support' => get_theme_support('custom-header'),
'registered_view_modes' => \Tainacan\Theme_Helper::get_instance()->get_registered_view_modes(),
];
$maps = [

View File

@ -276,6 +276,38 @@
</b-select>
</b-field>
<!-- Enabled View Modes ------------------------------- -->
<div class="field">
<b-dropdown
ref="enabledViewModesDropdown"
:mobile-modal="false"
:disabled="Object.keys(registeredViewModes).length < 0">
<button
class="button is-white"
slot="trigger"
position="is-top-right"
type="button">
<span>{{ $i18n.get('label_enabled_view_modes') }}</span>
<b-icon icon="menu-down"/>
</button>
<b-dropdown-item
v-for="(viewMode, index) in Object.keys(registeredViewModes)"
:key="index"
class="control"
custom>
<b-checkbox
@input="updateViewModeslist(viewMode)"
:value="checkIfViewModeEnabled(viewMode)">
{{ registeredViewModes[viewMode].label }}
</b-checkbox>
</b-dropdown-item>
</b-dropdown>
<help-button
:title="$i18n.getHelperTitle('collections', 'enabled_view_modes')"
:message="$i18n.getHelperMessage('collections', 'enabled_view_modes')"/>
</div>
</div>
</div>
@ -360,7 +392,9 @@ export default {
collections: [],
isFetchingCollections: true,
thumbnailMediaFrame: undefined,
headerImageMediaFrame: undefined
headerImageMediaFrame: undefined,
registeredViewModes: tainacan_plugin.registered_view_modes,
viewModesList: []
}
},
methods: {
@ -395,7 +429,8 @@ export default {
slug: this.form.slug,
status: this.form.status,
moderators_ids: this.form.moderators_ids,
parent: this.form.parent
parent: this.form.parent,
enabled_view_modes: this.form.enabled_view_modes
};
this.updateCollection(data).then(updatedCollection => {
@ -408,6 +443,7 @@ export default {
this.form.status = this.collection.status;
this.form.cover_page_id = this.collection.cover_page_id;
this.form.enable_cover_page = this.collection.enable_cover_page;
this.form.enabled_view_modes = this.collection.enabled_view_modes;
this.isLoading = false;
this.formErrorMessage = '';
@ -446,8 +482,21 @@ export default {
this.form.cover_page_id = this.collection.cover_page_id;
this.form.slug = this.collection.slug;
this.form.parent = this.collection.parent;
this.form.enabled_view_modes = [];
this.moderators = [];
// Fills or update enabled view modes
// for (let viewMode of Object.keys(this.registeredViewModes)) {
// let indexOfViewMode = this.form.enabled_view_modes.findIndex(aViewMode => aViewMode.viewMode == viewMode);
// if (indexOfViewMode < 0) {
// this.form.enabled_view_modes.push({
// viewMode: viewMode,
// label: this.registeredViewModes[viewMode].label,
// enabled: false
// });
// }
// }
// Pre-fill status with publish to incentivate it
this.form.status = 'publish';
@ -474,6 +523,18 @@ export default {
cancelBack(){
this.$router.push(this.$routerHelper.getCollectionsPath());
},
updateViewModeslist(viewMode) {
let index = this.form.enabled_view_modes.findIndex(aViewMode => aViewMode == viewMode);
if (index > -1)
this.form.enabled_view_modes.splice(index, 1);
else
this.form.enabled_view_modes.push(viewMode);
},
checkIfViewModeEnabled(viewMode) {
let index = this.form.enabled_view_modes.findIndex(aViewMode => aViewMode == viewMode);
return index > -1;
},
fecthCoverPages(search) {
this.isFetchingPages = true;
this.fetchPages(search)
@ -581,7 +642,7 @@ export default {
if (this.$route.fullPath.split("/").pop() == "new") {
this.createNewCollection();
this.isNewCollection = true;
} else if (this.$route.fullPath.split("/").pop() == "edit") {
} else if (this.$route.fullPath.split("/").pop() == "settings") {
this.isLoading = true;
@ -603,8 +664,20 @@ export default {
this.form.enable_cover_page = this.collection.enable_cover_page;
this.form.cover_page_id = this.collection.cover_page_id;
this.form.parent = this.collection.parent;
this.form.enabled_view_modes = JSON.parse(JSON.stringify(this.collection.enabled_view_modes));
this.moderators = JSON.parse(JSON.stringify(this.collection.moderators));
// Fills or update enabled view modes
// for (let viewMode of Object.keys(this.registeredViewModes)) {
// let indexOfViewMode = this.form.enabled_view_modes.findIndex(aViewMode => aViewMode.viewMode == viewMode);
// if (indexOfViewMode < 0) {
// this.form.enabled_view_modes.push({
// viewMode: viewMode,
// label: this.registeredViewModes[viewMode].label,
// enabled: false
// });
// }
// }
// Generates CoverPage from current cover_page_id info
if (this.form.cover_page_id != undefined && this.form.cover_page_id != '') {
@ -650,6 +723,7 @@ export default {
}
},
mounted() {
if (this.$route.fullPath.split("/").pop() != "new") {
document.getElementById('collection-page-container').addEventListener('scroll', ($event) => {
this.$emit('onShrinkHeader', ($event.originalTarget.scrollTop > 53));
@ -664,12 +738,11 @@ export default {
@import "../../scss/_variables.scss";
.tainacan-form>.columns>.column {
overflow: auto;
.field {
position: relative;
}
.field {
position: relative;
}
.thumbnail-field {
max-height: 128px;
margin-bottom: 96px;

View File

@ -459,6 +459,7 @@ export default {
this.form.document_type = this.item.document_type;
this.loadMetadata();
this.fetchAttachments(this.itemId);
})
.catch(error => this.$console.error(error));

View File

@ -36,7 +36,7 @@
</div>
<div class="table-wrapper">
<table class="table">
<table class="tainacan-table">
<thead>
<tr>
<!-- Checking list -->
@ -78,7 +78,13 @@
@click="goToCategoryEditPage(category.id)"
:label="$i18n.get('label_name')"
:aria-label="$i18n.get('label_name') + ': ' + category.name">
<p>{{ category.name }}</p>
<p
v-tooltip="{
content: category.name,
autoHide: false,
placement: 'auto-start'
}">
{{ category.name }}</p>
</td>
<!-- Description -->
<td
@ -86,7 +92,13 @@
@click="goToCategoryEditPage(category.id)"
:label="$i18n.get('label_description')"
:aria-label="$i18n.get('label_description') + ': ' + category.description">
<p>{{ category.description }}</p>
<p
v-tooltip="{
content: category.description,
autoHide: false,
placement: 'auto-start'
}">
{{ category.description }}</p>
</td>
<!-- Actions -->
<td

View File

@ -34,7 +34,7 @@
</div>
</div>
<div class="table-wrapper">
<table class="table">
<table class="tainacan-table">
<thead>
<tr>
<!-- Checking list -->
@ -54,9 +54,13 @@
<th>
<div class="th-wrap">{{ $i18n.get('label_description') }}</div>
</th>
<!-- Creation -->
<!-- Creation Date -->
<th>
<div class="th-wrap">{{ $i18n.get('label_creation') }}</div>
<div class="th-wrap">{{ $i18n.get('label_creation_date') }}</div>
</th>
<!-- Created By -->
<th>
<div class="th-wrap">{{ $i18n.get('label_created_by') }}</div>
</th>
<th class="actions-header">
&nbsp;
@ -95,7 +99,13 @@
@click="goToCollectionPage(collection.id)"
:label="$i18n.get('label_name')"
:aria-label="$i18n.get('label_name') + ': ' + collection.name">
<p>{{ collection.name }}</p>
<p
v-tooltip="{
content: collection.name,
autoHide: false,
placement: 'auto-start'
}">
{{ collection.name }}</p>
</td>
<!-- Description -->
<td
@ -103,15 +113,41 @@
@click="goToCollectionPage(collection.id)"
:label="$i18n.get('label_description')"
:aria-label="$i18n.get('label_description') + ': ' + collection.description">
<p>{{ collection.description }}</p>
<p
v-tooltip="{
content: collection.description,
autoHide: false,
placement: 'auto-start'
}">
{{ collection.description }}</p>
</td>
<!-- Creation -->
<!-- Creation Date -->
<td
@click="goToCollectionPage(collection.id)"
class="table-creation column-default-width"
:label="$i18n.get('label_creation')"
:aria-label="$i18n.get('label_creation') + ': ' + collection.creation">
<p v-html="collection.creation" />
:label="$i18n.get('label_creation_date')"
:aria-label="$i18n.get('label_creation_date') + ': ' + collection.creation_date">
<p
v-tooltip="{
content: collection.creation_date,
autoHide: false,
placement: 'auto-start'
}"
v-html="collection.creation_date" />
</td>
<!-- Created by -->
<td
@click="goToCollectionPage(collection.id)"
class="table-creation column-default-width"
:label="$i18n.get('label_created_by')"
:aria-label="$i18n.get('label_created_by') + ': ' + collection.author_name">
<p
v-tooltip="{
content: collection.author_name,
autoHide: false,
placement: 'auto-start'
}"
v-html="collection.author_name" />
</td>
<!-- Actions -->
<td
@ -133,7 +169,7 @@
@click.prevent.stop="deleteOneCollection(collection.id)">
<b-icon
type="is-secondary"
icon="delete"/>
:icon="!isOnTrash ? 'delete' : 'delete-forever'"/>
</a>
</div>
</td>
@ -161,7 +197,8 @@ export default {
totalCollections: 0,
page: 1,
collectionsPerPage: 12,
collections: Array
collections: Array,
isOnTrash: false
},
watch: {
collections() {
@ -193,9 +230,9 @@ export default {
},
deleteOneCollection(collectionId) {
this.$dialog.confirm({
message: this.$i18n.get('info_warning_collection_delete'),
message: this.isOnTrash ? this.$i18n.get('info_warning_collection_delete') : this.$i18n.get('info_warning_collection_trash'),
onConfirm: () => {
this.deleteCollection(collectionId)
this.deleteCollection({ collectionId: collectionId, isPermanently: this.isOnTrash })
.then(() => {
// this.$toast.open({
// duration: 3000,
@ -205,7 +242,7 @@ export default {
// queue: true
// });
for (let i = 0; i < this.selectedCollections.length; i++) {
if (this.selectedCollections[i].id == this.collectionId)
if (this.selectedCollections[i].id == collectionId)
this.selectedCollections.splice(i, 1);
}
}).catch(() => {
@ -222,12 +259,12 @@ export default {
},
deleteSelectedCollections() {
this.$dialog.confirm({
message: this.$i18n.get('info_warning_selected_collections_delete'),
message: this.isOnTrash ? this.$i18n.get('info_warning_selected_collections_delete') : this.$i18n.get('info_warning_selected_collections_trash'),
onConfirm: () => {
for (let i = 0; i < this.collections.length; i++) {
if (this.selectedCollections[i]) {
this.deleteCollection(this.collections[i].id)
this.deleteCollection({ collectionId: this.collections[i].id, isPermanently: this.isOnTrash })
.then(() => {
// this.loadCollections();
// this.$toast.open({

View File

@ -1,16 +1,20 @@
<template>
<div class="table-container">
<div class="table-wrapper">
<table class="table">
<table class="tainacan-table">
<thead>
<tr>
<!-- Title -->
<th>
<div class="th-wrap">{{ $i18n.get('label_event_title') }}</div>
</th>
<!-- Who and When -->
<!-- Created by -->
<th>
<div class="th-wrap">{{ $i18n.get('label_who_when') }}</div>
<div class="th-wrap">{{ $i18n.get('label_created_by') }}</div>
</th>
<!-- Event date -->
<th>
<div class="th-wrap">{{ $i18n.get('label_event_date') }}</div>
</th>
<!-- Status -->
<!--<th>-->
@ -28,15 +32,40 @@
@click="goToEventPage(event.id)"
:label="$i18n.get('label_event_title')"
:aria-label="$i18n.get('label_event_title') + ': ' + event.title">
<p>{{ event.title }}</p>
<p
v-tooltip="{
content: event.title,
autoHide: false,
placement: 'auto-start'
}">{{ event.title }}</p>
</td>
<!-- Who and When -->
<!-- User -->
<td
class="table-creation column-small-width"
@click="goToEventPage(event.id)"
:label="$i18n.get('label_who_when')"
:aria-label="$i18n.get('label_who_when') + ': ' + event.by">
<p v-html="event.by" />
:label="$i18n.get('label_created_by')"
:aria-label="$i18n.get('label_created_by') + ': ' + event.user_name">
<p
v-tooltip="{
content: event.user_name,
autoHide: false,
placement: 'auto-start'
}"
v-html="event.user_name" />
</td>
<!-- Event Date -->
<td
class="table-creation column-small-width"
@click="goToEventPage(event.id)"
:label="$i18n.get('label_event_date')"
:aria-label="$i18n.get('label_event_date') + ': ' + event.log_date">
<p
v-tooltip="{
content: event.log_date,
autoHide: false,
placement: 'auto-start'
}"
v-html="event.log_date" />
</td>
<!-- Status -->
<!--<td-->

View File

@ -69,14 +69,14 @@
v-model="filter.enabled"
@input="onChangeEnable($event, index)"/>
<a
:style="{ visibility: filter.collection_id != collectionId ? 'hidden' : 'visible' }"
:style="{ visibility: filter.collection_id != collectionId && !isRepositoryLevel? 'hidden' : 'visible' }"
@click.prevent="editFilter(filter)">
<b-icon
type="is-gray"
icon="pencil"/>
</a>
<a
:style="{ visibility: filter.collection_id != collectionId ? 'hidden' : 'visible' }"
:style="{ visibility: filter.collection_id != collectionId && !isRepositoryLevel ? 'hidden' : 'visible' }"
@click.prevent="removeFilter(filter)">
<b-icon
type="is-gray"

View File

@ -13,6 +13,7 @@
</div>
<div class="field is-pulled-right">
<b-dropdown
:mobile-modal="false"
position="is-bottom-left"
v-if="items.length > 0 && items[0].current_user_can_edit"
:disabled="!isSelectingItems"
@ -38,7 +39,7 @@
<div class="table-wrapper">
<table
:class="{'selectable-table': !isOnTheme }"
class="table">
class="tainacan-table">
<thead>
<tr>
<!-- Checking list -->
@ -112,7 +113,8 @@
v-tooltip="{
content: renderMetadata( item.metadata[column.slug] ),
html: true,
autoHide: false
autoHide: false,
placement: 'auto-start'
}"
v-if="item.metadata != undefined &&
column.field !== 'row_thumbnail' &&
@ -126,7 +128,14 @@
class="table-thumb"
:src="item[column.slug].thumb">
</span>
<p v-if="column.field == 'row_author' || column.field == 'row_creation'">
<p
v-tooltip="{
content: item[column.slug],
html: true,
autoHide: false,
placement: 'auto-start'
}"
v-if="column.field == 'row_author' || column.field == 'row_creation'">
{{ item[column.slug] }}
</p>
@ -152,12 +161,10 @@
@click.prevent.stop="deleteOneItem(item.id)">
<b-icon
type="is-secondary"
icon="delete"/>
:icon="!isOnTrash ? 'delete' : 'delete-forever'"/>
</a>
</div>
</td>
</tr>
</tbody>
</table>
@ -167,7 +174,6 @@
<script>
import { mapActions } from 'vuex';
import DataAndTooltip from '../other/data-and-tooltip.vue'
export default {
name: 'ItemsList',
@ -183,10 +189,8 @@ export default {
tableFields: Array,
items: Array,
isLoading: false,
isOnTheme: false
},
components: {
DataAndTooltip
isOnTheme: false,
isOnTrash: false
},
mounted() {
this.selectedItems = [];
@ -218,9 +222,9 @@ export default {
},
deleteOneItem(itemId) {
this.$dialog.confirm({
message: this.$i18n.get('info_warning_item_delete'),
message: this.isOnTrash ? this.$i18n.get('info_warning_item_delete') : this.$i18n.get('info_warning_item_trash'),
onConfirm: () => {
this.deleteItem(itemId)
this.deleteItem({ itemId: itemId, isPermanently: this.isOnTrash })
.then(() => {
// this.$toast.open({
// duration: 3000,
@ -230,7 +234,7 @@ export default {
// queue: true
// });
for (let i = 0; i < this.selectedItems.length; i++) {
if (this.selectedItems[i].id == this.itemId)
if (this.selectedItems[i].id == itemId)
this.selectedItems.splice(i, 1);
}
}).catch(() => {
@ -248,12 +252,12 @@ export default {
},
deleteSelectedItems() {
this.$dialog.confirm({
message: this.$i18n.get('info_warning_selected_items_delete'),
message: this.isOnTrash ? this.$i18n.get('info_warning_selected_items_delete') : this.$i18n.get('info_warning_selected_items_trash'),
onConfirm: () => {
for (let i = 0; i < this.selectedItems.length; i++) {
if (this.selectedItems[i]) {
this.deleteItem(this.items[i].id)
this.deleteItem({ itemId: this.items[i].id, isPermanently: this.isOnTrash })
.then(() => {
// this.$toast.open({
// duration: 3000,
@ -300,15 +304,6 @@ export default {
} else {
return metadata.value_as_html;
}
},
getCreationHtml(item) {
return this.$i18n.get('info_date') + item['creation_date'];
},
getAuthorHtml(item) {
return item['author_name'];
},
getDecodedURI(url) {
return decodeURIComponent(url);
}
}
}

View File

@ -1,75 +1,93 @@
<template>
<nav
id="primary-menu"
:class="isMenuCompressed ? 'is-compressed' : ''"
role="navigation"
:aria-label="$i18n.get('label_main_menu')"
<nav
id="primary-menu"
:class="isMenuCompressed ? 'is-compressed' : ''"
role="navigation"
:aria-label="$i18n.get('label_main_menu')"
class="column is-sidebar-menu">
<aside class="menu">
<ul class="menu-list">
<li><router-link
tag="a"
to="/collections"
:class="activeRoute == 'CollectionsPage' || $route.params.collectionId != undefined ? 'is-active':''">
<b-icon
size="is-small"
icon="folder-multiple"/> <span class="menu-text">{{ $i18n.getFrom('collections', 'name') }}</span>
</router-link></li>
<!-- <li><router-link
tag="a"
to="/items"
:class="activeRoute == 'ItemsPage' ? 'is-active':''">
<b-icon
size="is-small"
icon="file-multiple"/> <span class="menu-text">{{ $i18n.getFrom('items', 'name') }}</span>
</router-link></li> -->
<li>
<router-link
tag="a"
to="/collections"
:class="activeRoute == 'CollectionsPage' || $route.params.collectionId != undefined ? 'is-active':''">
<b-icon
size="is-small"
icon="folder-multiple"/>
<span class="menu-text">{{ $i18n.getFrom('collections', 'name') }}</span>
</router-link>
</li>
<li>
<router-link
tag="a"
to="/items"
:class="activeRoute == 'ItemsPage' ? 'is-active':''">
<b-icon
size="is-small"
icon="file-multiple"/>
<span class="menu-text">{{ $i18n.getFrom('items', 'name') }}</span>
</router-link>
</li>
<li class="separator"/>
<li><router-link
tag="a"
to="/fields"
:class="activeRoute == 'FieldsPage' ? 'is-active':''">
<b-icon
size="is-small"
icon="format-list-bulleted-type"/> <span class="menu-text">{{ $i18n.getFrom('fields', 'name') }}</span>
</router-link></li>
<li><router-link
tag="a"
to="/filters"
:class="activeRoute == 'FiltersPage' ? 'is-active':''">
<b-icon
size="is-small"
icon="filter"/> <span class="menu-text">{{ $i18n.getFrom('filters', 'name') }}</span>
</router-link></li>
<li><router-link
tag="a"
to="/taxonomies"
:class="activeRoute == 'CategoriesPage' ? 'is-active':''">
<b-icon
size="is-small"
icon="shape"/> <span class="menu-text">{{ $i18n.getFrom('categories', 'name') }}</span>
</router-link></li>
<li><router-link
tag="a"
to="/events"
:class="activeRoute == 'EventsPage' ? 'is-active':''">
<b-icon
size="is-small"
icon="flash"/> <span class="menu-text">{{ $i18n.get('events') }}</span>
</router-link></li>
<li>
<router-link
tag="a"
to="/fields"
:class="activeRoute == 'FieldsPage' ? 'is-active':''">
<b-icon
size="is-small"
icon="format-list-bulleted-type"/>
<span class="menu-text">{{ $i18n.getFrom('fields', 'name') }}</span>
</router-link>
</li>
<li>
<router-link
tag="a"
to="/filters"
:class="activeRoute == 'FiltersPage' ? 'is-active':''">
<b-icon
size="is-small"
icon="filter"/>
<span class="menu-text">{{ $i18n.getFrom('filters', 'name') }}</span>
</router-link>
</li>
<li>
<router-link
tag="a"
to="/taxonomies"
:class="activeRoute == 'CategoriesPage' ? 'is-active':''">
<b-icon
size="is-small"
icon="shape"/>
<span class="menu-text">{{ $i18n.getFrom('categories', 'name') }}</span>
</router-link>
</li>
<li>
<router-link
tag="a"
to="/events"
:class="activeRoute == 'EventsPage' ? 'is-active':''">
<b-icon
size="is-small"
icon="flash"/>
<span class="menu-text">{{ $i18n.get('events') }}</span>
</router-link>
</li>
</ul>
</aside>
</nav>
</template>
<script>
export default {
name: 'PrimaryMenu',
props: {
isMenuCompressed: false,
activeRoute: '/collections'
export default {
name: 'PrimaryMenu',
props: {
isMenuCompressed: false,
activeRoute: '/collections'
}
}
}
</script>
<style lang="scss" scoped>
@ -78,19 +96,19 @@ export default {
#primary-menu {
background-color: $primary;
padding: 100px 0px 0px 0px;
padding: 100px 0px 0px 0px;
-webkit-transition: max-width 0.2s linear; /* Safari */
transition: max-width 0.2s linear;
transition: max-width 0.2s linear;
max-width: $side-menu-width;
z-index: 99;
.separator {
height: 2px;
background-color: $separator-color;
width: 100%;
margin: 24px 0;
}
li{
li {
a {
color: white;
white-space: nowrap;
@ -99,13 +117,13 @@ export default {
line-height: 1.5em;
border-radius: 0px;
-webkit-transition: padding 0.2s linear; /* Safari */
transition: padding 0.2s linear;
transition: padding 0.2s linear;
}
a:hover, a.is-active {
background-color: $primary;
color: $tertiary;
}
a:focus{
a:focus {
box-shadow: none;
}
.menu-text {
@ -119,30 +137,32 @@ export default {
&.is-compressed {
max-width: 45px;
a {
a {
padding-left: 0.8em;
padding-right: 0.8em;
}
.menu-text {
visibility: hidden;
.menu-text {
visibility: hidden;
opacity: 0;
}
}
@media screen and (max-width: 769px) {
width: 100% !important;
max-width: 100% !important;
max-width: 100% !important;
padding-top: $header-height;
.menu{
.menu {
padding-top: 0px;
}
ul {
ul {
flex-flow: wrap;
display: flex;
align-items: stretch;
justify-content: space-evenly;
.separator { display: none; }
a{
justify-content: space-evenly;
.separator {
display: none;
}
a {
padding: 0.8em !important;
text-align: center;
}

View File

@ -1,89 +0,0 @@
<template>
<span class="data-wrapper">
<p
class="data-area"
v-html="data" />
<div class="data-tooltip">
<div class="data-tooltip-body">
<p v-html="data" />
</div>
</div>
</span>
</template>
<script>
export default {
name: 'DataAndTooltip',
props: {
data: '',
}
}
</script>
<style lang="scss">
@import "../../scss/_variables.scss";
.data-wrapper {
position: relative;
}
.data-area {
text-overflow: ellipsis;
overflow-x: hidden;
white-space: nowrap;
margin-bottom: 0;
}
.data-wrapper:hover .data-tooltip {
margin-bottom: -8px;
margin-left: -8px;
visibility: visible;
opacity: 1;
}
.data-tooltip {
z-index: 99999999999999999999;
color: $tertiary;
background-color: $primary-light;
border: none;
display: block;
border-radius: 5px;
max-width: 280px;
min-width: 100px;
height: auto;
transition: margin-bottom 0.2s ease, opacity 0.3s ease;
position: absolute;
bottom: calc(100% - 6px);
left: 0;
margin-bottom: -27px;
visibility: hidden;
opacity: 0;
.data-tooltip-body {
padding: 20px;
p {
font-size: 11px;
font-weight: normal;
}
}
&:before {
content: "";
display: block;
position: absolute;
left: 28px;
width: 0;
height: 0;
border-style: solid;
}
&:before {
border-color: $primary-light transparent transparent transparent;
border-right-width: 6px;
border-top-width: 7px;
border-left-width: 6px;
bottom: -10px;
}
}
</style>

View File

@ -0,0 +1,97 @@
<template>
<b-field class="filter-item-forms">
<b-collapse
class="show"
:open="open">
<label
class="label"
slot="trigger"
slot-scope="props">
<b-icon
:icon="props.open ? 'menu-down' : 'menu-right'"
/>
{{ $i18n.get('collections') }}
</label>
<div
class="block overflow-at">
<div
v-for="(collection, key) in collections"
:key="key"
class="control">
<b-checkbox
v-model="collectionsIdsToFilter"
:native-value="collection.id"
@input="apply_filter">
{{ collection.name }}
</b-checkbox>
</div>
</div>
</b-collapse>
</b-field>
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
export default {
name: 'CollectionFilter',
props: {
query: Object,
open: false,
},
created(){
this.fetchCollections({page: 1, collectionsPerPage: -1, status: null});
},
mounted(){
let routeQueries = this.$route.query;
if(routeQueries.metaquery &&
routeQueries.metaquery[0] &&
Array.isArray(routeQueries.metaquery[0].value)){
this.collectionsIdsToFilter = routeQueries.metaquery[0].value;
this.apply_filter();
}
},
data(){
return {
inputs: [],
collectionsIdsToFilter: []
}
},
computed: {
collections(){
return this.getCollections();
},
},
methods: {
...mapActions('search', [
'setPage'
]),
...mapActions('collection', [
'fetchCollections'
]),
...mapGetters('collection', [
'getCollections',
]),
apply_filter(){
this.$eventBusSearch.$emit( 'input', {
filter: 'checkbox',
field_id: 'collection_id',
value: this.collectionsIdsToFilter,
compare: 'IN',
collection_id: this.collectionsIdsToFilter,
});
},
}
}
</script>
<style scoped>
.overflow-at {
overflow: auto;
max-height: 125px;
}
</style>

View File

@ -1,22 +1,29 @@
<template>
<div>
<collections-filter
:open="collapsed"
:query="getQuery"
v-if="isRepositoryLevel"/>
<tainacan-filter-item
v-show="!isMenuCompressed"
:query="getQuery"
v-for="(filter, index) in filters"
:key="index"
:filter="filter"
:open="collapsed"/>
:open="collapsed"
:is-repository-level="isRepositoryLevel"/>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import CollectionsFilter from '../repository/collection-filter/collection-filter.vue';
export default {
props: {
filters: Array,
collapsed: Boolean,
isRepositoryLevel: Boolean,
},
methods: {
...mapGetters('search',[
@ -27,6 +34,9 @@
getQuery() {
return this.getPostQuery();
}
},
components: {
CollectionsFilter
}
}
</script>

View File

@ -1,82 +1,143 @@
<template>
<span>
<div class="sub-header">
<div
class="header-item"
v-if="!isOnTheme">
<b-dropdown
:mobile-modal="false"
id="item-creation-options-dropdown">
<button
class="button is-secondary"
slot="trigger">
<span>{{ $i18n.getFrom('items','add_new') }}</span>
<b-icon icon="menu-down"/>
</button>
<b-dropdown-item>
<router-link
id="a-create-item"
tag="div"
:to="{ path: $routerHelper.getNewItemPath(collectionId) }">
{{ $i18n.get('add_one_item') }}
</router-link>
</b-dropdown-item>
<b-dropdown-item disabled>
{{ $i18n.get('add_items_bulk') + ' (Not ready)' }}
</b-dropdown-item>
<b-dropdown-item disabled>
{{ $i18n.get('add_items_external_source') + ' (Not ready)' }}
</b-dropdown-item>
</b-dropdown>
</div>
<div class="header-item">
<b-dropdown
ref="displayedFieldsDropdown"
:mobile-modal="false"
:disabled="!hasResults"
class="show">
<button
class="button is-white"
slot="trigger">
<span>{{ $i18n.get('label_table_fields') }}</span>
<b-icon icon="menu-down"/>
</button>
<div class="metadata-options-container">
<b-dropdown-item
v-for="(column, index) in localTableFields"
:key="index"
class="control"
custom>
<b-checkbox
v-model="column.display"
:native-value="column.display">
{{ column.name }}
</b-checkbox>
</b-dropdown-item>
</div>
<div class="dropdown-item-apply">
<button
@click="onChangeDisplayedFields()"
class="button is-success">
{{ $i18n.get('label_apply_changes') }}
</button>
</div>
</b-dropdown>
</div>
<div
v-if="isOnTheme"
class="header-item">
<b-field>
<b-dropdown
@change="onChangeViewMode($event)"
:value="viewMode">
<button
class="button is-white"
slot="trigger">
<span>View (tests)</span>
<b-icon icon="menu-down" />
</button>
<b-dropdown-item :value="'table'">
<b-icon icon="table"/>
Table
</b-dropdown-item>
<b-dropdown-item :value="'cards'">
<b-icon icon="view-grid"/>
Cards
</b-dropdown-item>
<b-dropdown-item :value="'list'">
<b-icon icon="view-list"/>
List
</b-dropdown-item>
</b-dropdown>
</b-field>
</div>
<div class="header-item">
<b-field>
<b-select
:disabled="!hasResults"
@input="onChangeOrderBy($event)"
:placeholder="$i18n.get('label_sorting')">
<option
v-for="field in tableFields"
v-if="
field.id === 'creation_date' ||
field.id === 'author_name' || (
field.id !== undefined &&
field.field_type_object.related_mapped_prop !== 'description' &&
field.field_type_object.primitive_type !== 'term' &&
field.field_type_object.primitive_type !== 'item' &&
field.field_type_object.primitive_type !== 'compound'
)"
:value="field"
:key="field.id">
{{ field.name }}
</option>
</b-select>
<button
:disabled="!hasResults"
class="button is-white is-small"
@click="onChangeOrder()">
<b-icon :icon="order === 'ASC' ? 'sort-ascending' : 'sort-descending'"/>
</button>
</b-field>
</div>
</div>
<div
class="header-item"
v-if="!isOnTheme">
<b-dropdown id="item-creation-options-dropdown">
<button
class="button is-secondary"
slot="trigger">
<span>{{ $i18n.getFrom('items','add_new') }}</span>
<b-icon icon="menu-down"/>
</button>
<b-dropdown-item>
<router-link
id="a-create-item"
tag="div"
:to="{ path: $routerHelper.getNewItemPath(collectionId) }">
{{ $i18n.get('add_one_item') }}
</router-link>
</b-dropdown-item>
<b-dropdown-item disabled>
{{ $i18n.get('add_items_bulk') + ' (Not ready)' }}
</b-dropdown-item>
<b-dropdown-item disabled>
{{ $i18n.get('add_items_external_source') + ' (Not ready)' }}
</b-dropdown-item>
</b-dropdown>
</div>
<div class="header-item">
<b-dropdown class="show">
<button
class="button is-white"
slot="trigger">
<span>{{ $i18n.get('label_table_fields') }}</span>
<b-icon icon="menu-down"/>
</button>
<b-dropdown-item
v-for="(column, index) in tableFields"
:key="index"
class="control"
custom>
<b-checkbox
@input="onChangeDisplayedField($event, index)"
:value="column.display"
:native-value="column.display">
{{ column.name }}
</b-checkbox>
</b-dropdown-item>
</b-dropdown>
</div>
<div class="header-item">
<b-field>
<b-select
@input="onChangeOrderBy($event)"
:placeholder="$i18n.get('label_sorting')">
<option
v-for="field in tableFields"
v-if="
field.id === 'creation_date' ||
field.id === 'author_name' || (
field.id !== undefined &&
field.field_type_object.related_mapped_prop !== 'description' &&
field.field_type_object.primitive_type !== 'term' &&
field.field_type_object.primitive_type !== 'item' &&
field.field_type_object.primitive_type !== 'compound'
)"
:value="field"
:key="field.id">
{{ field.name }}
</option>
</b-select>
<button
class="button is-white is-small"
@click="onChangeOrder()">
<b-icon :icon="order === 'ASC' ? 'sort-ascending' : 'sort-descending'"/>
</button>
</b-field>
v-if="!isOnTheme"
class="tabs">
<ul>
<li
@click="onChangeTab('')"
:class="{ 'is-active': status == undefined || status == ''}"><a>{{ $i18n.get('label_all_items') }}</a></li>
<li
@click="onChangeTab('draft')"
:class="{ 'is-active': status == 'draft'}"><a>{{ $i18n.get('label_draft_items') }}</a></li>
<li
@click="onChangeTab('trash')"
:class="{ 'is-active': status == 'trash'}"><a>{{ $i18n.get('label_trash_items') }}</a></li>
</ul>
</div>
</span>
</template>
@ -88,14 +149,22 @@
name: 'SearchControl',
data() {
return {
prefTableFields: []
prefTableFields: [],
localTableFields: []
}
},
props: {
collectionId: Number,
isRepositoryLevel: false,
tableFields: Array,
isOnTheme: false
isOnTheme: false,
status: '',
hasResults: false ,
viewMode: 'table'
},
watch: {
tableFields() {
this.localTableFields = JSON.parse(JSON.stringify(this.tableFields));
}
},
computed: {
orderBy() {
@ -105,10 +174,14 @@
return this.getOrder();
}
},
mounted() {
this.localTableFields = JSON.parse(JSON.stringify(this.tableFields));
},
methods: {
...mapGetters('search', [
'getOrderBy',
'getOrder'
'getOrder',
'getStatus'
]),
onChangeOrderBy(field) {
this.$eventBusSearch.setOrderBy(field);
@ -116,32 +189,101 @@
onChangeOrder() {
this.order == 'DESC' ? this.$eventBusSearch.setOrder('ASC') : this.$eventBusSearch.setOrder('DESC');
},
onChangeDisplayedField(event, index) {
if (this.tableFields[index] != undefined) {
this.tableFields[index].display = event;
if (event)
this.$eventBusSearch.addFetchOnlyMeta(this.tableFields[index].id);
else
this.$eventBusSearch.removeFetchOnlyMeta(this.tableFields[index].id);
onChangeTab(status) {
this.$eventBusSearch.setStatus(status);
},
onChangeViewMode(event) {
this.$eventBusSearch.setViewMode(event);
},
onChangeDisplayedFields() {
let fetchOnlyFieldIds = [];
for (let i = 0; i < this.localTableFields.length; i++) {
this.tableFields[i].display = this.localTableFields[i].display;
if (this.tableFields[i].id != undefined) {
if (this.tableFields[i].display) {
fetchOnlyFieldIds.push(this.tableFields[i].id);
}
}
}
this.$eventBusSearch.addFetchOnly({
'0': 'thumbnail',
'meta': fetchOnlyFieldIds,
'1': 'creation_date',
'2': 'author_name'
});
this.$refs.displayedFieldsDropdown.toggle();
}
}
}
</script>
<style>
<style lang="scss">
@import '../../scss/_variables.scss';
.sub-header {
min-height: $subheader-height;
height: $subheader-height;
padding-top: $page-small-top-padding;
padding-left: $page-side-padding;
padding-right: $page-side-padding;
border-bottom: 0.5px solid #ddd;
display: flex;
justify-content: space-between;
@media screen and (max-width: 769px) {
height: 60px;
margin-top: 0;
.header-item {
padding-right: 0.5em;
}
}
}
.header-item {
display: inline-block;
.field {
align-items: center;
}
#item-creation-options-dropdown {
margin-right: 80px;
}
.dropdown-menu {
display: block;
div.dropdown-content {
padding: 0;
.metadata-options-container {
max-height: 240px;
overflow: auto;
}
.dropdown-item-apply {
width: 100%;
border-top: 1px solid #efefef;
padding: 8px 12px;
text-align: right;
}
.dropdown-item-apply .button {
width: 100%;
}
}
}
}
.header-item .field {
align-items: center;
}
#item-creation-options-dropdown {
margin-right: 80px;
}
.header-item .dropdown-menu {
display: block;
.tabs {
padding-top: 20px;
margin-bottom: 20px;
padding-left: $page-side-padding;
padding-right: $page-side-padding;
}
</style>

View File

@ -7,7 +7,6 @@ import Vue from 'vue';
import Buefy from 'buefy';
import VTooltip from 'v-tooltip'
// Custom elements
import Text from '../../classes/field-types/text/Text.vue';
import Textarea from '../../classes/field-types/textarea/Textarea.vue';

View File

@ -27,7 +27,7 @@ Vue.use(VueRouter);
const i18nGet = function (key) {
let string = tainacan_plugin.i18n[key];
return (string != undefined && string != null && string != '' ) ? string : "ERROR: Invalid i18n key!";
return (string !== undefined && string !== null && string !== '' ) ? string : "ERROR: Invalid i18n key!";
};
const routes = [
@ -44,15 +44,15 @@ const routes = [
{ path: 'items/:itemId/edit', name: 'ItemEditionForm', component: ItemEditionForm, meta: {title: i18nGet('title_edit_item'), icon: 'folder-multiple'} },
{ path: 'items/new', name: 'CollectionItemCreatePage', component: ItemEditionForm, meta: {title: i18nGet('title_create_item_collection'), icon: 'folder-multiple'} },
{ path: 'items/:itemId', name: 'ItemPage', component: ItemPage, meta: {title: i18nGet('title_item_page'), icon: 'folder-multiple'} },
{ path: 'edit', component: CollectionEditionForm, name: 'CollectionEditionForm', meta: {title: i18nGet('title_edit_collection'), icon: 'folder-multiple'} },
{ path: 'settings', component: CollectionEditionForm, name: 'CollectionEditionForm', meta: {title: i18nGet('title_collection_settings'), icon: 'folder-multiple'} },
{ path: 'fields', component: FieldsList, name: 'FieldsList', meta: {title: i18nGet('title_collection_fields_edition'), icon: 'folder-multiple'} },
{ path: 'filters', component: FiltersList, name: 'FiltersList', meta: {title: i18nGet('title_collection_filters_edition'), icon: 'folder-multiple'} },
{ path: 'events', component: EventsPage, name: 'CollectionEventsPage', meta: {title: i18nGet('title_collection_events'), icon: 'flash'} }
]
},
// { path: '/items', name: 'ItemsPage', component: ItemsPage, meta: {title: i18nGet('title_items_page'), icon: 'file-multiple'} },
// { path: '/items/new', name: 'ItemCreationForm', component: ItemEditionForm, meta: {title: i18nGet('title_create_item'), icon: 'file-multiple'} },
{ path: '/items', name: 'ItemsPage', component: ItemsPage, meta: {title: i18nGet('title_items_page'), icon: 'file-multiple'} },
{ path: '/items/new', name: 'ItemCreationForm', component: ItemEditionForm, meta: {title: i18nGet('title_create_item'), icon: 'file-multiple'} },
{ path: '/filters', name: 'FiltersPage', component: FiltersPage, meta: {title: i18nGet('title_repository_filters_page'), icon: 'filter'} },

View File

@ -1,6 +1,7 @@
// Main imports
import Vue from 'vue';
import Buefy from 'buefy';
import VTooltip from 'v-tooltip'
// Custom elements
import Text from '../../classes/field-types/text/Text.vue';
@ -28,6 +29,7 @@ import FilterCategorySelectbox from '../../classes/filter-types/category/Selectb
import TaincanFormItem from '../../classes/field-types/tainacan-form-item.vue';
import TaincanFiltersList from '../../classes/filter-types/tainacan-filter-item.vue';
import ItemsPage from '../pages/lists/items-page.vue';
import TableViewMode from '../../theme-helper/table-view-mode.vue';
// Remaining imports
import HelpButton from '../components/other/help-button.vue';
@ -39,6 +41,7 @@ import { I18NPlugin, UserPrefsPlugin, RouterHelperPlugin, ConsolePlugin } from '
// Configure and Register Plugins
Vue.use(Buefy);
Vue.use(VTooltip)
Vue.use(I18NPlugin);
Vue.use(UserPrefsPlugin);
Vue.use(RouterHelperPlugin);
@ -74,6 +77,8 @@ Vue.component('help-button', HelpButton);
Vue.component('draggable', draggable);
Vue.component('items-page', ItemsPage);
Vue.component('table-view-mode', TableViewMode);
Vue.use(eventBusSearch, { store: store, router: routerTheme});
import ThemeItemsList from '../theme-items-list.vue';
@ -83,12 +88,20 @@ new Vue({
store,
router: routerTheme,
data: {
collectionId: ''
collectionId: '',
defaultViewMode: '',
enabledViewModes: {}
},
render: h => h(ThemeItemsList),
beforeMount () {
if (this.$el.attributes['collection-id'] != undefined)
this.collectionId = this.$el.attributes['collection-id'].value;
if (this.$el.attributes['default-view-mode'] != undefined)
this.defaultViewMode = this.$el.attributes['default-view-mode'].value;
if (this.$el.attributes['enabled-view-modes'] != undefined)
this.enabledViewModes = this.$el.attributes['enabled-view-modes'].value.split(',');
}
});

View File

@ -178,7 +178,7 @@ RouterHelperPlugin.install = function (Vue, options = {}) {
return '/taxonomies/?' + qs.stringify(query);
},
getCategoryTermsPath(categoryId, query) {
return '/categoryId/' + categoryId + 'terms/?' + qs.stringify(query);
return '/categoryId/' + categoryId + '/terms/?' + qs.stringify(query);
},
getFiltersPath(query) {
return '/filters/?' + qs.stringify(query);
@ -233,7 +233,7 @@ RouterHelperPlugin.install = function (Vue, options = {}) {
getNewCategoryPath() {
return '/taxonomies/new';
},
getNewTermPath() {
getNewTermPath(categoryId) {
return '/taxonomies/' + categoryId + '/terms/new';
},
getNewEventPath() {
@ -241,7 +241,7 @@ RouterHelperPlugin.install = function (Vue, options = {}) {
},
// Edit
getCollectionEditPath(id) {
return '/collections/' + id + '/edit';
return '/collections/' + id + '/settings';
},
getItemEditPath(collectionId, itemId) {
return '/collections/' + collectionId + '/items/' + itemId + '/edit';

View File

@ -57,7 +57,8 @@
:total-collections="totalCollections"
:page="page"
:collections-per-page="collectionsPerPage"
:collections="collections"/>
:collections="collections"
:is-on-trash="status == 'trash'"/>
<!-- Empty state image -->
<div v-if="totalCollections <= 0 && !isLoading">
@ -197,18 +198,13 @@ export default {
}
},
computed: {
collections(){
let collectionsList = this.getCollections();
for (let collection of collectionsList)
collection['creation'] = this.$i18n.get('info_created_by') + collection['author_name'] + '<br>' + this.$i18n.get('info_date') + collection['creation_date'];
return collectionsList;
},
field_mappers: {
get() {
var data = this.getFieldMappers()
return data;
return this.getFieldMappers()
}
},
collections() {
return this.getCollections();
}
},
created() {

View File

@ -3,12 +3,15 @@
:class="{'primary-page': isRepositoryLevel}">
<!-- SEARCH AND FILTERS --------------------- -->
<!-- Filter menu compress button -->
<button
id="filter-menu-compress-button"
:class="{'filter-menu-compress-button-top-repo': isRepositoryLevel}"
:style="{ top: isHeaderShrinked ? '125px' : '152px'}"
@click="isFiltersMenuCompressed = !isFiltersMenuCompressed">
<b-icon :icon="isFiltersMenuCompressed ? 'menu-right' : 'menu-left'" />
</button>
<!-- Side bar with search and filters -->
<aside
v-show="!isFiltersMenuCompressed"
class="filters-menu">
@ -16,11 +19,11 @@
:is-full-page="false"
:active.sync="isLoadingFilters"/>
<b-field class="margin-1">
<b-field>
<div class="control is-small is-clearfix">
<input
class="input is-small"
:placeholder=" $i18n.get('instruction_search_collection') "
:placeholder="$i18n.get('instruction_search')"
type="search"
autocomplete="on"
:value="searchQuery"
@ -42,12 +45,11 @@
</b-field>
<!-- <a class="is-size-7 is-secondary is-pulled-right">Busca avançada</a> -->
<br>
<br>
<h3 class="has-text-weight-semibold">{{ $i18n.get('filters') }}</h3>
<a
v-if="!isLoadingFilters && filters.length > 0"
v-if="!isLoadingFilters &&
((filters.length >= 0 &&
isRepositoryLevel) || filters.length > 0)"
class="collapse-all is-size-7"
@click="collapseAll = !collapseAll">
{{ collapseAll ? $i18n.get('label_collapse_all') : $i18n.get('label_expand_all') }}
@ -61,9 +63,12 @@
<br>
<filters-items-list
v-if="!isLoadingFilters && filters.length > 0"
v-if="!isLoadingFilters &&
((filters.length >= 0 &&
isRepositoryLevel) || filters.length > 0)"
:filters="filters"
:collapsed="collapseAll"/>
:collapsed="collapseAll"
:is-repository-level="isRepositoryLevel"/>
<section
v-else
@ -86,23 +91,146 @@
</section>
</aside>
<!-- ITEMS LIST AREA (ASIDE THE ASIDE) ------------------------- -->
<div
id="items-list-area"
class="items-list-area"
:class="{ 'spaced-to-right': !isFiltersMenuCompressed }">
<b-loading
:is-full-page="false"
:active.sync="isLoadingItems"/>
<!-- SEARCH CONTROL ------------------------- -->
<div class="sub-header">
<div class="search-control">
<b-loading
:is-full-page="false"
:active.sync="isLoadingFields"/>
<search-control
v-if="fields.length > 0 && (items.length > 0 || isLoadingItems)"
:is-repository-level="isRepositoryLevel"
:collection-id="collectionId"
:table-fields="tableFields"
:pref-table-fields="prefTableFields"
:is-on-theme="isOnTheme"/>
<!-- Item Creation Dropdown, only on Admin -->
<div
class="search-control-item"
v-if="!isOnTheme">
<b-dropdown
:mobile-modal="false"
id="item-creation-options-dropdown">
<button
class="button is-secondary"
slot="trigger">
<span>{{ $i18n.getFrom('items','add_new') }}</span>
<b-icon icon="menu-down"/>
</button>
<b-dropdown-item>
<router-link
id="a-create-item"
tag="div"
:to="{ path: $routerHelper.getNewItemPath(collectionId) }">
{{ $i18n.get('add_one_item') }}
</router-link>
</b-dropdown-item>
<b-dropdown-item disabled>
{{ $i18n.get('add_items_bulk') + ' (Not ready)' }}
</b-dropdown-item>
<b-dropdown-item disabled>
{{ $i18n.get('add_items_external_source') + ' (Not ready)' }}
</b-dropdown-item>
</b-dropdown>
</div>
<!-- Displayed Fields Dropdown -->
<div
v-if="!isOnTheme || registeredViewModes[viewMode].dynamic_metadata"
class="search-control-item">
<b-dropdown
ref="displayedFieldsDropdown"
:mobile-modal="false"
:disabled="totalItems <= 0"
class="show">
<button
class="button is-white"
slot="trigger">
<span>{{ $i18n.get('label_table_fields') }}</span>
<b-icon icon="menu-down"/>
</button>
<div class="metadata-options-container">
<b-dropdown-item
v-for="(column, index) in localTableFields"
:key="index"
class="control"
custom>
<b-checkbox
v-model="column.display"
:native-value="column.display">
{{ column.name }}
</b-checkbox>
</b-dropdown-item>
</div>
<div class="dropdown-item-apply">
<button
@click="onChangeDisplayedFields()"
class="button is-success">
{{ $i18n.get('label_apply_changes') }}
</button>
</div>
</b-dropdown>
</div>
<!-- View Modes Dropdown -->
<div
v-if="isOnTheme"
class="search-control-item">
<b-field>
<b-dropdown
@change="onChangeViewMode($event)"
:mobile-modal="false">
<button
class="button is-white"
slot="trigger">
<span>{{ $i18n.get('label_view_mode') }}</span>
<b-icon icon="menu-down" />
</button>
<b-dropdown-item
v-for="(viewMode, index) of enabledViewModes"
:key="index"
:value="viewMode">
<span v-html="registeredViewModes[viewMode].icon" />
{{ registeredViewModes[viewMode].label }}
</b-dropdown-item>
</b-dropdown>
</b-field>
</div>
<!-- Change OrderBy Select and Order Button-->
<div class="search-control-item">
<b-field>
<b-select
:disabled="totalItems <= 0"
@input="onChangeOrderBy($event)"
:placeholder="$i18n.get('label_sorting')">
<option
v-for="field in tableFields"
v-if="
field.id === 'creation_date' ||
field.id === 'author_name' || (
field.id !== undefined &&
field.field_type_object.related_mapped_prop !== 'description' &&
field.field_type_object.primitive_type !== 'term' &&
field.field_type_object.primitive_type !== 'item' &&
field.field_type_object.primitive_type !== 'compound'
)"
:value="field"
:key="field.id">
{{ field.name }}
</option>
</b-select>
<button
:disabled="totalItems <= 0"
class="button is-white is-small"
@click="onChangeOrder()">
<b-icon :icon="order === 'ASC' ? 'sort-ascending' : 'sort-descending'"/>
</button>
</b-field>
</div>
</div>
<!-- STATUS TABS, only on Admin -------- -->
<div
v-if="!isOnTheme"
class="tabs">
@ -118,24 +246,43 @@
:class="{ 'is-active': status == 'trash'}"><a>{{ $i18n.get('label_trash_items') }}</a></li>
</ul>
</div>
<!-- <div
:items="items"
id="theme-items-list" /> -->
<!-- LISTING RESULTS ------------------------- -->
<div class="above-subheader">
<b-loading
:is-full-page="false"
:active.sync="isLoadingItems"/>
<!-- ITEMS LISTING RESULTS ------------------------- -->
<div class="above-search-control">
<!-- Admin Table -->
<items-list
v-if="!isLoadingItems && items.length > 0"
v-if="!isOnTheme &&
!isLoadingItems &&
totalItems > 0"
:collection-id="collectionId"
:table-fields="tableFields"
:items="items"
:is-loading="isLoading"
:is-on-theme="isOnTheme"/>
:is-loading="isLoadingItems"
:is-on-theme="isOnTheme"
:is-on-trash="status == 'trash'"/>
<!-- Theme View Modes -->
<div
v-if="isOnTheme &&
!isLoadingItems &&
registeredViewModes[viewMode] != undefined &&
registeredViewModes[viewMode].type == 'template'"
v-html="itemsListTemplate"/>
<component
v-if="isOnTheme &&
!isLoadingItems &&
registeredViewModes[viewMode] != undefined &&
registeredViewModes[viewMode].type == 'component'"
:collection-id="collectionId"
:table-fields="tableFields"
:items="items"
:is-loading="isLoadingItems"
:is="'table-view-mode'"/>
<!-- Empty Placeholder (only used in Admin) -->
<section
v-if="!isLoadingItems && items.length <= 0"
v-if="!isOnTheme && !isLoadingItems && totalItems <= 0"
class="section">
<div class="content has-text-grey has-text-centered">
<p>
@ -157,8 +304,9 @@
</router-link>
</div>
</section>
<!-- Pagination Footer -->
<pagination v-if="items.length > 0"/>
<!-- Pagination -->
<pagination v-if="totalItems > 0 && (!isOnTheme || registeredViewModes[viewMode].show_pagination)"/>
</div>
</div>
@ -166,11 +314,10 @@
</template>
<script>
import SearchControl from '../../components/search/search-control.vue'
import ItemsList from '../../components/lists/items-list.vue';
import FiltersItemsList from '../../components/search/filters-items-list.vue';
import Pagination from '../../components/search/pagination.vue'
import {mapActions, mapGetters} from 'vuex';
import { mapActions, mapGetters } from 'vuex';
export default {
name: 'ItemsPage',
@ -187,21 +334,62 @@
collapseAll: true,
isOnTheme: false,
futureSearchQuery: '',
isHeaderShrinked: false
isHeaderShrinked: false,
localTableFields: [],
registeredViewModes: tainacan_plugin.registered_view_modes
}
},
props: {
collectionId: Number
collectionId: Number,
defaultViewMode: String, // Used only on theme
enabledViewModes: Object // Used only on theme
},
computed: {
items() {
return this.getItems();
},
itemsListTemplate() {
return this.getItemsListTemplate();
},
totalItems() {
return this.getTotalItems();
},
filters() {
return this.getFilters();
},
fields() {
return this.getFields();
},
searchQuery() {
return this.getSearchQuery();
},
status() {
return this.getStatus();
},
viewMode() {
return this.getViewMode();
},
orderBy() {
return this.getOrderBy();
},
order() {
return this.getOrder();
}
},
components: {
SearchControl,
ItemsList,
FiltersItemsList,
Pagination
},
watch: {
tableFields() {
this.localTableFields = JSON.parse(JSON.stringify(this.tableFields));
}
},
methods: {
...mapGetters('collection', [
'getItems'
'getItems',
'getItemsListTemplate'
]),
...mapActions('fields', [
'fetchFields'
@ -217,14 +405,47 @@
]),
...mapGetters('search', [
'getSearchQuery',
'getStatus'
'getStatus',
'getOrderBy',
'getOrder',
'getViewMode',
'getTotalItems'
]),
updateSearch() {
this.$eventBusSearch.setSearchQuery(this.futureSearchQuery);
},
onChangeOrderBy(field) {
this.$eventBusSearch.setOrderBy(field);
},
onChangeOrder() {
this.order == 'DESC' ? this.$eventBusSearch.setOrder('ASC') : this.$eventBusSearch.setOrder('DESC');
},
onChangeTab(status) {
this.$eventBusSearch.setStatus(status);
},
onChangeViewMode(viewMode) {
this.$eventBusSearch.setViewMode(viewMode);
},
onChangeDisplayedFields() {
let fetchOnlyFieldIds = [];
for (let i = 0; i < this.localTableFields.length; i++) {
this.tableFields[i].display = this.localTableFields[i].display;
if (this.tableFields[i].id != undefined) {
if (this.tableFields[i].display) {
fetchOnlyFieldIds.push(this.tableFields[i].id);
}
}
}
this.$eventBusSearch.addFetchOnly({
'0': 'thumbnail',
'meta': fetchOnlyFieldIds,
'1': 'creation_date',
'2': 'author_name'
});
this.$refs.displayedFieldsDropdown.toggle();
},
prepareFieldsAndFilters() {
this.isLoadingFilters = true;
@ -255,13 +476,12 @@
slug: 'thumbnail',
id: undefined,
display: true
})
;
});
let fetchOnlyFieldIds = [];
for (let field of this.fields) {
if (field.display !== 'never') {
// Will be pushed on array
let display = true;
@ -289,7 +509,7 @@
field: 'row_creation',
field_type: undefined,
slug: 'creation_date',
id: 'creation_date',
id: undefined,
display: true
});
this.tableFields.push({
@ -297,7 +517,7 @@
field: 'row_author',
field_type: undefined,
slug: 'author_name',
id: 'author_name',
id: undefined,
display: true
});
@ -323,37 +543,11 @@
});
}
},
computed: {
items() {
return this.getItems();
},
filters() {
return this.getFilters();
},
fields() {
return this.getFields();
},
searchQuery() {
return this.getSearchQuery();
},
status() {
return this.getStatus();
}
},
created() {
/*
document.addEventListener('tainacan-items-change', () => {
var themeList = document.getElementById('theme-items-list');
var items = themeList.attributes.items.value;
var e = document.createElement('p');
e.innerHTML = items;
this.isOnTheme = (this.$route.name === null);
themeList.appendChild(e);
}); */
this.isOnTheme = (this.$route.name == null);
this.isRepositoryLevel = (this.collectionId == undefined);
this.isRepositoryLevel = (this.collectionId === undefined);
this.$eventBusSearch.setCollectionId(this.collectionId);
@ -365,17 +559,22 @@
this.hasFiltered = hasFiltered;
});
this.$eventBusSearch.$on('hasToPrepareFieldsAndFilters', () => {
this.prepareFieldsAndFilters();
this.$eventBusSearch.$on('hasToPrepareFieldsAndFilters', (to) => {
/* This condition is to prevent a incorrect fetch by filter or fields when we come from items
* at collection level to items page at repository level
*/
if(this.collectionId === to.params.collectionId) {
this.prepareFieldsAndFilters();
}
});
},
mounted() {
//this.$eventBusSearch.updateStoreFromURL();
//this.$eventBusSearch.loadItems();
this.prepareFieldsAndFilters();
this.localTableFields = JSON.parse(JSON.stringify(this.tableFields));
// Watch Scroll for shrinking header, only on Admin at collection level
if (!this.isRepositoryLevel && !this.isOnTheme) {
document.getElementById('items-list-area').addEventListener('scroll', ($event) => {
this.isHeaderShrinked = ($event.originalTarget.scrollTop > 53);
@ -390,63 +589,8 @@
@import '../../scss/_variables.scss';
.margin-1 {
margin-bottom: 0.1rem;
}
.page-container {
padding: 0px;
}
.sub-header {
min-height: $subheader-height;
height: $subheader-height;
padding-top: $page-small-top-padding;
padding-left: $page-side-padding;
padding-right: $page-side-padding;
border-bottom: 0.5px solid #ddd;
position: relative;
@media screen and (max-width: 769px) {
height: 60px;
margin-top: 0;
.header-item {
padding-right: 0.5em;
}
}
}
.tabs {
padding-top: 20px;
margin-bottom: 20px;
padding-left: $page-side-padding;
padding-right: $page-side-padding;
}
.above-subheader {
margin-bottom: 0;
margin-top: 0;
height: calc(100% - 184px);
}
.pagination-area {
margin-left: $page-side-padding;
margin-right: $page-side-padding;
}
.table-container {
padding-left: 8.333333%;
padding-right: 8.333333%;
//height: calc(100% - 82px);
}
#collection-search-button {
border-radius: 0px !important;
padding: 0px 8px !important;
border-color: $tainacan-input-background;
&:focus, &:active {
border-color: none !important;
}
padding: 0px;
}
.filters-menu {
@ -462,9 +606,20 @@
visibility: visible;
display: block;
transition: visibility ease 0.5s, display ease 0.5s;
margin-bottom: -0.1rem;
h3 {
font-size: 100%;
margin-top: 48px;
}
#collection-search-button {
border-radius: 0 !important;
padding: 0 8px !important;
border-color: $tainacan-input-background;
&:focus, &:active {
border-color: none !important;
}
}
.label {
@ -473,29 +628,21 @@
}
}
.items-list-area {
margin-left: 0;
transition: margin-left ease 0.5s;
height: 100%;
overflow: auto;
.filter-menu-compress-button-top-repo {
top: 123px !important;
}
.spaced-to-right {
margin-left: $filter-menu-width;
}
#filter-menu-compress-button {
position: absolute;
z-index: 9;
top: 152px;
left: 0px;
left: 0;
max-width: 23px;
height: 21px;
width: 23px;
border: none;
background-color: $primary-lighter;
color: $tertiary;
padding: 0px;
padding: 0;
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
cursor: pointer;
@ -506,6 +653,97 @@
}
}
.spaced-to-right {
margin-left: $filter-menu-width;
}
.search-control {
min-height: $subheader-height;
height: $subheader-height;
padding-top: $page-small-top-padding;
padding-left: $page-side-padding;
padding-right: $page-side-padding;
border-bottom: 0.5px solid #ddd;
display: flex;
justify-content: space-between;
@media screen and (max-width: 769px) {
height: 60px;
margin-top: 0;
.search-control-item {
padding-right: 0.5em;
}
}
}
.search-control-item {
display: inline-block;
.field {
align-items: center;
}
#item-creation-options-dropdown {
margin-right: 80px;
}
.dropdown-menu {
display: block;
div.dropdown-content {
padding: 0;
.metadata-options-container {
max-height: 240px;
overflow: auto;
}
.dropdown-item-apply {
width: 100%;
border-top: 1px solid #efefef;
padding: 8px 12px;
text-align: right;
}
.dropdown-item-apply .button {
width: 100%;
}
}
}
}
.above-search-control {
margin-bottom: 0;
margin-top: 0;
height: calc(100% - 184px);
}
.tabs {
padding-top: 20px;
margin-bottom: 20px;
padding-left: $page-side-padding;
padding-right: $page-side-padding;
}
.items-list-area {
margin-left: 0;
transition: margin-left ease 0.5s;
height: 100%;
overflow: auto;
position: relative;
}
.table-container {
padding-left: 8.333333%;
padding-right: 8.333333%;
//height: calc(100% - 82px);
}
.pagination-area {
margin-left: $page-side-padding;
margin-right: $page-side-padding;
}
</style>

View File

@ -32,6 +32,12 @@
&:focus {
outline: 0px;
}
&[disabled] {
border: none;
}
&.is-white[disabled] {
background-color: white !important;
}
}
.button.is-small {
height: 26px !important;

View File

@ -17,6 +17,9 @@
box-shadow: none !important;
text-decoration: none !important;
}
&[disabled] {
background-color: white !important;
}
}
&:not(.is-multiple)::after {
content: "\F35D" !important;

View File

@ -12,7 +12,7 @@
overflow: auto;
margin-bottom: 0px !important;
table.table {
table.tainacan-table {
width: 100%;
.checkbox-cell {
@ -126,6 +126,7 @@
.th-wrap {
font-size: 12px !important;
color: $gray-light;
font-weight: normal !important;
text-overflow: ellipsis;
overflow-x: hidden;

View File

@ -84,4 +84,10 @@
color: $primary;
}
}
.dropdown {
.button {
border: 1px solid $tainacan-input-background !important;
border-radius: 0 !important;
}
}
}

View File

@ -16,7 +16,7 @@
padding: 20px;
max-width: 280px;
max-height: 200px;
overflow: auto;
overflow-x: auto;
}
.tooltip-arrow {
width: 0;

View File

@ -21,6 +21,7 @@ return apply_filters('tainacan-admin-i18n', [
// Actions
'edit' => __( 'Edit', 'tainacan' ),
'settings' => __( 'Settings', 'tainacan' ),
'new' => __( 'New', 'tainacan' ),
'add' => __( 'Add', 'tainacan' ),
'import' => __( 'Import', 'tainacan' ),
@ -73,7 +74,7 @@ return apply_filters('tainacan-admin-i18n', [
'title_create_category_page' => __( 'Taxonomy Creation', 'tainacan' ),
'title_create_item_collection' => __( 'Create Item on Collection', 'tainacan' ),
'title_create_filter' => __( 'Filter Creation', 'tainacan' ),
'title_edit_collection' => __( 'Settings of Collection', 'tainacan' ),
'title_collection_settings' => __( 'Settings of Collection', 'tainacan' ),
'title_edit_item' => __( 'Edit Item', 'tainacan' ),
'title_category_edition_page' => __( 'Taxonomy Edition', 'tainacan' ),
'title_filter_edition' => __( 'Filter Edition', 'tainacan' ),
@ -155,7 +156,7 @@ return apply_filters('tainacan-admin-i18n', [
'label_term_without_name' => __( 'Term without name', 'tainacan' ),
'label_inherited' => __( 'Inherited', 'tainacan' ),
'label_sorting' => __( 'Sorting', 'tainacan' ),
'label_who_when' => __( 'Who and when', 'tainacan' ),
'label_event_date' => __( 'Event date', 'tainacan' ),
'label_event_title' => __( 'Event', 'tainacan' ),
'label_header_image' => __( 'Header Image', 'tainacan' ),
'label_empty_header_image' => __( 'Empty Header Image', 'tainacan' ),
@ -193,6 +194,10 @@ return apply_filters('tainacan-admin-i18n', [
'label_blank_collection' => __( 'Blank collection', 'tainacan' ),
'label_dublin_core' => __( 'Dublin Core', 'tainacan' ),
'label_created_by' => __( 'Created by', 'tainacan' ),
'label_apply_changes' => __( 'Apply changes', 'tainacan' ),
'label_view_mode' => __( 'View mode', 'tainacan' ),
'label_default_view_mode' => __( 'Default view mode', 'tainacan' ),
'label_enabled_view_modes' => __( 'Enabled view modes', 'tainacan' ),
// Instructions. More complex sentences to guide user and placeholders
'instruction_delete_selected_collections' => __( 'Delete selected collections', 'tainacan' ),
@ -213,8 +218,7 @@ return apply_filters('tainacan-admin-i18n', [
'instruction_select_document_file_for_item' => __( 'Select a document file for item', 'tainacan' ),
'instruction_insert_url' => __( 'Insert URL', 'tainacan' ),
'instruction_write_text' => __( 'Write Text', 'tainacan' ),
'instruction_search_repository' => __( 'Search on repository', 'tainacan' ),
'instruction_search_collection' => __( 'Search on collection', 'tainacan' ),
'instruction_search' => __( 'Search', 'tainacan' ),
// Info. Other feedback to user.
'info_name_is_required' => __( 'Name is required.', 'tainacan' ),
@ -237,11 +241,15 @@ return apply_filters('tainacan-admin-i18n', [
'info_collection_deleted' => __( 'Collection deleted.', 'tainacan' ),
'info_item_deleted' => __( 'Item deleted.', 'tainacan' ),
'info_category_deleted' => __( 'Taxonomy deleted', 'tainacan' ),
'info_warning_collection_delete' => __( 'Do you really want to delete this collection?', 'tainacan' ),
'info_warning_item_delete' => __( 'Do you really want to delete this item?', 'tainacan' ),
'info_warning_collection_delete' => __( 'Do you really want to permanently delete this collection?', 'tainacan' ),
'info_warning_collection_trash' => __( 'Do you really want to trash this collection?', 'tainacan' ),
'info_warning_item_delete' => __( 'Do you really want to permanently delete this item?', 'tainacan' ),
'info_warning_item_trash' => __( 'Do you really want to trash this item?', 'tainacan' ),
'info_warning_category_delete' => __( 'Do you really want to delete this taxonomy?', 'tainacan' ),
'info_warning_selected_collections_delete' => __( 'Do you really want to delete the selected collections?', 'tainacan' ),
'info_warning_selected_items_delete' => __( 'Do you really want to delete the selected items?', 'tainacan' ),
'info_warning_selected_collections_delete' => __( 'Do you really want to permanently delete the selected collections?', 'tainacan' ),
'info_warning_selected_collections_trash' => __( 'Do you really want to trash the selected collections?', 'tainacan' ),
'info_warning_selected_items_delete' => __( 'Do you really want to permanently delete the selected items?', 'tainacan' ),
'info_warning_selected_items_trash' => __( 'Do you really want to trash the selected items?', 'tainacan' ),
'info_warning_selected_categories_delete' => __( 'Do you really want to delete the selected categories?', 'tainacan' ),
'info_warning_collection_related' => __( 'The metadata Collection related is required', 'tainacan' ),
'info_warning_no_fields_found' => __( 'No metadata found in this collection', 'tainacan' ),

View File

@ -1,6 +1,8 @@
<template>
<items-page
class="theme-items-list"
class="theme-items-list"
:enabled-view-modes="$root.enabledViewModes"
:default-view-mode="$root.defaultViewMode"
:collection-id="$root.collectionId" />
</template>
@ -19,20 +21,7 @@ export default {
@import "../../node_modules/buefy/src/scss/components/_checkbox.scss";
@import "../../node_modules/buefy/src/scss/components/_radio.scss";
@import "../../node_modules/buefy/src/scss/components/_tag.scss";
/* Rules for sizing the icon. */
.material-icons.md-18 { font-size: 18px; }
.material-icons.md-24 { font-size: 24px; }
.material-icons.md-36 { font-size: 36px; }
.material-icons.md-48 { font-size: 48px; }
/* Rules for using icons as black on a light background. */
.material-icons.md-dark { color: rgba(0, 0, 0, 0.54); }
.material-icons.md-dark.md-inactive { color: rgba(0, 0, 0, 0.26); }
/* Rules for using icons as white on a dark background. */
.material-icons.md-light { color: rgba(255, 255, 255, 1); }
.material-icons.md-light.md-inactive { color: rgba(255, 255, 255, 0.3); }
@import "../../node_modules/buefy/src/scss/components/_loading.scss";
// Tainacan custom colors
$primary: #2cb4c1;
@ -65,8 +54,14 @@ export default {
$danger-invert: findColorInvert($danger);
$table-side-padding: 4.166666667%;
$filter-menu-width: 200px;
@import "../admin/scss/_tables.scss";
@import "../admin/scss/_tooltips.scss";
.theme-items-list {
position: relative;
display: flex;
a{ color: $secondary !important }
a:hover {
@ -385,234 +380,6 @@ export default {
}
}
// Tables
// Tables
.table-container {
padding: 0 $table-side-padding;
position: relative;
margin-bottom: 40px;
.table-wrapper {
width: 100%;
height: calc(100% - 40px);
border-collapse: separate;
overflow: auto;
margin-bottom: 0px !important;
table.table {
width: 100%;
.checkbox-cell {
min-width: 38px;
width: 38px;
padding: 0;
left: 0;
top: auto;
display: table-cell;
&::before {
box-shadow: inset 50px 0 10px -12px #222;
content: " ";
width: 50px;
height: 100%;
position: absolute;
left: 0;
top: 0;
visibility: hidden;
}
label.checkbox {
border-radius: 0px;
background-color: white;
padding: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
}
.b-checkbox.checkbox .control-label {
display: none;
}
&.is-selecting {
position: sticky !important;
position: -webkit-sticky !important;
&::before { visibility: visible !important; }
}
}
// Only to be used in case we can implement Column resizing
// th:not(:last-child) {
// border-right: 1px solid $tainacan-input-background !important;
// }
.thumbnail-cell {
width: 60px;
text-align: center;
}
.column-small-width {
min-width: 80px;
max-width: 80px;
p {
color: $gray-light;
font-size: 11px;
line-height: 1.5;
}
}
.column-default-width {
min-width: 80px;
max-width: 160px;
p {
color: $gray-light;
font-size: 11px;
line-height: 1.5;
}
}
.column-medium-width {
min-width: 120px;
max-width: 200px;
p {
color: $gray-light;
font-size: 11px;
line-height: 1.5;
}
}
.column-large-width {
min-width: 120px;
max-width: 240px;
p {
color: $gray-light;
font-size: 11px;
line-height: 1.5;
}
}
.column-main-content {
min-width: 120px !important;
max-width: 240px !important;
p {
font-size: 14px !important;
color: $tainacan-input-color !important;
margin: 0px !important;
}
}
.column-needed-width {
max-width: unset !important;
}
.column-align-right {
text-align: right !important;
}
th {
position: sticky;
position: -webkit-sticky;
background-color: white;
border-bottom: 1px solid $tainacan-input-background;
top: 0px;
z-index: 9;
padding: 10px;
vertical-align: bottom;
.th-wrap {
font-size: 12px !important;
font-weight: normal !important;
text-overflow: ellipsis;
overflow-x: hidden;
white-space: nowrap;
}
}
tbody {
tr {
cursor: pointer;
background-color: transparent;
&.selected-row {
background-color: $primary-lighter;
.checkbox-cell .checkbox, .actions-cell .actions-container {
background-color: $primary-lighter;
}
}
td {
height: 60px;
max-height: 60px;
padding: 10px;
vertical-align: middle;
line-height: 12px;
border: none !important;
p {
font-size: 14px;
margin: 0px;
text-overflow: ellipsis;
overflow-x: hidden;
white-space: nowrap;
}
}
img.table-thumb {
max-height: 38px !important;
border-radius: 3px;
}
td.actions-cell {
padding: 0px;
position: sticky !important;
position: -webkit-sticky !important;
right: 0px;
top: auto;
width: 80px;
.actions-container {
visibility: hidden;
display: flex;
position: relative;
padding: 0;
height: 100%;
width: 80px;
z-index: 9;
background-color: transparent;
float: right;
}
a {
margin: auto;
font-size: 18px !important;
}
}
&:hover {
background-color: $tainacan-input-background !important;
cursor: pointer;
.checkbox-cell {
position: sticky !important;
position: -webkit-sticky !important;
&::before { visibility: visible; }
.checkbox {
background-color: $tainacan-input-background !important;
}
}
.actions-cell {
.actions-container {
visibility: visible;
background: $tainacan-input-background !important;
}
&::after {
box-shadow: inset -97px 0 17px -21px #222;
content: " ";
width: 100px;
height: 100%;
position: absolute;
right: 0px;
top: 0;
}
}
}
}
}
}
}
}
.pagination-area {
display: flex;
justify-content: space-between;
@ -709,5 +476,18 @@ export default {
background-color: white;
}
#filter-menu-compress-button {
top: 70px !important;
}
.filters-menu {
height: auto;
min-width: $filter-menu-width;
}
#items-list-area {
width: 100%;
}
}
</style>

View File

@ -375,12 +375,8 @@ class REST_Controller extends \WP_REST_Controller {
$query_params['perpage'] = array(
'description' => __( "Maximum number of $object_name to be returned in result set." ),
'type' => 'integer',
'type' => 'numeric',
'default' => 10,
'minimum' => 1,
'maximum' => 100,
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
);
$query_params['paged'] = array(

View File

@ -110,6 +110,12 @@ class REST_Fields_Controller extends REST_Controller {
'permission_callback' => array($this, 'update_item_permissions_check'),
'args' => $this->get_endpoint_args_for_item_schema(\WP_REST_Server::EDITABLE)
),
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'get_item'),
'permission' => array($this, 'get_item_permissions_check'),
'args' => $this->get_endpoint_args_for_item_schema(\WP_REST_Server::READABLE)
)
)
);
}
@ -120,15 +126,24 @@ class REST_Fields_Controller extends REST_Controller {
* @return \WP_Error|\WP_REST_Response
*/
public function get_item( $request ) {
$collection_id = $request['collection_id'];
$collection_id = isset($request['collection_id']) ? $request['collection_id'] : false;
$field_id = $request['field_id'];
if($request['fetch'] === 'all_field_values' && $request['search']){
$results = $this->field_repository->fetch_all_field_values($collection_id, $field_id, $request['search']);
if($collection_id) {
$results = $this->field_repository->fetch_all_field_values( $collection_id, $field_id, $request['search'] );
} else {
$results = $this->field_repository->fetch_all_field_values( null, $field_id, $request['search']);
}
return new \WP_REST_Response($results, 200);
} elseif($request['fetch'] === 'all_field_values') {
$results = $this->field_repository->fetch_all_field_values($collection_id, $field_id);
if($collection_id) {
$results = $this->field_repository->fetch_all_field_values( $collection_id, $field_id );
} else {
$results = $this->field_repository->fetch_all_field_values( null, $field_id);
}
return new \WP_REST_Response($results, 200);
}
@ -145,14 +160,28 @@ class REST_Fields_Controller extends REST_Controller {
* @throws \Exception
*/
public function get_item_permissions_check( $request ) {
$collection = $this->collection_repository->fetch($request['collection_id']);
$collection_id = isset($request['collection_id']) ? $request['collection_id'] : false;
if($collection instanceof Entities\Collection) {
if ($request['context'] === 'edit' && ! $collection->can_read()) {
return false;
if($collection_id) {
$collection = $this->collection_repository->fetch( $collection_id );
if ( $collection instanceof Entities\Collection ) {
if ( $request['context'] === 'edit' && ! $collection->can_read() ) {
return false;
}
return true;
}
} elseif($request['field_id']) {
$field = $this->field_repository->fetch($request['field_id']);
return true;
if ( $field instanceof Entities\Field ) {
if ( $request['context'] === 'edit' && ! $field->can_read() ) {
return false;
}
return true;
}
}
return false;

View File

@ -116,7 +116,7 @@ class REST_Filters_Controller extends REST_Controller {
$received_type = $body['filter_type'];
if(empty($received_type)){
throw new \InvalidArgument\Exception('The type can\'t be empty');
throw new \InvalidArgumentException('The type can\'t be empty');
} elseif(!strrchr($received_type, '_')){
$received_type = ucfirst(strtolower($received_type));
} else {
@ -141,10 +141,20 @@ class REST_Filters_Controller extends REST_Controller {
$collection_id = $request['collection_id'];
$filter_obj->set_collection_id( $collection_id );
$filter_obj->set_field('');
if(!isset($body['field'])){
throw new \InvalidArgumentException('You need provide a field id');
}
$filter_obj->set_field($body['field']);
} else {
$filter_obj->set_collection_id( 'filter_in_repository' );
$filter_obj->set_field('');
if(!isset($body['field'])){
throw new \InvalidArgumentException('You need provide a field id');
}
$filter_obj->set_field($body['field']);
}
$filter_obj->set_filter_type($filter_type);

View File

@ -5,6 +5,7 @@ namespace Tainacan\API\EndPoints;
use \Tainacan\API\REST_Controller;
use Tainacan\Repositories;
use Tainacan\Entities;
use Tainacan\Tests\Collections;
/**
* Represents the Items REST Controller
@ -86,6 +87,17 @@ class REST_Items_Controller extends REST_Controller {
),
)
);
register_rest_route(
$this->namespace, '/' . $this->rest_base,
array(
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'get_items'),
'permission_callback' => array($this, 'get_items_permissions_check'),
'args' => $this->get_collection_params(),
),
)
);
}
/**
@ -199,24 +211,57 @@ class REST_Items_Controller extends REST_Controller {
public function get_items( $request ) {
$args = $this->prepare_filters($request);
$collection_id = $request['collection_id'];
$collection_id = [];
if($request['collection_id']) {
$collection_id = $request['collection_id'];
}
$items = $this->items_repository->fetch($args, $collection_id, 'WP_Query');
$response = [];
if ($items->have_posts()) {
while ( $items->have_posts() ) {
$items->the_post();
$item = new Entities\Item($items->post);
$return_template = false;
$prepared_item = $this->prepare_item_for_response($item, $request);
array_push($response, $prepared_item);
if ( isset($request['view_mode']) ) {
// TODO: Check if requested view mode is really enabled for current collection
$view_mode = \Tainacan\Theme_Helper::get_instance()->get_view_mode($request['view_mode']);
if ($view_mode && $view_mode['type'] == 'template' && isset($view_mode['template']) && file_exists($view_mode['template'])) {
$return_template = true;
}
wp_reset_postdata();
}
if ( $return_template ) {
ob_start();
global $wp_query;
$wp_query = $items;
$displayed_metadata = array_map(function($el) { return (int) $el; }, $request['fetch_only']['meta']);
include $view_mode['template'];
$response = ob_get_clean();
} else {
if ($items->have_posts()) {
while ( $items->have_posts() ) {
$items->the_post();
$item = new Entities\Item($items->post);
$prepared_item = $this->prepare_item_for_response($item, $request);
array_push($response, $prepared_item);
}
wp_reset_postdata();
}
}
$total_items = $items->found_posts;
$max_pages = ceil($total_items / (int) $items->query_vars['posts_per_page']);
@ -261,9 +306,13 @@ class REST_Items_Controller extends REST_Controller {
}
return true;
}
} else {
if('edit' === $request['context'] && !$this->collections_repository->can_read(new Entities\Collection())) {
return false;
}
return false;
return true;
}
}
/**

View File

@ -25,7 +25,8 @@ class Collection extends Entity {
$default_order,
$default_orderby,
$columns,
$default_view_mode,
$default_view_mode,
$enabled_view_modes,
$fields_order,
$filters_order,
$enable_cover_page,
@ -354,6 +355,15 @@ class Collection extends Entity {
return $this->get_mapped_property( 'default_view_mode' );
}
/**
* Get collection enabled_view_modes option
*
* @return string
*/
function get_enabled_view_modes() {
return $this->get_mapped_property( 'enabled_view_modes' );
}
/**
* Get collection fields ordination
*
@ -549,6 +559,17 @@ class Collection extends Entity {
$this->set_mapped_property( 'default_view_mode', $value );
}
/**
* Set collection enabled_view_modes option
*
* @param [array] $value
*
* @return void
*/
function set_enabled_view_modes( $value ) {
$this->set_mapped_property( 'enabled_view_modes', $value );
}
/**
* Set collection fields ordination
*

View File

@ -50,7 +50,14 @@
this.collection = ( this.collection_id ) ? this.collection_id : this.filter.collection_id;
this.field = ( this.field_id ) ? this.field_id : this.filter.field.field_id;
const vm = this;
axios.get('/collection/' + this.collection + '/fields/' + this.field )
let in_route = '/collection/' + this.collection + '/fields/' + this.field;
if(this.isRepositoryLevel){
in_route = '/fields/'+ this.field;
}
axios.get(in_route)
.then( res => {
let result = res.data;
if( result && result.field_type ){
@ -75,6 +82,9 @@
label: ''
}
},
props: {
isRepositoryLevel: Boolean,
},
mixins: [filter_type_mixin],
methods: {
setResults(option){
@ -101,7 +111,7 @@
promise = this.getValuesRelationship( collectionTarget );
} else {
promise = this.getValuesPlainText( this.field, query );
promise = this.getValuesPlainText( this.field, query, this.isRepositoryLevel );
}
promise.then( () => {

View File

@ -22,7 +22,13 @@
this.field = ( this.field_id ) ? this.field_id : this.filter.field.field_id ;
this.type = ( this.filter_type ) ? this.filter_type : this.filter.field.field_type;
axios.get('/collection/'+ this.collection +'/fields/' + this.field + '?context=edit')
let in_route = '/collection/' + this.isRepositoryLevel + '/fields/' + this.field +'?context=edit';
if(this.isRepositoryLevel){
in_route = '/fields?context=edit';
}
axios.get(in_route)
.then( res => {
let field = res.data;
this.selectedValues( field.field_type_options.taxonomy_id );
@ -50,7 +56,8 @@
id: '',
query: {
type: Object // concentrate all attributes field id and type
}
},
isRepositoryLevel: Boolean,
},
watch: {
selected( value ){

View File

@ -21,7 +21,14 @@
this.collection = ( this.collection_id ) ? this.collection_id : this.filter.collection_id;
this.field = ( this.field_id ) ? this.field_id : this.filter.field.field_id;
const vm = this;
axios.get('/collection/' + this.collection + '/fields/' + this.field + '?nopaging=1')
let in_route = '/collection/' + this.collection + '/fields/' + this.field +'?nopaging=1';
if(this.isRepositoryLevel){
in_route = '/fields?nopaging=1';
}
axios.get(in_route)
.then( res => {
let result = res.data;
if( result && result.field_type ){
@ -34,6 +41,9 @@
this.$console.log(error);
});
},
props: {
isRepositoryLevel: Boolean,
},
data(){
return {
isLoading: false,
@ -63,7 +73,7 @@
promise = this.getValuesRelationship( collectionTarget );
} else {
promise = this.getValuesPlainText( this.field );
promise = this.getValuesPlainText( this.field, null, this.isRepositoryLevel );
}
promise.then(() => {

View File

@ -66,7 +66,13 @@
this.collection = ( this.collection_id ) ? this.collection_id : this.filter.collection_id;
this.field = ( this.field_id ) ? this.field_id : this.filter.field.field_id;
axios.get('/collection/' + this.collection + '/fields/' + this.field )
let in_route = '/collection/' + this.collection + '/fields/' + this.field;
if(this.isRepositoryLevel){
in_route = '/fields/'+ this.field;
}
axios.get(in_route)
.then( res => {
let result = res.data;
if( result && result.field_type ){
@ -101,7 +107,8 @@
field_id: [Number], // not required, but overrides the filter field id if is set
collection_id: [Number], // not required, but overrides the filter field id if is set
id: '',
query: Object
query: Object,
isRepositoryLevel: Boolean,
},
watch: {
isTouched( val ){

View File

@ -12,9 +12,14 @@ export const filter_type_mixin = {
query: {}
},
methods: {
getValuesPlainText(fieldId, search) {
getValuesPlainText(fieldId, search, isRepositoryLevel) {
let url = '/collection/' + this.collection + '/fields/' + fieldId + '?fetch=all_field_values&nopaging=1';
if(isRepositoryLevel){
url = '/fields/' + fieldId + '?fetch=all_field_values&nopaging=1';
}
if( search ){
url += "&search=" + search;
}
@ -46,7 +51,7 @@ export const filter_type_mixin = {
.then(res => {
if (res.data.length > 0) {
for (let item of res.data) {
this.options.push({label: item.title, value: item.id, img: item.thumbnail });
this.options.push({label: item.title, value: item.id, img: item.thumbnail.thumb });
}
}
})

View File

@ -27,7 +27,14 @@
this.collection = ( this.collection_id ) ? this.collection_id : this.filter.collection_id;
this.field = ( this.field_id ) ? this.field_id : this.filter.field.field_id;
const vm = this;
axios.get('/collection/' + this.collection + '/fields/' + this.field )
let in_route = '/collection/' + this.collection + '/fields/' + this.field;
if(this.isRepositoryLevel){
in_route = '/fields/'+ this.field;
}
axios.get(in_route)
.then( res => {
let result = res.data;
if( result && result.field_type ){
@ -40,6 +47,9 @@
this.$console.error(error);
});
},
props: {
isRepositoryLevel: Boolean,
},
data(){
return {
isLoading: false,
@ -68,7 +78,7 @@
this.isLoading = true;
let promise = null;
promise = this.getValuesPlainText( this.field );
promise = this.getValuesPlainText( this.field, null, this.isRepositoryLevel );
promise.then(() => {
this.isLoading = false;

View File

@ -23,7 +23,14 @@
this.collection = ( this.collection_id ) ? this.collection_id : this.filter.collection_id;
this.field = ( this.field_id ) ? this.field_id : this.filter.field.field_id;
const vm = this;
axios.get('/collection/' + this.collection + '/fields/' + this.field )
let in_route = '/collection/' + this.collection + '/fields/' + this.field;
if(this.isRepositoryLevel){
in_route = '/fields?nopaging=1';
}
axios.get(in_route)
.then( res => {
let result = res.data;
if( result && result.field_type ){
@ -48,6 +55,9 @@
field_object: {}
}
},
props: {
isRepositoryLevel: Boolean
},
mixins: [filter_type_mixin],
watch: {
selected( value ){
@ -77,7 +87,7 @@
promise = this.getValuesRelationship( collectionTarget, query );
} else {
promise = this.getValuesPlainText( this.field, query );
promise = this.getValuesPlainText( this.field, query, this.isRepositoryLevel );
}
this.isLoading = true;
promise.then(() => {

View File

@ -1,6 +1,6 @@
<template>
<b-field
id="filter-item-forms"
class="filter-item-forms"
:message="getErrorMessage"
:type="filterTypeMessage">
<b-collapse
@ -22,6 +22,7 @@
:is="filter.filter_type_object.component"
:filter="filter"
:query="query"
:is-repository-level="isRepositoryLevel"
@input="listen( $event )"/>
</div>
</b-collapse>
@ -36,6 +37,7 @@
props: {
filter: Object,
query: Object,
isRepositoryLevel: Boolean,
open: true,
},
data(){
@ -76,7 +78,7 @@
<style lang="scss">
@import "../../../src/admin/scss/_variables.scss";
#filter-item-forms {
.filter-item-forms {
.datepicker {
@ -250,13 +252,7 @@
.tag {
height: 2em !important;
}
.autocomplete {
.dropdown-content {
position: fixed !important;
}
}
.b-checkbox.checkbox {
font-weight: normal;
font-size: 12px;

View File

@ -127,6 +127,15 @@ class Collections extends Repository {
'title' => __( 'Default view mode', 'tainacan' ),
'type' => 'string',
'description' => __( 'Collection default visualization mode', 'tainacan' ),
'default' => 'table',
//'validation' => v::stringType(),
],
'enabled_view_modes' => [
'map' => 'meta',
'title' => __( 'Enabled view modes', 'tainacan' ),
'type' => 'array',
'description' => __( 'Which visualization modes will be available for the public to choose from', 'tainacan' ),
'default' => [],
//'validation' => v::stringType(),
],
'fields_order' => [
@ -272,7 +281,7 @@ class Collections extends Repository {
* @return mixed|Collection
*/
public function delete( $args ) {
if ( ! empty( $args[1] ) && $args[1] === true ) {
if ( ! empty( $args[1] ) && $args[1] == true ) {
$deleted = new Entities\Collection( wp_delete_post( $args[0], $args[1] ) );
if($deleted) {

View File

@ -642,8 +642,11 @@ class Fields extends Repository {
}
}
# TODO: Fetch all field value for repository level
/**
* Fetch all values of a field from a collection in all it collection items
* Fetch all values of a field from a collection or repository
*
* @param $collection_id
* @param $field_id
@ -722,8 +725,9 @@ class Fields extends Repository {
foreach ($post_statuses as $post_status) {
$sql_string = $wpdb->prepare(
"SELECT item_id, field_id, mvalue
if($collection_id) {
$sql_string = $wpdb->prepare(
"SELECT item_id, field_id, mvalue
FROM (
SELECT ID as item_id
FROM $wpdb->posts
@ -734,8 +738,24 @@ class Fields extends Repository {
FROM $wpdb->postmeta $search_query
) metas
ON items.item_id = metas.post_id AND metas.field_id = %d",
$item_post_type, $post_status, $field_id
);
$item_post_type, $post_status, $field_id
);
} else {
$sql_string = $wpdb->prepare(
"SELECT item_id, field_id, mvalue
FROM (
SELECT ID as item_id
FROM $wpdb->posts
WHERE post_status = %s
) items
JOIN (
SELECT meta_key as field_id, meta_value as mvalue, post_id
FROM $wpdb->postmeta $search_query
) metas
ON items.item_id = metas.post_id AND metas.field_id = %d",
$post_status, $field_id
);
}
$pre_result = $wpdb->get_results( $sql_string, ARRAY_A );
if (!empty($pre_result)) {
@ -750,8 +770,10 @@ class Fields extends Repository {
$post_statuses = get_post_stati( $args, 'names', 'and' );
foreach ($post_statuses as $post_status) {
$sql_string = $wpdb->prepare(
"SELECT item_id, field_id, mvalue
if($collection_id) {
$sql_string = $wpdb->prepare(
"SELECT item_id, field_id, mvalue
FROM (
SELECT ID as item_id
FROM $wpdb->posts
@ -762,8 +784,24 @@ class Fields extends Repository {
FROM $wpdb->postmeta $search_query
) metas
ON items.item_id = metas.post_id AND metas.field_id = %d",
$item_post_type, $post_status, $field_id
);
$item_post_type, $post_status, $field_id
);
} else {
$sql_string = $wpdb->prepare(
"SELECT item_id, field_id, mvalue
FROM (
SELECT ID as item_id
FROM $wpdb->posts
WHERE post_status = %s
) items
JOIN (
SELECT meta_key as field_id, meta_value as mvalue, post_id
FROM $wpdb->postmeta $search_query
) metas
ON items.item_id = metas.post_id AND metas.field_id = %d",
$post_status, $field_id
);
}
$pre_result = $wpdb->get_results( $sql_string, ARRAY_A );

View File

@ -185,7 +185,7 @@ class Filters extends Repository {
*
*/
public function delete($args){
if(!empty($args[1]) && $args[1] === true){
if(!empty($args[1]) && $args[1] == true){
$deleted = new Entities\Filter(wp_delete_post($args[0], $args[1]));

View File

@ -289,7 +289,7 @@ class Items extends Repository {
* @return mixed|Entities\Item
*/
public function delete( $args ) {
if ( ! empty( $args[1] ) && $args[1] === true ) {
if ( ! empty( $args[1] ) && $args[1] == true ) {
$deleted = new Entities\Item( wp_delete_post( $args[0], $args[1] ) );

View File

@ -186,7 +186,7 @@ class Taxonomies extends Repository {
$taxonomy_name = $args[1];
$permanently = $args[2];
if($permanently === true){
if($permanently == true){
$unregistered = unregister_taxonomy($taxonomy_name);
if($unregistered instanceof \WP_Error){

View File

@ -26,8 +26,8 @@ export default {
});
},
watch: {
'$route' () {
if (this.$route.params.collectionId)
'$route' (to, from) {
if (this.$route.params.collectionId)
this.collectionId = parseInt(this.$route.params.collectionId);
if (this.$route.name == null || this.$route.name == undefined || this.$route.name == 'CollectionItemsPage' || this.$route.name == 'ItemsPage') {
@ -40,9 +40,9 @@ export default {
if (this.$route.query.orderby == undefined)
this.$route.query.orderby = 'date';
this.$store.dispatch('search/set_postquery', this.$route.query);
this.$store.dispatch('search/set_postquery', this.$route.query);
this.loadItems();
this.loadItems(to);
}
}
@ -108,6 +108,10 @@ export default {
this.$store.dispatch('search/setSearchQuery', searchQuery);
this.updateURLQueries();
},
setViewMode(viewMode) {
this.$store.dispatch('search/setViewMode', viewMode);
this.updateURLQueries();
},
updateURLQueries() {
this.$router.push({ query: {}});
this.$router.push({ query: this.$store.getters['search/getPostQuery'] });
@ -115,18 +119,20 @@ export default {
updateStoreFromURL() {
this.$store.dispatch('search/set_postquery', this.$route.query);
},
loadItems() {
loadItems(to) {
// Foreces fetch_only to be filled before any search happens
// Forces fetch_only to be filled before any search happens
if (this.$store.getters['search/getFetchOnly'] == undefined)
this.$emit( 'hasToPrepareFieldsAndFilters');
this.$emit( 'hasToPrepareFieldsAndFilters', to);
else {
this.$emit( 'isLoadingItems', true);
this.$store.dispatch('collection/fetchItems', this.collectionId).then((res) => {
this.$store.dispatch('collection/fetchItems',
{ 'collectionId': this.collectionId,
'isOnTheme': (this.$route.name == null)
})
.then((res) => {
this.$emit( 'isLoadingItems', false);
this.$emit( 'hasFiltered', res.hasFiltered);
//var event = new Event('tainacan-items-change')
//document.dispatchEvent(event);
})
.catch(() => {
this.$emit( 'isLoadingItems', false);

View File

@ -1,7 +1,7 @@
import axios from '../../../axios/axios';
import qs from 'qs';
export const fetchItems = ({ rootGetters, dispatch, commit }, collectionId) => {
export const fetchItems = ({ rootGetters, dispatch, commit }, { collectionId, isOnTheme }) => {
commit('cleanItems');
return new Promise ((resolve, reject) => {
@ -14,27 +14,42 @@ export const fetchItems = ({ rootGetters, dispatch, commit }, collectionId) => {
hasFiltered = true;
// Differentiates between repository level and collection level queries
let endpoint = '/collection/'+collectionId+'/items?context=edit&'
let endpoint = '/collection/'+collectionId+'/items?'
if (collectionId == undefined)
endpoint = '/items?context=edit&'
endpoint = '/items?'
if (!isOnTheme)
endpoint = endpoint + 'context=edit&'
axios.tainacan.get(endpoint + qs.stringify(postQueries) )
.then(res => {
let items = res.data;
commit('setItems', items );
let viewModeObject = tainacan_plugin.registered_view_modes[postQueries.view_mode];
if (isOnTheme && viewModeObject != undefined && viewModeObject.type == 'template') {
commit('setItemsListTemplate', items );
resolve({'itemsListTemplate': items, 'total': res.headers['x-wp-total'], hasFiltered: hasFiltered});
} else {
commit('setItems', items );
resolve({'items': items, 'total': res.headers['x-wp-total'], hasFiltered: hasFiltered});
}
dispatch('search/setTotalItems', res.headers['x-wp-total'], { root: true } );
resolve({'items': items, 'total': res.headers['x-wp-total'], hasFiltered: hasFiltered});
})
})
.catch(error => reject(error));
});
}
export const deleteItem = ({ commit }, item_id ) => {
export const deleteItem = ({ commit }, { itemId, isPermanently }) => {
return new Promise((resolve, reject) => {
axios.tainacan.delete('/items/' + item_id)
let endpoint = '/items/' + itemId;
if (isPermanently)
endpoint = endpoint + '?permanently=1'
axios.tainacan.delete(endpoint)
.then( res => {
commit('deleteItem', { id: item_id });
commit('deleteItem', { id: itemId });
resolve( res );
}).catch((error) => {
reject( error );
@ -94,9 +109,13 @@ export const fetchCollectionName = ({ commit }, id) => {
});
}
export const deleteCollection = ({ commit }, id) => {
return new Promise((resolve, reject) =>{
axios.tainacan.delete('/collections/' + id)
export const deleteCollection = ({ commit }, { collectionId, isPermanently }) => {
return new Promise((resolve, reject) => {
let endpoint = '/collections/' + collectionId;
if (isPermanently)
endpoint = endpoint + '?permanently=true'
axios.tainacan.delete(endpoint)
.then(res => {
let collection = res.data;
commit('deleteCollection', collection);
@ -108,7 +127,18 @@ export const deleteCollection = ({ commit }, id) => {
});
}
export const updateCollection = ({ commit }, { collection_id, name, description, slug, status, enable_cover_page, cover_page_id, moderators_ids, parent }) => {
export const updateCollection = ({ commit }, {
collection_id,
name,
description,
slug,
status,
enable_cover_page,
cover_page_id,
moderators_ids,
parent,
enabled_view_modes
}) => {
return new Promise((resolve, reject) => {
axios.tainacan.patch('/collections/' + collection_id, {
name: name,
@ -118,7 +148,8 @@ export const updateCollection = ({ commit }, { collection_id, name, description,
cover_page_id: "" + cover_page_id,
enable_cover_page: enable_cover_page,
moderators_ids: moderators_ids,
parent: parent
parent: parent,
enabled_view_modes: enabled_view_modes
}).then( res => {
commit('setCollection', {
id: collection_id,
@ -129,7 +160,8 @@ export const updateCollection = ({ commit }, { collection_id, name, description,
enable_cover_page: enable_cover_page,
cover_page_id: cover_page_id,
moderators_ids: moderators_ids,
parent: parent
parent: parent,
enabled_view_modes: enabled_view_modes
});
resolve( res.data );
}).catch( error => {

View File

@ -2,6 +2,10 @@ export const getItems = state => {
return state.items;
}
export const getItemsListTemplate = state => {
return state.itemsListTemplate;
}
export const getCollections = state => {
return state.collections;
}

View File

@ -4,6 +4,7 @@ import * as mutations from './mutations';
const state = {
items: [],
itemsListTemplate: '',
collections: [],
collection: null,
collectionName: '',

View File

@ -3,6 +3,9 @@ import Vue from 'vue';
export const setItems = ( state, items ) => {
state.items = items;
}
export const setItemsListTemplate = ( state, items ) => {
state.itemsListTemplate = items;
}
export const cleanItems = (state) => {
state.items = [];

View File

@ -9,7 +9,7 @@ export const fetchFilters = ({ commit }, {collectionId, isRepositoryLevel, isCon
else
endpoint = '/filters/';
endpoint += '?nopaging=1'
endpoint += '?nopaging=1';
if (isContextEdit) {
endpoint += '&context=edit';
@ -39,12 +39,14 @@ export const sendFilter = ( { commit }, { collectionId, fieldId, name, filterTyp
endpoint = '/collection/' + collectionId + '/field/' + fieldId +'/filters/';
else
endpoint = '/filters/';
axios.tainacan.post(endpoint + '?context=edit', {
filter_type: filterType,
filter: {
name: name,
status: status
}
},
field: fieldId,
})
.then( res => {
let filter = res.data;

View File

@ -95,4 +95,9 @@ export const setOrder = ({ commit }, order ) => {
// Set search query
export const setSearchQuery = ({ commit }, searchQuery ) => {
commit('setSearchQuery', searchQuery );
};
// Set ViewMode (view_mode)
export const setViewMode = ({ commit }, viewMode ) => {
commit('setViewMode', viewMode );
};

View File

@ -44,6 +44,10 @@ export const getStatus = state => {
return state.postquery.status;
}
export const getViewMode = state => {
return state.postquery.view_mode;
}
export const getFetchOnly = state => {
return state.postquery.fetch_only;
}

View File

@ -19,6 +19,7 @@ const state = {
'1': 'creation_date',
'2': 'author_name'
},
view_mode: 'table'
},
totalItems: 0
};

View File

@ -111,5 +111,9 @@ export const setSearchQuery = ( state, searchQuery ) => {
};
export const setStatus = ( state, status ) => {
state.status = status;
state.postquery.status = status;
};
export const setViewMode = ( state, viewMode ) => {
state.postquery.view_mode = viewMode;
};

View File

@ -1,5 +1,5 @@
=== Tainacan ===
Contributors: fabianobn, jacsonp, leogermani, weryques
Contributors: fabianobn, jacsonp, leogermani, weryques, wetah
Tags: museums, libraries, archives, GLAM, collections, repository
Requires at least: 4.8
Tested up to: 4.9

View File

@ -5,6 +5,9 @@ Plugin URI: https://tainacan.org/new
Description: powerfull and flexible repository platform for WordPress. Manage and publish you digital collections as easily as publishing a post to your blog, while having all the tools of a professional respository platform.
Author: Media Lab / UFG
Version: 0.1
Text Domain: tainacan
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-3.0.html
*/
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );

View File

@ -10,6 +10,11 @@ class Theme_Helper {
public $visiting_collection_cover = false;
/**
* Stores view modes available to be used by the theme
*/
private $registered_view_modes = [];
public static function get_instance() {
if ( ! isset( self::$instance ) ) {
self::$instance = new self();
@ -43,6 +48,13 @@ class Theme_Helper {
add_filter('get_the_archive_title', array($this, 'filter_archive_title'));
add_shortcode( 'tainacan-search', array($this, 'search_shortcode'));
$this->register_view_mode('table', [
'label' => __('Table', 'tainacan'),
'dynamic_metadata' => true,
'icon' => '<span class="icon"><i class="mdi mdi-table mdi-24px"></i></span>',
'type' => 'component',
]);
}
@ -265,6 +277,78 @@ class Theme_Helper {
}
/**
* Register a new View Mode
*
* View Modes are used to display items in the faceted search when browsing a collection using
* the current active theme. It can be a php/html template or a web component.
*
* Collection managers can choose from registered view modes which will be the default mode and what others modes will be available
* for the visitors to choose from for each collection
*
* @param string $slug a unique slug for the view mode
* @param array|string $args {
* Optional. Array of arguments
*
* @type string $label Label, visible to users. Default to $slug
* @type string $description Description, visible only to editors in the admin. Default none.
* @type string $type Type. Accepted values are 'template' or 'component'. Defautl 'template'
* @type string $template Full path to the template file to be used. Required if $type is set to template.
* Default: theme-path/tainacan/view-mode-{$slug}.php
* @type string $component Component tag name. The web component js must be included and must accept two props:
* * items - the list of items to be rendered
* * displayed_metadata - list of metadata to be displayed
* Default view-mode-{$slug}
* @type string $thumbnail Full URL to an thumbnail that represents the view mode. Displayed in admin.
* @type string $icon HTML that outputs an icon that represents the view mode. Displayed in front end.
* @type bool $show_pagination Wether to display or not pagination controls. Default true.
* @type bool $dynamic_metadata Wether to display or not (and use or not) the "displayed metadata" selector. Default false.
*
*
* }
*
* @return void
*/
public function register_view_mode($slug, $args = []) {
$defaults = array(
'label' => $slug,
'description' => '',
'type' => 'template',
'template' => get_stylesheet_directory() . '/tainacan/view-mode-' . $slug . '.php',
'component' => 'view-mode-' . $slug,
'thumbnail' => '', // get_stylesheet_directory() . '/tainacan/view-mode-' . $slug . '.png',
'icon' => '', //
'show_pagination' => true,
'dynamic_metadata' => false,
);
$args = wp_parse_args($args, $defaults);
$this->registered_view_modes[$slug] = $args;
}
/**
* Get a list of all registered view modes
*
* @return array The list of registered view modes
*/
public function get_registered_view_modes() {
return $this->registered_view_modes;
}
/**
* Get one specific view mode by its slug
*
* @param string $slug The view mode slug
*
* @return array|false The view mode definition or false if it is not found
*/
public function get_view_mode($slug) {
return isset($this->registered_view_modes[$slug]) ? $this->registered_view_modes[$slug] : false;
}
}

View File

@ -0,0 +1,123 @@
<template>
<div class="table-container">
<div class="table-wrapper">
<table class="tainacan-table">
<thead>
<tr>
<!-- Displayed Fields -->
<th
v-for="(column, index) in tableFields"
:key="index"
v-if="column.display"
class="column-default-width"
:class="{
'thumbnail-cell': column.field == 'row_thumbnail',
'column-small-width' : column.field_type_object != undefined ? (column.field_type_object.className == 'Tainacan\\Field_Types\\Date' || column.field_type_object.className == 'Tainacan\\Field_Types\\Numeric') : false,
'column-medium-width' : column.field_type_object != undefined ? (column.field_type_object.className == 'Tainacan\\Field_Types\\Selectbox' || column.field_type_object.className == 'Tainacan\\Field_Types\\Category' || column.field_type_object.className == 'Tainacan\\Field_Types\\Compound') : false,
'column-large-width' : column.field_type_object != undefined ? (column.field_type_object.className == 'Tainacan\\Field_Types\\Textarea') : false,
}"
:custom-key="column.slug">
<div class="th-wrap">{{ column.name }}</div>
</th>
</tr>
</thead>
<tbody>
<tr
:key="index"
v-for="(item, index) of items">
<!-- Item Displayed Metadata -->
<td
:key="index"
v-for="(column, index) in tableFields"
v-if="column.display"
:label="column.name"
:aria-label="(column.field != 'row_thumbnail' && column.field != 'row_creation' && column.field != 'row_author')
? column.name + '' + (item.metadata ? item.metadata[column.slug].value_as_string : '') : ''"
class="column-default-width"
:class="{
'thumbnail-cell': column.field == 'row_thumbnail',
'column-main-content' : column.field_type_object != undefined ? (column.field_type_object.related_mapped_prop == 'title') : false,
'column-needed-width column-align-right' : column.field_type_object != undefined ? (column.field_type_object.className == 'Tainacan\\Field_Types\\Numeric') : false,
'column-small-width' : column.field_type_object != undefined ? (column.field_type_object.className == 'Tainacan\\Field_Types\\Date' || column.field_type_object.className == 'Tainacan\\Field_Types\\Numeric') : false,
'column-medium-width' : column.field_type_object != undefined ? (column.field_type_object.className == 'Tainacan\\Field_Types\\Selectbox' || column.field_type_object.className == 'Tainacan\\Field_Types\\Category' || column.field_type_object.className == 'Tainacan\\Field_Types\\Compound') : false,
'column-large-width' : column.field_type_object != undefined ? (column.field_type_object.className == 'Tainacan\\Field_Types\\Textarea') : false,
}"
@click="goToItemPage(item)">
<!-- <data-and-tooltip
v-if="column.field !== 'row_thumbnail' &&
column.field !== 'row_actions' &&
column.field !== 'row_creation'"
:data="renderMetadata( item.metadata[column.slug] )"/> -->
<p
v-tooltip="{
content: renderMetadata( item.metadata[column.slug] ),
html: true,
autoHide: false,
placement: 'auto-start'
}"
v-if="item.metadata != undefined &&
column.field !== 'row_thumbnail' &&
column.field !== 'row_actions' &&
column.field !== 'row_creation' &&
column.field !== 'row_author'"
v-html="renderMetadata( item.metadata[column.slug] )"/>
<span v-if="column.field == 'row_thumbnail'">
<img
class="table-thumb"
:src="item[column.slug].thumb">
</span>
<p
v-tooltip="{
content: item[column.slug],
html: true,
autoHide: false,
placement: 'auto-start'
}"
v-if="column.field == 'row_author' || column.field == 'row_creation'">
{{ item[column.slug] }}
</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
export default {
name: 'TableViewMode',
props: {
collectionId: Number,
tableFields: Array,
items: Array,
isLoading: false
},
methods: {
goToItemPage(item) {
window.location.href = item.url;
},
renderMetadata(metadata) {
if (!metadata) {
return '';
} else if (metadata.date_i18n) {
return metadata.date_i18n;
} else {
return metadata.value_as_html;
}
}
}
}
</script>
<style>
</style>

View File

@ -152,6 +152,26 @@ function tainacan_the_collection_description() {
echo tainacan_get_the_collection_description();
}
/**
* Outputs the div used by Vue to render the faceted search
*/
function tainacan_the_faceted_search() {
$props = ' ';
// if in a collection page
$collection_id = tainacan_get_collection_id();
if ($collection_id) {
$props .= 'collection-id="' . $collection_id . '" ';
$collection = new \Tainacan\Entities\Collection($collection_id);
$props .= 'default-view-mode="' . $collection->get_default_view_mode() . '" ';
$props .= 'enabled-view-modes="' . implode(',', $collection->get_enabled_view_modes()) . '" ';
}
echo '<div id="tainacan-items-page" ', $props, '></div>';
}
/**
* When visiting a term archive, returns the current term object
*
@ -162,4 +182,11 @@ function tainacan_get_term() {
return get_queried_object();
}
return false;
}
/**
* @see \Tainacan\Theme_Helper->register_view_mode()
*/
function tainacan_register_view_mode($slug, $args = []) {
\Tainacan\Theme_Helper::get_instance()->register_view_mode($slug, $args);
}

View File

@ -257,13 +257,46 @@ class TAINACAN_REST_Terms_Controller extends TAINACAN_UnitApiTestCase {
}
public function test_create_and_fetch_filter_in_repository(){
$collection = $this->tainacan_entity_factory->create_entity(
'collection',
array(
'name' => 'Collection filtered',
'description' => 'Is filtered',
),
true
);
$field = $this->tainacan_entity_factory->create_entity(
'field',
array(
'name' => 'Field filtered',
'description' => 'Is filtered',
'collection_id' => $collection->get_id(),
'field_type' => 'Tainacan\Field_Types\Text'
),
true
);
$field2 = $this->tainacan_entity_factory->create_entity(
'field',
array(
'name' => 'Field filtered',
'description' => 'Is filtered',
'collection_id' => 'default',
'field_type' => 'Tainacan\Field_Types\Text'
),
true
);
$filter_attr = json_encode([
'filter_type' => 'autocomplete',
'filter' => [
'name' => '2x Filter',
'description' => 'Description of 2x Filter',
'status' => 'publish'
]
],
'field' => $field2->get_id()
]);
$filter_attr2 = json_encode([
@ -272,7 +305,8 @@ class TAINACAN_REST_Terms_Controller extends TAINACAN_UnitApiTestCase {
'name' => '4x Filter',
'description' => 'Description of 4x Filter',
'status' => 'publish'
]
],
'field' => $field->get_id()
]);
#### CREATE A FILTER IN REPOSITORY ####