Merge branch 'develop' into feature/566

This commit is contained in:
mateuswetah 2022-09-01 11:04:18 -03:00
commit e159dc64ae
482 changed files with 130259 additions and 94188 deletions

View File

@ -25,15 +25,18 @@ module.exports = {
'vue/no-confusing-v-for-v-if': 'off',
'vue/no-use-v-if-with-v-for': 'off',
'vue/multi-word-component-names': 'off',
'vue/require-default-prop': 'off'
'vue/require-default-prop': 'off',
'vue/no-v-text-v-html-on-component': 'off'
},
globals: {
'wp': true,
'tainacan_plugin': true,
'tainacan_blocks': true,
'_': true,
'jQuery': true,
'tainacan_extra_components': true,
'tainacan_extra_plugins': true,
'grecaptcha': true
'grecaptcha': true,
'webkit': true
}
}

View File

@ -93,7 +93,7 @@ mkdir $wp_plugin_dir
rsync -axz --exclude='vendor/bin/phpc*' --exclude='vendor/squizlabs' --exclude='vendor/wimg' \
--exclude='vendor/respect/validation/.git' --exclude='vendor/symfony/polyfill-mbstring/.git' \
--exclude='vendor/respect/validation/docs' --exclude='vendor/respect/validation/tests' \
--exclude='pdf-viewer/pdfjs-dist/web/compressed.tracemonkey-pldi-09.pdf' \
--exclude='views/libs/pdf-viewer/pdfjs-dist/web/compressed.tracemonkey-pldi-09.pdf' \
--exclude='vendor/tecnickcom/tcpdf/fonts' \
--exclude='vendor/smalot/pdfparser/src/Smalot/PdfParser/Tests/' \
--exclude='vendor/tecnickcom/tcpdf/examples' \

View File

@ -14,37 +14,39 @@ sass -E 'UTF-8' --cache-location .tmp/sass-cache-2 src/views/roles/tainacan-role
sass -E 'UTF-8' --cache-location .tmp/sass-cache-3 src/views/reports/tainacan-reports.scss:src/assets/css/tainacan-reports.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-4 src/views/gutenberg-blocks/blocks/item-gallery/style.scss:src/assets/css/tainacan-gutenberg-block-item-gallery.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-4 src/views/mobile-app/tainacan-mobile-app.scss:src/assets/css/tainacan-mobile-app.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-5 src/views/gutenberg-blocks/blocks/collections-list/style.scss:src/assets/css/tainacan-gutenberg-block-collections-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-5 src/views/gutenberg-blocks/blocks/item-gallery/style.scss:src/assets/css/tainacan-gutenberg-block-item-gallery.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-6 src/views/gutenberg-blocks/blocks/carousel-collections-list/style.scss:src/assets/css/tainacan-gutenberg-block-carousel-collections-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-6 src/views/gutenberg-blocks/blocks/collections-list/style.scss:src/assets/css/tainacan-gutenberg-block-collections-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-7 src/views/gutenberg-blocks/blocks/items-list/style.scss:src/assets/css/tainacan-gutenberg-block-items-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-7 src/views/gutenberg-blocks/blocks/carousel-collections-list/style.scss:src/assets/css/tainacan-gutenberg-block-carousel-collections-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-8 src/views/gutenberg-blocks/blocks/dynamic-items-list/style.scss:src/assets/css/tainacan-gutenberg-block-dynamic-items-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-8 src/views/gutenberg-blocks/blocks/items-list/style.scss:src/assets/css/tainacan-gutenberg-block-items-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-9 src/views/gutenberg-blocks/blocks/search-bar/style.scss:src/assets/css/tainacan-gutenberg-block-search-bar.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-9 src/views/gutenberg-blocks/blocks/dynamic-items-list/style.scss:src/assets/css/tainacan-gutenberg-block-dynamic-items-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-10 src/views/gutenberg-blocks/blocks/carousel-items-list/style.scss:src/assets/css/tainacan-gutenberg-block-carousel-items-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-10 src/views/gutenberg-blocks/blocks/search-bar/style.scss:src/assets/css/tainacan-gutenberg-block-search-bar.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-12 src/views/gutenberg-blocks/blocks/carousel-items-list/style.scss:src/assets/css/tainacan-gutenberg-block-carousel-items-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-11 src/views/gutenberg-blocks/blocks/carousel-items-list/style.scss:src/assets/css/tainacan-gutenberg-block-carousel-items-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-12 src/views/gutenberg-blocks/blocks/terms-list/style.scss:src/assets/css/tainacan-gutenberg-block-terms-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-13 src/views/gutenberg-blocks/blocks/carousel-items-list/style.scss:src/assets/css/tainacan-gutenberg-block-carousel-items-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-13 src/views/gutenberg-blocks/blocks/facets-list/style.scss:src/assets/css/tainacan-gutenberg-block-facets-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-14 src/views/gutenberg-blocks/blocks/terms-list/style.scss:src/assets/css/tainacan-gutenberg-block-terms-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-14 src/views/gutenberg-blocks/blocks/carousel-terms-list/style.scss:src/assets/css/tainacan-gutenberg-block-carousel-terms-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-15 src/views/gutenberg-blocks/blocks/facets-list/style.scss:src/assets/css/tainacan-gutenberg-block-facets-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-15 src/views/gutenberg-blocks/blocks/faceted-search/style.scss:src/assets/css/tainacan-gutenberg-block-faceted-search.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-16 src/views/gutenberg-blocks/blocks/carousel-terms-list/style.scss:src/assets/css/tainacan-gutenberg-block-carousel-terms-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-16 src/views/gutenberg-blocks/blocks/item-submission-form/style.scss:src/assets/css/tainacan-gutenberg-block-item-submission-form.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-17 src/views/gutenberg-blocks/blocks/faceted-search/style.scss:src/assets/css/tainacan-gutenberg-block-faceted-search.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-17 src/views/gutenberg-blocks/blocks/related-items-list/style.scss:src/assets/css/tainacan-gutenberg-block-related-items-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-18 src/views/gutenberg-blocks/blocks/item-submission-form/style.scss:src/assets/css/tainacan-gutenberg-block-item-submission-form.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-18 src/views/gutenberg-blocks/scss/gutenberg-blocks-editor-style.scss:src/assets/css/tainacan-gutenberg-block-common-editor-styles.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-19 src/views/gutenberg-blocks/blocks/related-items-list/style.scss:src/assets/css/tainacan-gutenberg-block-related-items-list.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-19 src/views/gutenberg-blocks/scss/gutenberg-blocks-theme-style.scss:src/assets/css/tainacan-gutenberg-block-common-theme-styles.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-20 src/views/gutenberg-blocks/scss/gutenberg-blocks-editor-style.scss:src/assets/css/tainacan-gutenberg-block-common-editor-styles.css
sass -E 'UTF-8' --cache-location .tmp/sass-cache-21 src/views/gutenberg-blocks/scss/gutenberg-blocks-theme-style.scss:src/assets/css/tainacan-gutenberg-block-common-theme-styles.css
echo "Compilação do Sass Concluído!"
exit 0

View File

@ -1,6 +0,0 @@
{
"projectId": "tubzok",
"videoRecording": false,
"viewportWidth": 1280,
"viewportHeight": 720
}

1
docs/README.md Normal file
View File

@ -0,0 +1 @@
You can find all of Tainacan documentation in our Wiki: [https://wiki.tainacan.org](https://wiki.tainacan.org)

13064
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,58 +8,59 @@
"build-prod": "cross-env NODE_ENV=production webpack --config webpack.prod.js --progress --mode production"
},
"dependencies": {
"apexcharts": "^3.34.0",
"axios": "^0.21.4",
"apexcharts": "^3.35.5",
"axios": "^0.27.2",
"blurhash": "^1.1.5",
"buefy": "^0.9.19",
"bulma": "^0.9.3",
"buefy": "^0.9.21",
"bulma": "^0.9.4",
"conditioner-core": "^2.3.3",
"countup.js": "^2.1.0",
"css-vars-ponyfill": "^2.4.7",
"floating-vue": "^1.0.0-beta.15",
"moment": "^2.29.1",
"countup.js": "^2.3.2",
"css-vars-ponyfill": "^2.4.8",
"floating-vue": "^1.0.0-beta.18",
"masonry-layout": "^4.2.2",
"moment": "^2.29.4",
"node-sass": "^7.0.1",
"photoswipe": "^5.2.2",
"qs": "^6.10.3",
"photoswipe": "^5.3.0",
"qs": "^6.10.5",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"swiper": "^8.0.7",
"swiper": "^8.3.2",
"t": "^0.5.1",
"vue": "^2.6.14",
"vue-apexcharts": "^1.6.2",
"vue-blurhash": "^0.1.4",
"vue-countup-v2": "^4.0.0",
"vue-masonry-css": "^1.0.3",
"vue-router": "^3.1.6",
"vue-router": "^3.5.4",
"vue-the-mask": "^0.11.1",
"vuedraggable": "^2.24.3",
"vuex": "^3.4.0"
"vuex": "^3.6.2"
},
"devDependencies": {
"@babel/core": "^7.17.8",
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7",
"acorn": "^8.7.0",
"autoprefixer": "^10.4.4",
"babel-loader": "^8.2.4",
"@babel/core": "^7.18.5",
"@babel/preset-env": "^7.18.2",
"@babel/preset-react": "^7.17.12",
"@types/masonry-layout": "^4.2.5",
"acorn": "^8.7.1",
"autoprefixer": "^10.4.7",
"babel-loader": "^8.2.5",
"circular-dependency-plugin": "5.2.2",
"cross-env": "^7.0.3",
"css-loader": "^6.7.1",
"eslint": "^8.12.0",
"eslint-plugin-vue": "^8.5.0",
"eslint": "^8.18.0",
"eslint-plugin-vue": "^9.1.1",
"eslint-webpack-plugin": "^3.1.1",
"file-loader": "^6.2.0",
"moment-locales-webpack-plugin": "^1.2.0",
"postcss-loader": "^6.2.1",
"sass-loader": "^12.6.0",
"postcss-loader": "7.0.0",
"sass-loader": "^13.0.0",
"style-loader": "^3.3.1",
"terser-webpack-plugin": "5.3.1",
"terser-webpack-plugin": "5.3.3",
"vue-loader": "^15.9.8",
"vue-template-compiler": "^2.6.14",
"webpack": "^5.70.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.7.4",
"webpack-merge": "^5.8.0",
"circular-dependency-plugin": "5.2.2"
"webpack": "^5.73.0",
"webpack-bundle-analyzer": "^4.6.1",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.9.2",
"webpack-merge": "^5.8.0"
}
}

View File

@ -136,7 +136,8 @@
.wp-block-tainacan-carousel-collections-list .tainacan-carousel .swiper a,
.wp-block-tainacan-carousel-collections-list .tainacan-carousel .swiper a:hover {
color: inherit;
text-decoration: none; }
text-decoration: none;
display: block; }
.wp-block-tainacan-carousel-collections-list .tainacan-carousel .swiper .swiper-slide-duplicate img {
display: initial !important; }
.wp-block-tainacan-carousel-collections-list .tainacan-carousel .swiper .swiper-slide.collection-list-item-grid a {

File diff suppressed because one or more lines are too long

View File

@ -143,7 +143,8 @@
.wp-block-tainacan-carousel-items-list .tainacan-carousel .swiper a,
.wp-block-tainacan-carousel-items-list .tainacan-carousel .swiper a:hover {
color: inherit;
text-decoration: none; }
text-decoration: none;
display: block; }
.wp-block-tainacan-carousel-items-list .tainacan-carousel .swiper .is-forced-square > a > div {
padding-bottom: 100% !important; }
.wp-block-tainacan-carousel-items-list .tainacan-carousel .swiper .is-forced-square > a > div img {

File diff suppressed because one or more lines are too long

View File

@ -136,7 +136,8 @@
.wp-block-tainacan-carousel-terms-list .tainacan-carousel .swiper a,
.wp-block-tainacan-carousel-terms-list .tainacan-carousel .swiper a:hover {
color: inherit;
text-decoration: none; }
text-decoration: none;
display: block; }
.wp-block-tainacan-carousel-terms-list .tainacan-carousel .swiper .swiper-slide-duplicate img {
display: initial !important; }
.wp-block-tainacan-carousel-terms-list .tainacan-carousel .swiper .swiper-slide.term-list-item-grid a {

File diff suppressed because one or more lines are too long

View File

@ -47,7 +47,8 @@
color: inherit;
border: none;
font-weight: bold;
line-height: normal; }
line-height: normal;
display: block; }
.wp-block-tainacan-collections-list ul.collections-list.collections-layout-grid li.collection-list-item img,
.wp-block-tainacan-collections-list ul.collections-list-edit.collections-layout-grid li.collection-list-item img {
height: auto;

View File

@ -1,6 +1,6 @@
{
"version": 3,
"mappings": "AAEA,mCAAoC;EAChC,MAAM,EAAE,QAAQ;EAGhB,uDAAoB;IAChB,QAAQ,EAAE,QAAQ;IAClB,KAAK,EAAE,CAAC;EAIZ;gGAC2D;IACvD,OAAO,EAAE,IAAI;IACb,UAAU,EAAE,MAAM;IAClB,OAAO,EAAE,CAAC;EAId;8FACyD;IACrD,qBAAqB,EAAE,wBAAwB;IAC/C,eAAe,EAAE,iBAAiB;IAClC,kBAAkB,EAAE,eAAe;IAEnC;mGAAG;MACC,UAAU,EAAE,YAAY;MACxB,YAAY,EAAE,YAAY;MAC1B,WAAW,EAAE,YAAY;MACzB,MAAM,EAAE,gBAAgB;MAExB;yGAAI;QACA,MAAM,EAAE,gBAAgB;QACxB,aAAa,EAAE,cAAc;EAIzC;sFACiD;IAC7C,OAAO,EAAE,CAAC;ICtCd,SAAS,EAAE,IAAI;IACf,OAAO,EAAE,IAAI;IACb,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,IAAI;IDqCT,gBAAgB,EAAE,6BAA6B;IAC/C,qBAAqB,EAAE,wBAAwB;IAC/C,QAAQ,EAAE,GAAG;IACb,eAAe,EAAE,YAAY;IAC7B,eAAe,EAAE,IAAI;IAErB;gHAAwB;MACpB,QAAQ,EAAE,QAAQ;MAClB,OAAO,EAAE,KAAK;MACd,MAAM,EAAE,mBAAmB;MAC3B,aAAa,EAAE,IAAI;MACnB,KAAK,EAAE,KAAK;MAEZ;oHAAE;QACE,KAAK,EAAE,OAAO;QACd,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,IAAI;QACjB,WAAW,EAAE,MAAM;MAGvB;sHAAI;QACA,MAAM,EAAE,IAAI;QACZ,KAAK,EAAE,KAAK;QACZ,SAAS,EAAE,KAAK;QAChB,OAAO,EAAE,GAAG;QACZ,aAAa,EAAE,KAAK;MAGxB;iJAA+B;QAC3B,OAAO,EAAE,IAAI;MAGjB;;0HACU;QACN,KAAK,EAAE,OAAO;QACd,eAAe,EAAE,IAAI;EAIjC,oFAAiD;IAC7C,OAAO,EAAE,IAAI;IACb,WAAW,EAAE,UAAU;IAEvB,2FAAO;MACH,QAAQ,EAAE,mBAAmB;MAC7B,gBAAgB,EAAE,yBAAyB;MAC3C,KAAK,EAAE,oCAAmC;MAC1C,OAAO,EAAE,GAAG;MACZ,WAAW,EAAE,GAAG;MAChB,SAAS,EAAE,IAAI;MACf,UAAU,EAAE,MAAM;MAClB,QAAQ,EAAE,QAAQ;MAClB,OAAO,EAAE,CAAC;MACV,KAAK,EAAE,KAAK;MACZ,GAAG,EAAE,GAAG;MACR,eAAe,EAAE,MAAM;MACvB,OAAO,EAAE,GAAG;MAEZ,qGAAU;QAAE,MAAM,EAAE,GAAG;IAG3B,iGAAe;MACX,MAAM,EAAE,IAAI;MACZ,UAAU,EAAE,OAAO;MACnB,gBAAgB,EAAE,gBAAiC;MACnD,OAAO,EAAE,CAAC;MACV,KAAK,EAAE,IAAI;MACX,GAAG,EAAE,IAAI;MACT,MAAM,EAAE,8CAA6C;MACrD,aAAa,EAAE,IAAI;MACnB,UAAU,EAAE,wCAAwC;IAExD,uGAAqB;MACjB,gBAAgB,EAAE,gBAAiC;MACnD,MAAM,EAAE,yDAAwD;EAGxE,yCAA0C;IAEtC;wFACiD;MAC7C,qBAAqB,EAAE,uBAAuB;MAE9C;kHAAwB;QACpB,KAAK,EAAE,IAAI;QACX;wHAAI;UAAE,KAAK,EAAE,IAAI;EAM7B;sFACiD;IAC7C,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,IAAI;IACb,SAAS,EAAE,IAAI;IACf,WAAW,EAAE,MAAM;IACnB,eAAe,EAAE,IAAI;IAErB;gHAAwB;MACpB,QAAQ,EAAE,QAAQ;MAClB,MAAM,EAAE,mBAAmB;MAC3B,aAAa,EAAE,IAAI;MACnB,UAAU,EAAE,IAAI;MAChB,SAAS,EAAE,gBAAgB;MAC3B,KAAK,EAAE,gBAAgB;MAEvB;oHAAE;QACE,KAAK,EAAE,OAAO;QACd,MAAM,EAAE,IAAI;QACZ,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,MAAM;QACnB,UAAU,EAAE,SAAS;QACrB,UAAU,EAAE,UAAU;MAG1B;sHAAI;QACA,MAAM,EAAE,IAAI;QACZ,KAAK,EAAE,IAAI;QACX,SAAS,EAAE,IAAI;QACf,OAAO,EAAE,GAAG;QACZ,YAAY,EAAE,IAAI;MAItB;iJAA+B;QAC3B,OAAO,EAAE,IAAI;MAGjB;;0HACU;QACN,KAAK,EAAE,OAAO;QACd,eAAe,EAAE,IAAI;MAGzB,0CAA2C;QApC/C;oHAAwB;UAqChB,SAAS,EAAE,gBAAgB;UAC3B,KAAK,EAAE,gBAAgB;MAG3B,0CAA2C;QAzC/C;oHAAwB;UA0ChB,SAAS,EAAE,oBAAoB;UAC/B,KAAK,EAAE,oBAAoB;MAG/B,yCAA0C;QA9C9C;oHAAwB;UA+ChB,SAAS,EAAE,gBAAgB;UAC3B,KAAK,EAAE,gBAAgB;MAG3B,yCAA0C;QAnD9C;oHAAwB;UAoDhB,SAAS,EAAE,iBAAiB;UAC5B,KAAK,EAAE,iBAAiB;;AAOhC;iIACa;EACT,KAAK,EAAE,OAAO",
"mappings": "AAEA,mCAAoC;EAChC,MAAM,EAAE,QAAQ;EAGhB,uDAAoB;IAChB,QAAQ,EAAE,QAAQ;IAClB,KAAK,EAAE,CAAC;EAIZ;gGAC2D;IACvD,OAAO,EAAE,IAAI;IACb,UAAU,EAAE,MAAM;IAClB,OAAO,EAAE,CAAC;EAId;8FACyD;IACrD,qBAAqB,EAAE,wBAAwB;IAC/C,eAAe,EAAE,iBAAiB;IAClC,kBAAkB,EAAE,eAAe;IAEnC;mGAAG;MACC,UAAU,EAAE,YAAY;MACxB,YAAY,EAAE,YAAY;MAC1B,WAAW,EAAE,YAAY;MACzB,MAAM,EAAE,gBAAgB;MAExB;yGAAI;QACA,MAAM,EAAE,gBAAgB;QACxB,aAAa,EAAE,cAAc;EAIzC;sFACiD;IAC7C,OAAO,EAAE,CAAC;ICtCd,SAAS,EAAE,IAAI;IACf,OAAO,EAAE,IAAI;IACb,OAAO,EAAE,QAAQ;IACjB,OAAO,EAAE,IAAI;IDqCT,gBAAgB,EAAE,6BAA6B;IAC/C,qBAAqB,EAAE,wBAAwB;IAC/C,QAAQ,EAAE,GAAG;IACb,eAAe,EAAE,YAAY;IAC7B,eAAe,EAAE,IAAI;IAErB;gHAAwB;MACpB,QAAQ,EAAE,QAAQ;MAClB,OAAO,EAAE,KAAK;MACd,MAAM,EAAE,mBAAmB;MAC3B,aAAa,EAAE,IAAI;MACnB,KAAK,EAAE,KAAK;MAEZ;oHAAE;QACE,KAAK,EAAE,OAAO;QACd,MAAM,EAAE,IAAI;QACZ,WAAW,EAAE,IAAI;QACjB,WAAW,EAAE,MAAM;QACnB,OAAO,EAAE,KAAK;MAGlB;sHAAI;QACA,MAAM,EAAE,IAAI;QACZ,KAAK,EAAE,KAAK;QACZ,SAAS,EAAE,KAAK;QAChB,OAAO,EAAE,GAAG;QACZ,aAAa,EAAE,KAAK;MAGxB;iJAA+B;QAC3B,OAAO,EAAE,IAAI;MAGjB;;0HACU;QACN,KAAK,EAAE,OAAO;QACd,eAAe,EAAE,IAAI;EAIjC,oFAAiD;IAC7C,OAAO,EAAE,IAAI;IACb,WAAW,EAAE,UAAU;IAEvB,2FAAO;MACH,QAAQ,EAAE,mBAAmB;MAC7B,gBAAgB,EAAE,yBAAyB;MAC3C,KAAK,EAAE,oCAAmC;MAC1C,OAAO,EAAE,GAAG;MACZ,WAAW,EAAE,GAAG;MAChB,SAAS,EAAE,IAAI;MACf,UAAU,EAAE,MAAM;MAClB,QAAQ,EAAE,QAAQ;MAClB,OAAO,EAAE,CAAC;MACV,KAAK,EAAE,KAAK;MACZ,GAAG,EAAE,GAAG;MACR,eAAe,EAAE,MAAM;MACvB,OAAO,EAAE,GAAG;MAEZ,qGAAU;QAAE,MAAM,EAAE,GAAG;IAG3B,iGAAe;MACX,MAAM,EAAE,IAAI;MACZ,UAAU,EAAE,OAAO;MACnB,gBAAgB,EAAE,gBAAiC;MACnD,OAAO,EAAE,CAAC;MACV,KAAK,EAAE,IAAI;MACX,GAAG,EAAE,IAAI;MACT,MAAM,EAAE,8CAA6C;MACrD,aAAa,EAAE,IAAI;MACnB,UAAU,EAAE,wCAAwC;IAExD,uGAAqB;MACjB,gBAAgB,EAAE,gBAAiC;MACnD,MAAM,EAAE,yDAAwD;EAGxE,yCAA0C;IAEtC;wFACiD;MAC7C,qBAAqB,EAAE,uBAAuB;MAE9C;kHAAwB;QACpB,KAAK,EAAE,IAAI;QACX;wHAAI;UAAE,KAAK,EAAE,IAAI;EAM7B;sFACiD;IAC7C,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,IAAI;IACb,SAAS,EAAE,IAAI;IACf,WAAW,EAAE,MAAM;IACnB,eAAe,EAAE,IAAI;IAErB;gHAAwB;MACpB,QAAQ,EAAE,QAAQ;MAClB,MAAM,EAAE,mBAAmB;MAC3B,aAAa,EAAE,IAAI;MACnB,UAAU,EAAE,IAAI;MAChB,SAAS,EAAE,gBAAgB;MAC3B,KAAK,EAAE,gBAAgB;MAEvB;oHAAE;QACE,KAAK,EAAE,OAAO;QACd,MAAM,EAAE,IAAI;QACZ,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,MAAM;QACnB,UAAU,EAAE,SAAS;QACrB,UAAU,EAAE,UAAU;MAG1B;sHAAI;QACA,MAAM,EAAE,IAAI;QACZ,KAAK,EAAE,IAAI;QACX,SAAS,EAAE,IAAI;QACf,OAAO,EAAE,GAAG;QACZ,YAAY,EAAE,IAAI;MAItB;iJAA+B;QAC3B,OAAO,EAAE,IAAI;MAGjB;;0HACU;QACN,KAAK,EAAE,OAAO;QACd,eAAe,EAAE,IAAI;MAGzB,0CAA2C;QApC/C;oHAAwB;UAqChB,SAAS,EAAE,gBAAgB;UAC3B,KAAK,EAAE,gBAAgB;MAG3B,0CAA2C;QAzC/C;oHAAwB;UA0ChB,SAAS,EAAE,oBAAoB;UAC/B,KAAK,EAAE,oBAAoB;MAG/B,yCAA0C;QA9C9C;oHAAwB;UA+ChB,SAAS,EAAE,gBAAgB;UAC3B,KAAK,EAAE,gBAAgB;MAG3B,yCAA0C;QAnD9C;oHAAwB;UAoDhB,SAAS,EAAE,iBAAiB;UAC5B,KAAK,EAAE,iBAAiB;;AAOhC;iIACa;EACT,KAAK,EAAE,OAAO",
"sources": ["../../views/gutenberg-blocks/blocks/collections-list/style.scss","../../views/gutenberg-blocks/scss/gutenberg-blocks-variables.scss"],
"names": [],
"file": "tainacan-gutenberg-block-collections-list.css"

View File

@ -129,7 +129,8 @@
align-items: center;
border-bottom: 1px solid var(--tainacan-block-gray3, #cbcbcb);
padding: 1.00em 0.5em 0.75em 0.5em;
position: relative; }
position: relative;
box-sizing: border-box; }
@media only screen and (max-width: 768px) {
.wp-block-tainacan-dynamic-items-list .dynamic-items-search-bar {
flex-wrap: wrap; }

File diff suppressed because one or more lines are too long

View File

@ -122,7 +122,8 @@
display: flex;
align-items: center;
border-bottom: 1px solid var(--tainacan-block-gray3, #cbcbcb);
padding: 1.00em 0.5em 0.75em 0.5em; }
padding: 1.00em 0.5em 0.75em 0.5em;
box-sizing: border-box; }
@media only screen and (max-width: 768px) {
.wp-block-tainacan-facets-list .facets-search-bar {
flex-wrap: wrap; }

File diff suppressed because one or more lines are too long

View File

@ -81,7 +81,10 @@
pointer-events: none; }
.tainacan-media-component__swiper-main ul.swiper-wrapper[data-pswp-uid]:not([data-pswp-uid='']) li.swiper-slide .swiper-slide-content a {
cursor: zoom-in !important; }
.tainacan-media-component__swiper-main ul.swiper-wrapper[data-pswp-uid]:not([data-pswp-uid='']) li.swiper-slide .swiper-slide-content .tainacan-item-file-download a {
cursor: pointer !important; }
.tainacan-media-component__swiper-main li.swiper-slide {
box-sizing: border-box;
height: 100%;
max-width: 100%;
padding: 0 var(--swiper-navigation-size, 44px);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,40 @@
html {
overflow-y: hidden; }
body.admin_page_tainacan_mobile_app {
background-color: white;
padding: 0px;
margin: 0px;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
overflow: hidden; }
body.admin_page_tainacan_mobile_app #adminmenumain,
body.admin_page_tainacan_mobile_app #wpadminbar,
body.admin_page_tainacan_mobile_app #wpfooter {
display: none; }
body.admin_page_tainacan_mobile_app #wp-auth-check-wrap {
z-index: 9999999; }
body.admin_page_tainacan_mobile_app #wpcontent,
body.admin_page_tainacan_mobile_app #wpbody,
body.admin_page_tainacan_mobile_app #wpbody-content {
padding: 0px;
margin: 0; }
body.admin_page_tainacan_mobile_app h1 {
display: none; }
body.admin_page_tainacan_mobile_app .wrap {
margin: 0px !important;
padding: 1em 2em;
display: flex;
justify-content: space-between;
align-items: center;
height: calc(100vh - 2em);
width: calc(100vw - 4em);
text-align: center;
background-image: url("../../assets/images/tainacan_loading.gif");
background-repeat: no-repeat;
background-position: center; }
/*# sourceMappingURL=tainacan-mobile-app.css.map */

View File

@ -0,0 +1,7 @@
{
"version": 3,
"mappings": "AACA,IAAK;EACD,UAAU,EAAE,MAAM;;AAEtB,mCAAoC;EAChC,gBAAgB,EAAE,KAAK;EACvB,OAAO,EAAE,GAAG;EACZ,MAAM,EAAE,GAAG;EACX,QAAQ,EAAE,QAAQ;EAClB,GAAG,EAAE,CAAC;EACN,MAAM,EAAE,CAAC;EACT,IAAI,EAAE,CAAC;EACP,KAAK,EAAE,CAAC;EACR,QAAQ,EAAE,MAAM;EAEhB;;+CAEU;IACN,OAAO,EAAE,IAAI;EAEjB,uDAAoB;IAChB,OAAO,EAAE,OAAO;EAGpB;;qDAEgB;IACZ,OAAO,EAAE,GAAG;IACZ,MAAM,EAAE,CAAC;EAEb,sCAAG;IACC,OAAO,EAAE,IAAI;EAEjB,yCAAM;IACF,MAAM,EAAE,cAAc;IACtB,OAAO,EAAE,OAAO;IAChB,OAAO,EAAE,IAAI;IACb,eAAe,EAAE,aAAa;IAC9B,WAAW,EAAE,MAAM;IACnB,MAAM,EAAE,iBAAiB;IACzB,KAAK,EAAE,iBAAiB;IACxB,UAAU,EAAE,MAAM;IAClB,gBAAgB,EAAE,+CAA+C;IACjE,iBAAiB,EAAE,SAAS;IAC5B,mBAAmB,EAAE,MAAM",
"sources": ["../../views/mobile-app/tainacan-mobile-app.scss"],
"names": [],
"file": "tainacan-mobile-app.css"
}

View File

@ -151,6 +151,9 @@
.tainacan-icon-items:before {
content: "items";
}
.tainacan-icon-item:before {
content: "item";
}
.tainacan-icon-menu:before {
content: "menu";
}

View File

@ -81,14 +81,44 @@ class REST_Collections_Controller extends REST_Controller {
),
'schema' => [$this, 'get_schema'],
));
register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<collection_id>[\d]+)/metadata_order', array(
register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<collection_id>[\d]+)/metadata_section_order', array(
array(
'methods' => \WP_REST_Server::EDITABLE,
'callback' => array($this, 'update_metadata_section_order'),
'permission_callback' => array($this, 'update_metadata_section_order_permissions_check'),
'args' => [
'metadata_section_order' => [
'description' => __( 'The order of the metadata section in the collection, an array of objects with integer ID and bool enabled.', 'tainacan' ),
'required' => true,
'validate_callback' => [$this, 'validate_metadata_section_order']
]
],
),
'schema' => [$this, 'get_schema'],
));
register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<collection_id>[\d]+)/metadata_section/default_section/metadata_order', array(
array(
'methods' => \WP_REST_Server::EDITABLE,
'callback' => array($this, 'update_metadata_order'),
'permission_callback' => array($this, 'update_metadata_order_permissions_check'),
'args' => [
'metadata_order' => [
'description' => __( 'The order of the metadata in the collection, an array of objects with integer ID and bool enabled.', 'tainacan' ),
'description' => __( 'The order of the metadata in the section, an array of objects with integer ID and bool enabled.', 'tainacan' ),
'required' => true,
'validate_callback' => [$this, 'validate_filters_metadata_order']
]
],
),
'schema' => [$this, 'get_schema'],
));
register_rest_route($this->namespace, '/' . $this->rest_base . '/(?P<collection_id>[\d]+)/metadata_section/(?P<metadata_section_id>[\d]+)/metadata_order', array(
array(
'methods' => \WP_REST_Server::EDITABLE,
'callback' => array($this, 'update_metadata_order'),
'permission_callback' => array($this, 'update_metadata_order_permissions_check'),
'args' => [
'metadata_order' => [
'description' => __( 'The order of the metadata in the section, an array of objects with integer ID and bool enabled.', 'tainacan' ),
'required' => true,
'validate_callback' => [$this, 'validate_filters_metadata_order']
]
@ -367,7 +397,7 @@ class REST_Collections_Controller extends REST_Controller {
* @return bool|\WP_Error
* @throws \Exception
*/
public function get_item_permissions_check($request){
public function get_item_permissions_check($request){
$collection = $this->collections_repository->fetch($request['collection_id']);
if(($collection instanceof Entities\Collection)) {
@ -462,7 +492,7 @@ class REST_Collections_Controller extends REST_Controller {
if(! $collection instanceof Entities\Collection) {
return new \WP_REST_Response([
'error_message' => __('Collection with this ID was not found', 'tainacan' ),
'collection_id' => $collection_id
'collection_id' => $request['collection_id']
], 400);
}
@ -586,6 +616,27 @@ class REST_Collections_Controller extends REST_Controller {
}
public function validate_metadata_section_order($value, $request, $param) {
if ( is_array($value) ) {
foreach ($value as $val) {
if ( !is_array($val) ) {
return false;
}
if ( !isset($val['id']) || (!is_numeric($val['id']) && $val['id'] != \Tainacan\Entities\Metadata_Section::$default_section_slug ) ) {
return false;
}
if ( !isset($val['enabled']) || !is_bool($val['enabled']) ) {
return false;
}
if ( !isset($val['metadata_order']) || !is_array($val['metadata_order']) ) {
return false;
}
}
return true;
}
return false;
}
/**
* Update a collection metadata order
*
@ -595,6 +646,7 @@ class REST_Collections_Controller extends REST_Controller {
*/
public function update_metadata_order( $request ) {
$collection_id = $request['collection_id'];
$metadata_section_id = isset($request['metadata_section_id']) ? $request['metadata_section_id'] : \Tainacan\Entities\Metadata_Section::$default_section_slug;
$body = json_decode($request->get_body(), true);
@ -603,7 +655,66 @@ class REST_Collections_Controller extends REST_Controller {
$collection = $this->collections_repository->fetch($collection_id);
if( $collection instanceof Entities\Collection) {
$collection->set_metadata_order( $body['metadata_order'] );
$metadata_section_order = $collection->get_metadata_section_order();
if( !isset( $metadata_section_order ) || !is_array($metadata_section_order) ) {
$metadata_section_order = array();
}
$section_order_index = array_search( $metadata_section_id, array_column( $metadata_section_order, 'id' ) );
if ( $section_order_index !== false ) {
$metadata_section_order[$section_order_index]['metadata_order'] = $body['metadata_order'];
} else {
$metadata_section_order[] = array(
'id' => $metadata_section_id,
'metadata_order' => $body['metadata_order'],
'enabled' => true
);
}
$collection->set_metadata_section_order( $metadata_section_order );
if ( $collection->validate() ) {
$updated_collection = $this->collections_repository->update( $collection );
$response = $this->prepare_item_for_response($updated_collection, $request);
return new \WP_REST_Response( $response, 200 );
}
return new \WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'errors' => $collection->get_errors(),
'collection' => $this->prepare_item_for_response($collection, $request)
], 400);
}
return new \WP_REST_Response([
'error_message' => __('Collection with this ID was not found', 'tainacan' ),
'collection_id' => $collection_id
], 400);
}
return new \WP_REST_Response([
'error_message' => __('The body could not be empty', 'tainacan'),
'body' => $body
], 400);
}
/**
* Update a collection metadata section order
*
* @param \WP_REST_Request $request
*
* @return string|\WP_Error|\WP_REST_Response
*/
public function update_metadata_section_order( $request ) {
$collection_id = $request['collection_id'];
$body = json_decode($request->get_body(), true);
if( !empty($body) && isset($body['metadata_section_order']) ) {
$collection = $this->collections_repository->fetch($collection_id);
if( $collection instanceof Entities\Collection) {
$collection->set_metadata_section_order( $body['metadata_section_order'] );
if ( $collection->validate() ) {
$updated_collection = $this->collections_repository->update( $collection );
@ -651,6 +762,25 @@ class REST_Collections_Controller extends REST_Controller {
return false;
}
/**
* Verify if current user has permission to update metadata section order
*
* @param \WP_REST_Request $request
*
* @return bool|\WP_Error
* @throws \Exception
*/
public function update_metadata_section_order_permissions_check( $request ) {
$collection = $this->collections_repository->fetch($request['collection_id']);
if($collection instanceof Entities\Collection) {
return $collection->user_can( 'edit_metadata' ); // && $collection->user_can( 'edit_metadata_section' );
}
return false;
}
/**
* Update a collection metadata order
*
@ -680,8 +810,8 @@ class REST_Collections_Controller extends REST_Controller {
return new \WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'errors' => $prepared_collection->get_errors(),
'collection' => $this->prepare_item_for_response($prepared_collection, $request)
'errors' => $collection->get_errors(),
'collection' => $this->prepare_item_for_response($collection, $request)
], 400);
}

View File

@ -42,6 +42,11 @@ class REST_Item_Metadata_Controller extends REST_Controller {
public function register_routes() {
register_rest_route($this->namespace, '/item/(?P<item_id>[\d]+)/' . $this->rest_base . '/(?P<metadatum_id>[\d]+)',
array(
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'get_item_metadatum_value'),
'permission_callback' => array($this, 'get_items_permissions_check'),
),
array(
'methods' => \WP_REST_Server::EDITABLE,
'callback' => array($this, 'update_item'),
@ -55,7 +60,7 @@ class REST_Item_Metadata_Controller extends REST_Controller {
),
)
);
register_rest_route($this->namespace, '/item/(?P<item_id>[\d]+)/'. $this->rest_base,
register_rest_route($this->namespace, '/item/(?P<item_id>[\d]+)/' . $this->rest_base,
array(
array(
'methods' => \WP_REST_Server::READABLE,
@ -65,15 +70,6 @@ class REST_Item_Metadata_Controller extends REST_Controller {
)
)
);
register_rest_route($this->namespace, '/item/(?P<item_id>[\d]+)/'. $this->rest_base. '/(?P<metadatum_id>[\d]+)',
array(
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'get_item_metadatum_value'),
'permission_callback' => array($this, 'get_items_permissions_check'),
)
)
);
}
/**

View File

@ -464,6 +464,7 @@ class REST_Items_Controller extends REST_Controller {
$meta_id = $meta['key'];
$meta_value = is_array($meta['value']) ? $meta['value'] : [$meta['value']];
$meta_label = isset($meta['label']) ? $meta['label'] : $meta_value;
$meta_type = 'CHAR';
$filter_type_component = false;
$arg_type = 'meta';
$f = false;
@ -535,6 +536,11 @@ class REST_Items_Controller extends REST_Controller {
return apply_filters("tainacan-item-get-author-name", $name);
}, $meta_label);
break;
case 'int':
case 'float':
case 'numeric':
$meta_type = 'NUMERIC';
break;
}
}
}
@ -549,7 +555,8 @@ class REST_Items_Controller extends REST_Controller {
'arg_type' => $arg_type,
'value' => $meta_value,
'label' => $meta_label,
'compare' => isset($meta['compare']) ? $meta['compare'] : '='
'compare' => isset($meta['compare']) ? $meta['compare'] : '=',
'type' => $meta_type,
));
}
@ -596,6 +603,17 @@ class REST_Items_Controller extends REST_Controller {
$collection_id = $request['collection_id'];
}
$filters_args = $this->prepare_filters_arguments($args, $collection_id);
if(isset($args['meta_query']) && !empty($args['meta_query']) && is_array($filters_args) && !empty($filters_args)) {
foreach($filters_args as $filters_arg) {
if($filters_arg['filter'] !== false) {
for($idx = 0; $idx < count($args['meta_query']); $idx++) {
if($args['meta_query'][$idx]['key'] == $filters_arg['metadatum']['metadatum_id']) {
$args['meta_query'][$idx]['type'] = $filters_arg['type'];
}
}
}
}
}
$max_items_per_page = $TAINACAN_API_MAX_ITEMS_PER_PAGE;
if ( $max_items_per_page > -1 ) {
@ -996,16 +1014,32 @@ class REST_Items_Controller extends REST_Controller {
$errors = [];
foreach ($metadata as $item_metadatum) {
$new_item_metadatum = new Entities\Item_Metadata_Entity( $new_item, $item_metadatum->get_metadatum() );
$new_item_metadatum->set_value( $item_metadatum->get_value() );
if ( $new_item_metadatum->validate() ) {
Repositories\Item_Metadata::get_instance()->insert( $new_item_metadatum );
if ( $item_metadatum->get_metadatum()->get_metadata_type() == 'Tainacan\Metadata_Types\Compound' ) {
$multiple_values = $item_metadatum->is_multiple() ? $item_metadatum->get_value() : [$item_metadatum->get_value()] ;
foreach ($multiple_values as $value) {
$parent_meta_id = null;
foreach ( $value as $meta_id => $meta ) {
if ( $meta instanceof Entities\Item_Metadata_Entity) {
$item_metadata = new Entities\Item_Metadata_Entity($new_item, $meta->get_metadatum(), null, $parent_meta_id);
$item_metadata->set_value( $meta->get_value() );
if ( $item_metadata->validate() ) {
$item_metadata = Repositories\Item_Metadata::get_instance()->insert( $item_metadata );
$parent_meta_id = $item_metadata->get_parent_meta_id();
} else {
$errors[] = $item_metadata->get_errors();
}
}
}
}
} else {
$errors[] = $new_item_metadatum->get_errors();
$new_item_metadatum = new Entities\Item_Metadata_Entity( $new_item, $item_metadatum->get_metadatum() );
$new_item_metadatum->set_value( $item_metadatum->get_value() );
if ( $new_item_metadatum->validate() ) {
Repositories\Item_Metadata::get_instance()->insert( $new_item_metadatum );
} else {
$errors[] = $new_item_metadatum->get_errors();
}
}
}
if ($args['status'] != 'draft') {
@ -1382,7 +1416,12 @@ class REST_Items_Controller extends REST_Controller {
], 400);
}
$secret_key = get_option("tnc_option_recaptch_secret_key");
$response = json_decode(file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret=$secret_key&response=".$captcha_data."&remoteip=".$_SERVER['REMOTE_ADDR']));
$api_url = "https://www.google.com/recaptcha/api/siteverify?secret=$secret_key&response=".$captcha_data."&remoteip=".$_SERVER['REMOTE_ADDR'];
$response = wp_remote_get( $api_url );
$body = wp_remote_retrieve_body( $response );
$response = json_decode($body);
if ($response->success) {
return true;
} else {

View File

@ -148,22 +148,14 @@ class REST_Metadata_Controller extends REST_Controller {
* @return \WP_Error|\WP_REST_Response
*/
public function get_item( $request ) {
$collection_id = isset($request['collection_id']) ? $request['collection_id'] : false;
$metadatum_id = $request['metadatum_id'];
$offset = '';
$number = '';
if($request['offset'] >= 0 && $request['number'] >= 1){
$offset = $request['offset'];
$number = $request['number'];
}
$result = $this->metadatum_repository->fetch($metadatum_id, 'OBJECT');
if (! $result instanceof Entities\Metadatum) {
return new \WP_REST_Response([
'error_message' => __('Metadata with this ID was not found', 'tainacan'),
'item_id' => $item_id
'item_id' => $metadatum_id
], 400);
}
@ -187,7 +179,7 @@ class REST_Metadata_Controller extends REST_Controller {
}
/**
* @param \WP_REST_Request $request
* @param String $request
*
* @param null $collection_id
*
@ -444,7 +436,7 @@ class REST_Metadata_Controller extends REST_Controller {
if (! $metadatum instanceof Entities\Metadatum) {
return new \WP_REST_Response([
'error_message' => __('Metadata with this ID was not found', 'tainacan'),
'item_id' => $item_id
'item_id' => $metadatum_id
], 400);
}
@ -496,8 +488,6 @@ class REST_Metadata_Controller extends REST_Controller {
$metadatum = $this->metadatum_repository->fetch($metadatum_id);
$error_message = __('Metadata with this ID was not found', 'tainacan');
if ($metadatum) {
// These conditions are for verify if endpoints are used correctly
if(!$collection_id && $metadatum->get_collection_id() !== 'default') {
@ -506,14 +496,14 @@ class REST_Metadata_Controller extends REST_Controller {
return new \WP_REST_Response( [
'error_message' => $error_message,
'metadatum_id' => $metadatum_id
] );
], 400 );
} elseif ($collection_id && $metadatum->get_collection_id() === 'default'){
$error_message = __('This metadata is not a collection metadata', 'tainacan');
return new \WP_REST_Response( [
'error_message' => $error_message,
'metadatum_id' => $metadatum_id
] );
], 400 );
}
if (isset($request['repository_level']) && $confirm_repository) {
@ -523,7 +513,7 @@ class REST_Metadata_Controller extends REST_Controller {
$prepared_metadata = $this->prepare_item_for_updating($metadatum, $attributes);
if($prepared_metadata->validate()){
$updated_metadata = $this->metadatum_repository->update($prepared_metadata);
$updated_metadata = $this->metadatum_repository->update($prepared_metadata, $attributes);
$response = $this->prepare_item_for_response($updated_metadata, $request);
@ -537,6 +527,7 @@ class REST_Metadata_Controller extends REST_Controller {
], 400);
}
$error_message = __('Metadata with this ID was not found', 'tainacan');
return new \WP_REST_Response( [
'error_message' => $error_message,
'metadatum_id' => $metadatum_id
@ -595,9 +586,9 @@ class REST_Metadata_Controller extends REST_Controller {
$endpoint_args = [];
if($method === \WP_REST_Server::READABLE) {
$endpoint_args = array_merge(
$endpoint_args,
parent::get_wp_query_params()
);
$endpoint_args,
parent::get_wp_query_params()
);
} elseif ($method === \WP_REST_Server::CREATABLE || $method === \WP_REST_Server::EDITABLE) {
$map = $this->metadatum_repository->get_map();

View File

@ -0,0 +1,664 @@
<?php
namespace Tainacan\API\EndPoints;
use \Tainacan\API\REST_Controller;
use Tainacan\Entities;
use Tainacan\Repositories;
class REST_Metadata_Sections_Controller extends REST_Controller {
public function __construct() {
parent::__construct();
$this->rest_base = 'metadata-sections';
add_action('init', array(&$this, 'init_objects'), 11);
}
/**
* Initialize objects after post_type register
*
* @throws \Exception
*/
public function init_objects() {
$this->metadata_sections_repository = Repositories\Metadata_Sections::get_instance();
$this->metadata_repository = Repositories\Metadata::get_instance();
$this->collection_repository = Repositories\Collections::get_instance();
}
/**
* If POST on metadatum/collection/<collection_id>, then
* a metadatum will be created in matched collection and all your item will receive this metadatum
*
* If POST on metadatum/item/<item_id>, then a value will be added in a metadatum and metadatum passed
* id body of requisition
*
* Both of GETs return the metadatum of matched objects
*
* @throws \Exception
*/
public function register_routes() {
register_rest_route($this->namespace, '/collection/(?P<collection_id>[\d]+)/' . $this->rest_base . '/(?P<metadata_section_id>[\d|default_section]+)',
array(
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'get_item'),
'permission_callback' => array($this, 'get_item_permissions_check'),
'args' => array(
'context' => array(
'type' => 'string',
'default' => 'view',
'description' => 'The context in which the request is made.',
'enum' => array(
'view',
'edit'
)
),
),
),
array(
'methods' => \WP_REST_Server::EDITABLE,
'callback' => array($this, 'update_item'),
'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::DELETABLE,
'callback' => array($this, 'delete_item'),
'permission_callback' => array($this, 'delete_item_permissions_check'),
),
'schema' => [$this, 'get_schema']
)
);
register_rest_route($this->namespace, '/collection/(?P<collection_id>[\d]+)/' . $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_wp_query_params(),
),
array(
'methods' => \WP_REST_Server::CREATABLE,
'callback' => array($this, 'create_item'),
'permission_callback' => array($this, 'create_item_permissions_check'),
'args' => $this->get_endpoint_args_for_item_schema(\WP_REST_Server::CREATABLE),
),
'schema' => [$this, 'get_schema']
)
);
register_rest_route($this->namespace, '/collection/(?P<collection_id>[\d]+)/' . $this->rest_base . '/(?P<metadata_section_id>[\d|default_section]+)/metadata',
array(
array(
'methods' => \WP_REST_Server::READABLE,
'callback' => array($this, 'get_metadata_list'),
'permission_callback' => array($this, 'get_item_permissions_check'),
'args' => $this->get_endpoint_args_for_item_schema(\WP_REST_Server::CREATABLE),
),
array(
'methods' => \WP_REST_Server::CREATABLE,
'callback' => array($this, 'add_metadata'),
'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::DELETABLE,
'callback' => array($this, 'delete_metadata'),
'permission_callback' => array($this, 'update_item_permissions_check'),
'args' => $this->get_endpoint_args_for_item_schema(\WP_REST_Server::EDITABLE),
),
'schema' => [$this, 'get_schema']
)
);
}
/**
* @param \WP_REST_Request|string $request
*
* @param $collection_id
*
* @return object|void|\WP_Error
* @throws \Exception
*/
public function prepare_item_for_database( $request, $collection_id = null) {
if($collection_id == null) {
throw new \InvalidArgumentException('You need provide a collection id');
}
$metadata_section = new Entities\Metadata_Section();
$meta = json_decode( $request, true );
foreach ( $meta as $key => $value ) {
$set_ = 'set_' . $key;
$metadata_section->$set_( $value );
}
$collection = new Entities\Collection( $collection_id );
$metadata_section->set_collection( $collection );
return $metadata_section;
}
/**
* @param Entities\Metadata_Section $item
* @param \WP_REST_Request $request
*
* @return array|\WP_Error|\WP_REST_Response
*/
public function prepare_item_for_response( $item, $request ) {
if (!empty($item)){
$item_arr = $item->_toArray();
if ($request['context'] === 'edit') {
$item_arr['current_user_can_edit'] = $item->can_edit();
$item_arr['current_user_can_delete'] = $item->can_delete();
$item_arr['enabled'] = $item->get_enabled_for_collection();
}
$args = [];
if ($request['include_disabled'] === 'true') {
$args['include_disabled'] = true;
}
$metadata_list = $item->get_metadata_object_list($args);
$item_arr['metadata_object_list'] = [];
if($metadata_list != false) {
foreach($metadata_list as $metadata) {
$meta_arr = $this->prepare_metadata_for_response($metadata, $request);
$item_arr['metadata_object_list'][] = $meta_arr;
}
}
/**
* Use this filter to add additional post_meta to the api response
* Use the $request object to get the context of the request and other variables
* For example, id context is edit, you may want to add your meta or not.
*
* Also take care to do any permissions verification before exposing the data
*/
$extra_metadata = apply_filters('tainacan-api-response-metadata-section-meta', [], $request);
foreach ($extra_metadata as $extra_meta) {
$item_arr[$extra_meta] = get_post_meta($item_arr['id'], $extra_meta, true);
}
return $item_arr;
}
return $item;
}
/**
* @param Entities\Metadata $item
* @param \WP_REST_Request $request
*
* @return array|\WP_Error|\WP_REST_Response
*/
public function prepare_metadata_for_response( $item, $request ) {
if(!empty($item)){
$item_arr = $item->_toArray();
$item_arr['metadata_type_object'] = $item->get_metadata_type_object()->_toArray();
if ( isset($request['include_options_as_html']) && $request['include_options_as_html'] == 'yes' )
$item_arr['options_as_html'] = $item->get_metadata_type_object()->get_options_as_html();
if ( isset($item_arr['metadata_type_options']) && isset($item_arr['metadata_type_options']['taxonomy_id']) ) {
$taxonomy = Repositories\Taxonomies::get_instance()->get_db_identifier_by_id( $item_arr['metadata_type_options']['taxonomy_id'] );
$item_arr['metadata_type_options']['taxonomy'] = $taxonomy;
}
if ($request['context'] === 'edit') {
$item_arr['current_user_can_edit'] = $item->can_edit();
$item_arr['current_user_can_delete'] = $item->can_delete();
ob_start();
$item->get_metadata_type_object()->form();
$form = ob_get_clean();
$item_arr['edit_form'] = $form;
$item_arr['enabled'] = $item->get_enabled_for_collection();
if(isset($item_arr['metadata_type_options']) && isset($item_arr['metadata_type_options']['children_objects'])) {
foreach ($item_arr['metadata_type_options']['children_objects'] as $index => $children) {
$item_arr['metadata_type_options']['children_objects'][$index]['current_user_can_edit'] = $item->can_edit();
$item_arr['metadata_type_options']['children_objects'][$index]['current_user_can_delete'] = $item->can_delete();
}
}
}
/**
* Use this filter to add additional post_meta to the api response
* Use the $request object to get the context of the request and other variables
* For example, id context is edit, you may want to add your meta or not.
*
* Also take care to do any permissions verification before exposing the data
*/
$extra_metadata = apply_filters('tainacan-api-response-metadatum-meta', [], $request);
foreach ($extra_metadata as $extra_meta) {
$item_arr[$extra_meta] = get_post_meta($item_arr['id'], $extra_meta, true);
}
$item_arr['inherited'] = $item_arr['collection_id'] != $request['collection_id'];
return $item_arr;
}
return $item;
}
/**
* @param \WP_REST_Request $request
*
* @return \WP_Error|\WP_REST_Response
*/
public function get_item( $request ) {
$metadata_section_id = $request['metadata_section_id'];
if($metadata_section_id == \Tainacan\Entities\Metadata_Section::$default_section_slug) {
$collection_id = is_numeric($request['collection_id']) ? (int)$request['collection_id'] : null;
$result = $this->metadata_sections_repository->get_default_section($collection_id);
} else {
$result = $this->metadata_sections_repository->fetch($metadata_section_id, 'OBJECT');
}
if (! $result instanceof Entities\Metadata_Section) {
return new \WP_REST_Response([
'error_message' => __('Metadata section with this ID was not found', 'tainacan'),
'item_id' => $metadata_section_id
], 400);
}
return new \WP_REST_Response($this->prepare_item_for_response($result, $request), 200);
}
/**
* @param \WP_REST_Request $request
*
* @return bool|\WP_Error
* @throws \Exception
*/
public function get_item_permissions_check( $request ) {
if(!isset($request['collection_id']) || !isset($request['metadata_section_id'])) {
return false;
}
$collection = $this->collection_repository->fetch($request['collection_id']);
if($collection instanceof Entities\Collection && $collection->can_read()) {
$metadata_section_id = $request['metadata_section_id'];
if($metadata_section_id == \Tainacan\Entities\Metadata_Section::$default_section_slug) {
$metadatum_section = $this->metadata_sections_repository->get_default_section($collection);
} else {
$metadatum_section = $this->metadata_sections_repository->fetch($metadata_section_id);
}
return $metadatum_section instanceof Entities\Metadata_Section && $metadatum_section->can_read();
}
return false;
}
/**
* @param \WP_REST_Request $request
*
* @return \WP_Error|\WP_REST_Response
* @throws \Exception
*/
public function create_item( $request ) {
if(!empty($request->get_body()) && isset($request['collection_id'])){
$collection_id = $request['collection_id'];
try {
$prepared = $this->prepare_item_for_database( $request->get_body(), $collection_id );
} catch (\Exception $exception) {
return new \WP_REST_Response($exception->getMessage(), 400);
}
if($prepared->validate()) {
$metadata_section = $this->metadata_sections_repository->insert($prepared);
$response = $this->prepare_item_for_response($metadata_section, $request);
return new \WP_REST_Response($response, 201);
} else {
return new \WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'errors' => $prepared->get_errors(),
'metadatum' => $this->prepare_item_for_response($prepared, $request),
], 400);
}
}
return new \WP_REST_Response([
'error_message' => __('Body cannot be empty.', 'tainacan'),
'item' => $request->get_body()
], 400);
}
// @TODO: fix the permissions check
/**
* @param $request
*
* @return bool|\WP_Error
* @throws \Exception
*/
public function create_item_permissions_check( $request ) {
return true;
if( isset($request['collection_id']) ) {
$collection = $this->collection_repository->fetch( $request['collection_id'] );
if ( $collection instanceof Entities\Collection ) {
return $collection->user_can( 'edit_metadata_section' );
}
} else {
return current_user_can( 'tnc_rep_edit_metadata_section' );
}
return false;
}
/**
* @param \WP_REST_Request $request
*
* @return \WP_Error|\WP_REST_Response
* @throws \Exception
*/
public function get_items( $request ) {
if(isset($request['collection_id'])) {
$collection_id = $request['collection_id'];
$args = $this->prepare_filters( $request );
if ($request['include_disabled'] === 'true') {
$args['include_disabled'] = true;
}
$collection = new Entities\Collection( $collection_id );
$result = $this->metadata_sections_repository->fetch_by_collection( $collection, $args );
}
$prepared_item = [];
foreach ( $result as $item ) {
$prepared_item[] = $this->prepare_item_for_response( $item, $request );
}
return new \WP_REST_Response($prepared_item, 200);
}
/**
* @param \WP_REST_Request $request
*
* @return bool|\WP_Error
* @throws \Exception
*/
public function get_items_permissions_check( $request ) {
if(!isset($request['collection_id'])) {
return true;
}
$collection = $this->collection_repository->fetch($request['collection_id']);
if($collection instanceof Entities\Collection){
if ( ! $collection->can_read() ) {
return false;
}
return true;
}
return false;
}
/**
* @param \WP_REST_Request $request
*
* @return \WP_Error|\WP_REST_Response
*/
public function delete_item( $request ) {
$metadata_section_id = $request['metadata_section_id'];
$metadatum_section = $this->metadata_sections_repository->fetch($metadata_section_id);
if (! $metadatum_section instanceof Entities\Metadata_Section) {
return new \WP_REST_Response([
'error_message' => __('Metadata section with this ID was not found', 'tainacan'),
'item_id' => $metadata_section_id
], 400);
}
$metadatum_section_trashed = $this->metadata_sections_repository->delete($metadatum_section);
$prepared = $this->prepare_item_for_response($metadatum_section_trashed, $request);
return new \WP_REST_Response($prepared, 200);
}
/**
* @param \WP_REST_Request $request
*
* @return bool|\WP_Error
* @throws \Exception
*/
public function delete_item_permissions_check( $request ) {
$metadata_section = $this->metadata_sections_repository->fetch($request['metadata_section_id']);
if ($metadata_section instanceof Entities\Metadata_Section) {
return $metadata_section->can_delete();
}
return false;
}
/**
* @param \WP_REST_Request $request
*
* @return \WP_Error|\WP_REST_Response
*/
public function update_item( $request ) {
$collection_id = is_numeric($request['collection_id']) ? $request['collection_id'] : null;
$body = json_decode($request->get_body(), true);
if(!empty($body)){
$metadata_section_id = $request['metadata_section_id'];
if($metadata_section_id == \Tainacan\Entities\Metadata_Section::$default_section_slug) {
$metadata_section = $this->metadata_sections_repository->get_default_section($collection_id);
} else {
$metadata_section = $this->metadata_sections_repository->fetch($metadata_section_id);
if ( $collection_id != $metadata_section->get_collection_id() ) {
return new \WP_REST_Response( [
'error_message' => __('This metadata section not found in collection', 'tainacan'),
'metadata_section_id' => $metadata_section_id
] );
}
}
if ($metadata_section) {
$attributes = [];
foreach ($body as $att => $value) {
$attributes[$att] = $value;
}
$prepared = $this->prepare_item_for_updating($metadata_section, $attributes);
if($prepared->validate()) {
$updated_metadata_section = $this->metadata_sections_repository->update($prepared);
$response = $this->prepare_item_for_response($updated_metadata_section, $request);
return new \WP_REST_Response($response, 200);
}
return new \WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'errors' => $prepared->get_errors(),
'metadatum_section' => $this->prepare_item_for_response($prepared, $request)
], 400);
}
return new \WP_REST_Response( [
'error_message' => __('Metadata with this ID was not found', 'tainacan'),
'metadata_section_id' => $metadata_section_id
] );
}
return new \WP_REST_Response([
'error_message' => __('The body could not be empty', 'tainacan'),
'body' => $body
], 400);
}
public function add_metadata( $request ) {
if( !empty($request->get_body()) && isset($request['metadata_section_id']) ){
$body = json_decode($request->get_body(), true);
$metadata_section_id = $request['metadata_section_id'];
$metadata_list = $body['metadata_list'];
try {
$metadata_section = $this->metadata_sections_repository->add_metadata($metadata_section_id, $metadata_list);
if($metadata_section == false) {
return new \WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'item' => $request->get_body()
], 400);
}
$response = $this->prepare_item_for_response($metadata_section, $request);
return new \WP_REST_Response($response, 201);
} catch (\Exception $exception) {
return new \WP_REST_Response($exception->getMessage(), 400);
}
}
return new \WP_REST_Response([
'error_message' => __('Body cannot be empty.', 'tainacan'),
'item' => $request->get_body()
], 400);
}
public function delete_metadata( $request ) {
if( !empty($request->get_body()) && isset($request['metadata_section_id']) ){
$body = json_decode($request->get_body(), true);
$metadata_section_id = $request['metadata_section_id'];
$metadata_list = $body['metadata_list'];
try {
$metadata_section = $this->metadata_sections_repository->delete_metadata($metadata_section_id, $metadata_list);
if($metadata_section == false) {
return new \WP_REST_Response([
'error_message' => __('One or more values are invalid.', 'tainacan'),
'item' => $request->get_body()
], 400);
}
$response = $this->prepare_item_for_response($metadata_section, $request);
return new \WP_REST_Response($response, 201);
} catch (\Exception $exception) {
return new \WP_REST_Response($exception->getMessage(), 400);
}
}
return new \WP_REST_Response([
'error_message' => __('Body cannot be empty.', 'tainacan'),
'item' => $request->get_body()
], 400);
}
public function get_metadata_list( $request ) {
if(isset($request['metadata_section_id']) ){
$metadata_section_id = $request['metadata_section_id'];
try {
$result = $this->metadata_sections_repository->get_metadata_object_list($metadata_section_id);
$prepared_item = [];
foreach ( $result as $item ) {
$prepared_item[] = $item->_toArray();
}
return new \WP_REST_Response($prepared_item, 200);
} catch (\Exception $exception) {
return new \WP_REST_Response($exception->getMessage(), 400);
}
}
return new \WP_REST_Response([
'error_message' => __('Metadata section id cannot be empty.', 'tainacan'),
'item' => $request->get_body()
], 400);
}
/**
* @param \WP_REST_Request $request
*
* @return bool|\WP_Error
* @throws \Exception
*/
public function update_item_permissions_check( $request ) {
return true;
$metadatum = $this->metadata_sections_repository->fetch($request['metadatum_id']);
if ($metadatum instanceof Entities\Metadatum) {
return $metadatum->can_edit();
}
return false;
}
/**
* @param null $object_name
*
* @return array|void
*/
public function get_wp_query_params() {
$query_params['context']['default'] = 'view';
$query_params = array_merge($query_params, parent::get_wp_query_params());
$query_params['name'] = array(
'description' => __('Limits the result set to metadata with a specific name'),
'type' => 'string',
);
$query_params = array_merge($query_params, parent::get_meta_queries_params());
return $query_params;
}
/**
* @param null $method
*
* @return array
* @throws \Exception
*/
public function get_endpoint_args_for_item_schema( $method = null ) {
$endpoint_args = [];
if($method === \WP_REST_Server::READABLE) {
$endpoint_args = array_merge(
$endpoint_args,
parent::get_wp_query_params()
);
} elseif ($method === \WP_REST_Server::CREATABLE || $method === \WP_REST_Server::EDITABLE) {
$map = $this->metadata_sections_repository->get_map();
foreach ($map as $mapped => $value){
$set_ = 'set_'. $mapped;
// Show only args that has a method set
if( !method_exists(new Entities\Metadatum(), "$set_") ){
unset($map[$mapped]);
}
}
$endpoint_args = $map;
}
return $endpoint_args;
}
function get_schema() {
$schema = [
'$schema' => 'http://json-schema.org/draft-04/schema#',
'title' => 'metadatum',
'type' => 'object'
];
$main_schema = parent::get_repository_schema( $this->metadata_sections_repository );
$permissions_schema = parent::get_permissions_schema();
// $item_metadata_scheme = parent::get_repository_schema( $this->item_metadata_repository );
// $item_scheme = parent::get_repository_schema( $this->item_repository );
// $collection_scheme = parent::get_repository_schema( $this->collection_repository );
$schema['properties'] = array_merge(
parent::get_base_properties_schema(),
$main_schema,
$permissions_schema
// $item_metadata_scheme,
// $item_scheme,
// $collection_scheme
);
return $schema;
}
}
?>

View File

@ -738,8 +738,15 @@ class REST_Reports_Controller extends REST_Controller {
return new \WP_REST_Response($response, 200);
}
private function get_activities_general($collection_id = false, $interval) {
private function get_activities_general($collection_id = false, $interval = false) {
global $wpdb;
if($interval == false) {
$end = (new \DateTime())->add(new \DateInterval('P1D'))->setTime(0,0,0);
$interval = [
'end' => $end->format('Y-m-d H:i:s'),
'start' => $end->sub(new \DateInterval('P1Y1D'))->format('Y-m-d H:i:s')
];
}
$collection_from = "";
$start = $interval['start'];
$end = $interval['end'];
@ -756,8 +763,15 @@ class REST_Reports_Controller extends REST_Controller {
return $wpdb->get_results($sql_statement);
}
private function get_activities_general_by_user($collection_id = false, $interval) {
private function get_activities_general_by_user($collection_id = false, $interval = false) {
global $wpdb;
if($interval == false) {
$end = (new \DateTime())->add(new \DateInterval('P1D'))->setTime(0,0,0);
$interval = [
'end' => $end->format('Y-m-d H:i:s'),
'start' => $end->sub(new \DateInterval('P1Y1D'))->format('Y-m-d H:i:s')
];
}
$collection_from = "";
$start = $interval['start'];
$end = $interval['end'];

View File

@ -22,6 +22,7 @@ $rest_oaipmh_expose_controller = new \Tainacan\API\EndPoints\REST_Oaipmh_
$rest_item_metadata_controller = new \Tainacan\API\EndPoints\REST_Item_Metadata_Controller();
$rest_sequence_edit_controller = new \Tainacan\API\EndPoints\REST_Sequence_Edit_Controller();
$rest_metadata_types_controller = new \Tainacan\API\EndPoints\REST_Metadata_Types_Controller();
$rest_metadata_sections_controller = new \Tainacan\API\EndPoints\REST_Metadata_Sections_Controller();
$rest_metadatum_mappers_controller = new \Tainacan\API\EndPoints\REST_Metadatum_Mappers_Controller();
$rest_background_processes_controller = new \Tainacan\API\EndPoints\REST_Background_Processes_Controller();
// Add here other endpoints imports

View File

@ -40,7 +40,7 @@ class Elastic_Press {
//activates the inclusion of the complete hierarchy of terms.
add_filter('ep_sync_terms_allow_hierarchy', '__return_true');
add_filter('tainacan_fetch_args', [$this, 'filter_args'], 10, 2);
add_filter('tainacan-fetch-args', [$this, 'filter_args'], 10, 2);
add_filter('tainacan-api-items-filters-response', function($filters) { return $this->last_aggregations; });
add_filter('tainacan-fetch-all-metadatum-values', [$this, 'fetch_all_metadatum_values'], 10, 3);

View File

@ -77,7 +77,7 @@ class Embed {
public function pdf_embed_handler($matches, $attr, $url, $rawattr) {
global $TAINACAN_BASE_URL;
$viewer_url = $TAINACAN_BASE_URL . '/views/libs/pdf-viewer/pdf-viewer.html?file=' . $url;
$viewer_url = $TAINACAN_BASE_URL . '/views/libs/pdf-viewer/pdfjs-dist/web/viewer.html?file=' . $url;
//$viewer_url = $TAINACAN_BASE_URL . '/assets/pdfjs-dist/web/viewer.html?file=' . $url;
$defaults = array(

View File

@ -74,6 +74,7 @@ class Media {
return $this->insert_attachment_from_blob($file, basename($url), $post_id);
} catch (\Exception $e) {
error_log($e);
return false;
}
@ -96,62 +97,22 @@ class Media {
}
/**
* Avoid memory overflow problems with large files (Exceeded maximum memory limit of PHP)
*
* @param $url
* @return string the file path
*/
public function save_remote_file($url) {
set_time_limit(0);
/**
* Avoid memory overflow problems with large files (Exceeded maximum memory limit of PHP)
*
* @param $url
* @return string the file path
*/
public function save_remote_file($url) {
// Include file.php
require_once( ABSPATH . 'wp-admin/includes/file.php' );
$filename = tempnam(sys_get_temp_dir(), basename($url));
# Open the file for writing...
self::$file_handle = fopen($filename, 'w+');
self::$file_name = $filename;
$callback = function ($ch, $str) {
$len = fwrite(self::$file_handle, $str);
return $len;
};
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_FILE, self::$file_handle);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); # optional
curl_setopt($ch, CURLOPT_TIMEOUT, -1); # optional: -1 = unlimited, 3600 = 1 hour
curl_setopt($ch, CURLOPT_VERBOSE, false); # Set to true to see all the innards
# Only if you need to bypass SSL certificate validation
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
# Assign a callback function to the CURL Write-Function
curl_setopt($ch, CURLOPT_WRITEFUNCTION, $callback);
# Execute the download - note we DO NOT put the result into a variable!
curl_exec($ch);
if (curl_errno($ch)) {
$error_msg = curl_error($ch);
# Close CURL
curl_close($ch);
# Close the file pointer
fclose(self::$file_handle);
throw new \Exception( "[save_remote_file]:" . $error_msg);
}
# Close CURL
curl_close($ch);
# Close the file pointer
fclose(self::$file_handle);
return $filename;
$filename = \download_url($url, 900);
if( is_wp_error($filename) ) {
throw new \Exception( "[save_remote_file]:" . implode("\n", $filename->get_error_messages()));
}
return $filename;
}
/**
@ -373,7 +334,7 @@ class Media {
$embed = $wp_embed->autoembed($url);
if ( $embed == $url ) {
if ( esc_url($embed) == esc_url($url) ) {
$output .= sprintf("<a href='%s' target='blank'>%s</a>", $url, $url);
} else {
$output .= $embed;

View File

@ -117,12 +117,12 @@ class Private_Files {
// regular ajax uploads via Admin Panel will send post_id
if ( isset($_REQUEST['post_id']) && $_REQUEST['post_id'] ) {
$post_id = $_REQUEST['post_id'];
$post_id = sanitize_text_field($_REQUEST['post_id']);
}
// API requests to media endpoint will send post
if ( false === $post_id && isset($_REQUEST['post']) && is_numeric($_REQUEST['post']) ) {
$post_id = $_REQUEST['post'];
$post_id = sanitize_text_field($_REQUEST['post']);
}
// tainacan internals, scripts and tests, will set this global
@ -191,7 +191,7 @@ class Private_Files {
$upload_dir = wp_get_upload_dir();
$base_upload_url = preg_replace('/^https?:\/\//', '', $upload_dir['baseurl']);
$requested_uri = $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
$requested_uri = sanitize_text_field($_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']);
if ( strpos($requested_uri, $base_upload_url) === false ) {
// Not uploads

View File

@ -25,6 +25,7 @@ class Roles {
private function __construct() {
$this->meta_caps = (new \Tainacan\Entities\Metadatum())->get_capabilities();
$this->meta_section_caps = (new \Tainacan\Entities\Metadata_Section())->get_capabilities();
$this->filters_caps = (new \Tainacan\Entities\Filter())->get_capabilities();
$this->capabilities = [
@ -177,6 +178,7 @@ class Roles {
'scope' => 'collection',
'dependencies' => [
$this->meta_caps->read_private_posts,
$this->meta_section_caps->read_private_posts,
$this->filters_caps->read_private_posts
],
'supercaps' => [
@ -217,6 +219,17 @@ class Roles {
'tnc_col_all_edit_metadata',
]
],
'tnc_col_%d_edit_metasection' => [
'display_name' => __('Manage metadata sections', 'tainacan'),
'description' => __('Create/edit metadata section in this collection', 'tainacan'),
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_edit_metasection',
]
],
'tnc_col_%d_edit_filters' => [
'display_name' => __('Manage filters', 'tainacan'),
'description' => __('Create/edit filters in this collection', 'tainacan'),
@ -239,6 +252,17 @@ class Roles {
'tnc_col_all_delete_metadata',
]
],
'tnc_col_%d_delete_metasection' => [
'display_name' => __('Delete metadata sections', 'tainacan'),
'description' => __('Delete metadata section in this collection', 'tainacan'),
'scope' => 'collection',
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_delete_metasection',
]
],
'tnc_col_%d_delete_filters' => [
'display_name' => __('Delete filters', 'tainacan'),
'description' => __('Delete filters in this collection', 'tainacan'),
@ -264,6 +288,21 @@ class Roles {
'tnc_col_all_read_private_metadata',
]
],
'tnc_col_%d_read_private_metasection' => [
'display_name' => __('View private metadata sections', 'tainacan'),
'description' => __('Access private metadata section in this collection', 'tainacan'),
'scope' => 'collection',
'dependencies' => [
$this->meta_caps->read_private_posts, // e.g.: 'read_private_tainacan-metasection'
$this->meta_section_caps->read_private_posts,
],
'supercaps' => [
'manage_tainacan',
'manage_tainacan_collection_all',
'manage_tainacan_collection_%d',
'tnc_col_all_read_private_metasection',
]
],
'tnc_col_%d_read_private_filters' => [
'display_name' => __('View private filters', 'tainacan'),
'description' => __('Access private filters in this collection', 'tainacan'),
@ -552,6 +591,7 @@ class Roles {
if( in_array($requested_cap, [
$this->meta_caps->read_private_posts,
$this->meta_section_caps->read_private_posts,
$this->filters_caps->read_private_posts]
) && (
$user->has_cap('manage_tainacan') ||
@ -662,6 +702,9 @@ class Roles {
$meta_caps = new \Tainacan\Entities\Metadatum();
$meta_caps = $meta_caps->get_capabilities();
$meta_section_caps = new \Tainacan\Entities\Metadata_Section();
$meta_section_caps = $meta_section_caps->get_capabilities();
$filters_caps = new \Tainacan\Entities\Filter();
$filters_caps = $filters_caps->get_capabilities();
@ -699,6 +742,23 @@ class Roles {
$filters_caps->read_private_posts
];
$edit_meta_section = [
$meta_section_caps->edit_posts,
$meta_section_caps->edit_others_posts,
$meta_section_caps->publish_posts,
$meta_section_caps->delete_posts,
$meta_section_caps->delete_private_posts,
$meta_section_caps->delete_published_posts,
$meta_section_caps->delete_others_posts,
$meta_section_caps->edit_private_posts,
$meta_section_caps->edit_published_posts,
$meta_section_caps->create_posts,
];
$read_private_meta_section = [
$meta_section_caps->read_private_posts
];
if ( !is_array( $args ) || !array_key_exists( 0, $args ) ) {
return $caps;
}
@ -713,6 +773,19 @@ class Roles {
foreach ($caps as $i => $c) {
// Handle edit metadata section
if ( in_array($c, $edit_meta_section) ) {
if (is_numeric($object)) {
$object = tainacan_metadata_sections()->fetch ( (int) $object );
}
if ( $object instanceof \Tainacan\Entities\Metadata_Section ) {
if ( is_numeric($object->get_collection_id()) ) {
unset($caps[$i]);
$caps[] = 'tnc_col_' . $object->get_collection_id() . '_' . $action. '_metasection';
}
}
}
// Handle edit metadata
if ( in_array($c, $edit_meta) ) {
if (is_numeric($object)) {
@ -755,6 +828,19 @@ class Roles {
foreach ($caps as $i => $c) {
// Handle read private metadata section
if ( in_array($c, $read_private_meta_section) ) {
if (is_numeric($object)) {
$object = tainacan_metadata_sections()->fetch ( (int) $object );
}
if ( $object instanceof \Tainacan\Entities\Metadata_Section ) {
if ( is_numeric($object->get_collection_id()) ) {
unset($caps[$i]);
$caps[] = 'tnc_col_' . $object->get_collection_id() . '_read_private_metasection';
}
}
}
// Handle read private metadata
if ( in_array($c, $read_private_meta) ) {
if (is_numeric($object)) {

View File

@ -28,6 +28,7 @@ class Collection extends Entity {
$default_view_mode,
$enabled_view_modes,
$metadata_order,
$metadata_section_order,
$filters_order,
$enable_cover_page,
$cover_page_id,
@ -405,12 +406,21 @@ class Collection extends Entity {
/**
* Get collection metadata ordination
*
* @return string
* @return Object | string
*/
function get_metadata_order() {
return $this->get_mapped_property( 'metadata_order' );
}
/**
* Get collection metadata section ordination
*
* @return Array | Object | string
*/
function get_metadata_section_order() {
return $this->get_mapped_property( 'metadata_section_order' );
}
/**
* Get enable cover page attribute
*
@ -594,6 +604,17 @@ class Collection extends Entity {
return $this->get_mapped_property( 'submission_use_recaptcha' );
}
/**
* Get the default metadata section properties.
*
* @param [string] $value
*
* @return void
*/
function get_default_metadata_section_properties( ) {
return $this->get_mapped_property( 'default_metadata_section_properties' );
}
/**
* Set the collection name
*
@ -721,6 +742,24 @@ class Collection extends Entity {
$this->set_mapped_property( 'metadata_order', $value );
}
/**
* Set collection metadata section ordination
*
* @param [string] $value
*
* @return void
*/
function set_metadata_section_order( $value ) {
if( !empty($value) ) {
$metadata_order = array( );
foreach($value as $section) {
$metadata_order = array_merge($metadata_order, $section['metadata_order']);
}
$this->set_metadata_order($metadata_order);
}
$this->set_mapped_property( 'metadata_section_order', $value );
}
/**
* Set collection filters ordination
*
@ -838,6 +877,17 @@ class Collection extends Entity {
return $this->set_mapped_property( 'submission_use_recaptcha', $value);
}
/**
* Set the default metadata section properties.
*
* @param [string] $value
*
* @return void
*/
function set_default_metadata_section_properties( $value ) {
return $this->set_mapped_property( 'default_metadata_section_properties', $value);
}
/**
* Validate Collection
*
@ -857,6 +907,20 @@ class Collection extends Entity {
return false;
}
$metadata_section_order = $this->get_metadata_section_order();
if ( isset($metadata_section_order['metadata_section_order']) ) {
$section_order = $metadata_section_order['metadata_section_order'];
$metadata_order = $this->get_metadata_order();
$order_general = array();
foreach($section_order as $section) {
$order_general = array_merge($order_general, $section['metadata_order']);
}
if( count($order_general) != count($metadata_order) ) {
return false;
}
}
return parent::validate();
}

View File

@ -500,6 +500,9 @@ class Item_Metadata_Entity extends Entity {
$one_filled = true;
if ($this->is_collection_key()) {
if ( !isset($dupe_array[$val]) ) {
$dupe_array[$val] = 1;
} else
if (++$dupe_array[$val] > 1) {
$this->add_error( 'key_exists', sprintf( __('%s is a collection key and there is another item with the same value', 'tainacan'), $metadatum->get_name() ) );
return false;
@ -553,7 +556,31 @@ class Item_Metadata_Entity extends Entity {
if ($this->is_collection_key()) {
$Tainacan_Items = \Tainacan\Repositories\Items::get_instance();
if($metadatum->get_parent()) {
$patent_metadatum = \tainacan_metadata()->fetch( $metadatum->get_parent(), 'OBJECT' );
if($patent_metadatum->is_multiple()) {
global $wpdb;
$test = get_post_meta( $item->get_id(), $this->metadatum->get_id(), true);
$rows = $wpdb->get_results(
$wpdb->prepare( "SELECT * FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s AND meta_value = %s AND meta_id <> %d",
$item->get_id(), $this->metadatum->get_id(), $value, $this->get_meta_id()),
ARRAY_A );
if ( is_array( $rows ) && count($rows) > 0 ) {
if( !$this->get_meta_id() ) {
$current_meta_value = array_column($rows, "meta_value");
if ( count($current_meta_value) !== count(array_unique($current_meta_value)) ) {
// translators: %s = metadatum name. ex: Register ID is a collection key and there is another item with the same value
$this->add_error( 'key_exists', sprintf( __('%s is a collection key and there is another item with the same value', 'tainacan'), $metadatum->get_name() ) );
return false;
}
} else {
// translators: %s = metadatum name. ex: Register ID is a collection key and there is another item with the same value
$this->add_error( 'key_exists', sprintf( __('%s is a collection key and there is another item with the same value', 'tainacan'), $metadatum->get_name() ) );
return false;
}
}
}
}
$test = $Tainacan_Items->fetch([
'meta_query' => [
[

View File

@ -539,10 +539,8 @@ class Item extends Entity {
*
* @type bool $hide_empty Wether to hide or not metadata the item has no value to
* Default: true
*
* @type string $empty_value_message Message string to display if $hide_empty is false and there is not metadata value.
* Default: ''
*
* @type bool $display_slug_as_class Show metadata slug as a class in the div before the metadata block
* Default: false
* @type string $before String to be added before each metadata block
@ -564,48 +562,57 @@ class Item extends Entity {
*/
public function get_metadata_as_html($args = array()) {
$Tainacan_Item_Metadata = \Tainacan\Repositories\Item_Metadata::get_instance();
$Tainacan_Metadata = \Tainacan\Repositories\Metadata::get_instance();
$return = '';
$defaults = array(
'metadata' => null,
'metadata__in' => null,
'metadata__not_in' => null,
'exclude_title' => false,
'exclude_description' => false,
'exclude_core' => false,
'hide_empty' => true,
'empty_value_message' => '',
'metadata' => null,
'metadata__in' => null,
'metadata__not_in' => null,
'exclude_title' => false,
'exclude_description' => false,
'exclude_core' => false,
'hide_empty' => true,
'empty_value_message' => '',
'display_slug_as_class' => false,
'before' => '<div class="metadata-type-$type $id">',
'after' => '</div>',
'before_title' => '<h3>',
'after_title' => '</h3>',
'before_value' => '<p>',
'after_value' => '</p>',
'before' => '<div class="metadata-type-$type" $id>',
'after' => '</div>',
'before_title' => '<h3>',
'after_title' => '</h3>',
'before_value' => '<p>',
'after_value' => '</p>'
);
$args = wp_parse_args($args, $defaults);
$item_metadata = array();
if (!is_null($args['metadata'])) {
// If a single metadata is passed, we use it instead of fetching more
if ( !is_null($args['metadata']) ) {
$metadatum = $args['metadata'];
$metadatum_object = null;
// A metadatum object was passed
if ( $metadatum instanceof \Tainacan\Entities\Metadatum ) {
$metadatum_object = $metadatum;
// A metadatum ID was passed
} elseif ( is_int($metadatum) ) {
$metadatum_object = $Tainacan_Metadata->fetch($metadatum);
// A metadatum slug was passed
} elseif ( is_string($metadatum) ) {
$query = $Tainacan_Metadata->fetch(['slug' => $metadatum], 'OBJECT');
if ( is_array($query) && sizeof($query) == 1 && isset($metadatum[0])) {
$metadatum_object = $metadatum[0];
}
}
if ( $metadatum_object instanceof \Tainacan\Entities\Metadatum ) {
// Some checks to see if things are really ok
if ( !($metadatum instanceof \Tainacan\Entities\Metadatum) ) {
return $return;
} else {
// Makes sure the current Metadatum is desired
if ( is_array($args['metadata__not_in'])
&& (
in_array($metadatum_object->get_slug(), $args['metadata__not_in']) ||
@ -614,114 +621,211 @@ class Item extends Entity {
) {
return $return;
}
}
$mto = $metadatum_object->get_metadata_type_object();
$item_meta = new \Tainacan\Entities\Item_Metadata_Entity($this, $metadatum_object);
if ($item_meta->has_value() || !$args['hide_empty']) {
$before = str_replace('$type', $mto->get_slug(), $args['before']);
if ($args['display_slug_as_class']) {
$before = str_replace('$id', 'metadata-slug-'.$item_meta->get_metadatum()->get_slug() , $before);
// Add it to the array which will be looped bellow
$item_metadata[] = new \Tainacan\Entities\Item_Metadata_Entity($this, $metadatum_object);
// If not single metadatum is passed, we query them
} else {
// Build query args ready to be passed to the API fetch
$query_args = [];
$post__in = [];
$post__not_in = [];
$post__name_in = [];
if (is_array($args['metadata__in'])) {
$post__in[] = -1; // If metadata__in is an empty array, this forces empty result
foreach ($args['metadata__in'] as $meta) {
if (is_numeric($meta)) {
$post__in[] = $meta;
} elseif (is_string($meta)) {
$post__name_in[] = $meta;
}
else {
$before = str_replace(' $id', '', $before);
}
}
if (is_array($args['metadata__not_in'])) {
foreach ($args['metadata__not_in'] as $meta) {
if (is_integer($meta)) {
$post__not_in[] = $meta;
}
$return .= $before;
$return .= $args['before_title'] . $metadatum_object->get_name() . $args['after_title'];
$return .= $args['before_value'] . ( $item_meta->has_value() ? $item_meta->get_value_as_html() : $args['empty_value_message'] ) . $args['after_value'];
$return .= $args['after'];
}
}
return $return;
}
$query_args = [];
$post__in = [];
$post__not_in = [];
$post__name_in = [];
if (is_array($args['metadata__in'])) {
$post__in[] = -1; // If metadata__in is an empty array, this forces empty result
foreach ($args['metadata__in'] as $meta) {
if (is_numeric($meta)) {
$post__in[] = $meta;
} elseif (is_string($meta)) {
$post__name_in[] = $meta;
}
if (sizeof($post__in) > 0) {
$query_args['post__in'] = $post__in;
}
}
if (is_array($args['metadata__not_in'])) {
foreach ($args['metadata__not_in'] as $meta) {
if (is_integer($meta)) {
$post__not_in[] = $meta;
}
if (sizeof($post__not_in) > 0) {
$query_args['post__not_in'] = $post__not_in;
}
if (sizeof($post__name_in) > 0) {
$query_args['post__name_in'] = $post__name_in;
}
// Get the item metadata objects from the item repository
$item_metadata = $this->get_metadata($query_args);
}
if (sizeof($post__in) > 0) {
$query_args['post__in'] = $post__in;
}
if (sizeof($post__not_in) > 0) {
$query_args['post__not_in'] = $post__not_in;
}
if (sizeof($post__name_in) > 0) {
$query_args['post__name_in'] = $post__name_in;
}
// Loop item metadata to print their "values" as html
$metadatum_index = 0;
foreach ( $item_metadata as $item_metadatum ) {
// Gets the metadata type object to perform some checks
$metadata_type_object = $item_metadatum->get_metadatum()->get_metadata_type_object();
$metadata = $this->get_metadata($query_args);
foreach ( $metadata as $item_meta ) {
$fto = $item_meta->get_metadatum()->get_metadata_type_object();
if ( $fto->get_core() ) {
// Core metadata may not be desired as they may be displayed differently
if ( $metadata_type_object->get_core() ) {
if ( $args['exclude_core'] ) {
continue;
} elseif ( $args['exclude_title'] && $fto->get_related_mapped_prop() == 'title' ) {
} elseif ( $args['exclude_title'] && $metadata_type_object->get_related_mapped_prop() == 'title' ) {
continue;
} elseif ( $args['exclude_description'] && $fto->get_related_mapped_prop() == 'description' ) {
} elseif ( $args['exclude_description'] && $metadata_type_object->get_related_mapped_prop() == 'description' ) {
continue;
}
}
if ($item_meta->has_value() || !$args['hide_empty']) {
$before = str_replace('$type', $fto->get_slug(), $args['before']);
if ($args['display_slug_as_class']) {
$before = str_replace('$id', 'metadata-slug-'.$item_meta->get_metadatum()->get_slug() , $before);
}
else {
$before = str_replace(' $id', '', $before);
}
$return .= $before;
$return .= $args['before_title'] . $item_meta->get_metadatum()->get_name() . $args['after_title'];
$return .= $args['before_value'] . ( $item_meta->has_value() ? $item_meta->get_value_as_html() : $args['empty_value_message'] ) . $args['after_value'];
$return .= $args['after'];
// Get the metadatum representation in html, with its label and value
$return .= $this->get_item_metadatum_as_html($item_metadatum, $args, $metadatum_index);
}
$metadatum_index++;
}
return $return;
// Returns the html content created by the function
return wp_kses_tainacan($return);
}
/**
* Return a single item metadata as a HTML string to be used as output.
*
* Each metadata is a label with the metadatum name and the value.
*
* This function expects a $item_metadatum object. For a more generic approach, check the get_metadata_as_html function
*
* @param object $item_metadatum The Item Metadatum object
* @param array|string $args {
* Optional. Array or string of arguments.
*
* @type bool $hide_empty Wether to hide or not metadata the item has no value to
* Default: true
* @type string $empty_value_message Message string to display if $hide_empty is false and there is not metadata value.
* Default: ''
* @type bool $display_slug_as_class Show metadata slug as a class in the div before the metadata block
* Default: false
* @type string $before String to be added before each metadata block
* Default '<div class="metadata-type-$type">' where $type is the metadata type slug
* @type string $after String to be added after each metadata block
* Default '</div>'
* @type string $before_title String to be added before each metadata title
* Default '<h3>'
* @type string $after_title String to be added after each metadata title
* Default '</h3>'
* @type string $before_value String to be added before each metadata value
* Default '<p>'
* @type string $after_value String to be added after each metadata value
* Default '</p>'
* }
* @param int $section_index The Metadatum index, if passed from an array
*
* @return string The HTML output
*/
public function get_item_metadatum_as_html($item_metadatum, $args = array(), $metadatum_index = null) {
$return = '';
$defaults = array(
'hide_empty' => true,
'empty_value_message' => '',
'display_slug_as_class' => false,
'before' => '<div class="metadata-type-$type" $id>',
'after' => '</div>',
'before_title' => '<h3>',
'after_title' => '</h3>',
'before_value' => '<p>',
'after_value' => '</p>'
);
$args = wp_parse_args($args, $defaults);
if ($item_metadatum->has_value() || !$args['hide_empty']) {
// Gets the metadata type object to use it if we need the slug
$metadata_type_object = $item_metadatum->get_metadatum()->get_metadata_type_object();
// Get metadatum wrapper tag.
$before = str_replace('$type', $metadata_type_object->get_slug(), $args['before']);
// Adds class with slug and adds metadatum id
if ($args['display_slug_as_class']) {
if ( !strpos($before, 'class="') ) {
$before = str_replace('>', ' class="metadata-slug-'. $item_metadatum->get_metadatum()->get_slug() . '">', $before);
} else
$before = str_replace('class="', 'class="metadata-slug-'. $item_metadatum->get_metadatum()->get_slug() . ' ', $before);
}
$before = str_replace('$id', ' id="metadata-id-' . $item_metadatum->get_metadatum()->get_id() . '"', $before);
// Let theme authors tweak the wrapper opener
$before = apply_filters( 'tainacan-get-item-metadatum-as-html-before', $before, $item_metadatum );
$before = apply_filters( 'tainacan-get-item-metadatum-as-html-before--type-' . $item_metadatum->get_metadatum()->get_metadata_type(), $before, $item_metadatum );
$before = apply_filters( 'tainacan-get-item-metadatum-as-html-before--id-' . $item_metadatum->get_metadatum()->get_id(), $before, $item_metadatum );
if ( is_numeric($metadatum_index) ) {
$before = apply_filters( 'tainacan-get-item-metadatum-as-html-before--index-' . $metadatum_index, $before, $item_metadatum );
}
// Renders the metadatum opener
$return .= $before;
// Renders the metadatum name
$metadatum_title_before = $args['before_title'];
$metadatum_title_before = apply_filters( 'tainacan-get-item-metadatum-as-html-before-title', $metadatum_title_before, $item_metadatum );
$metadatum_title_after = $args['after_title'];
$metadatum_title_after = apply_filters( 'tainacan-get-item-metadatum-as-html-after-title', $metadatum_title_after, $item_metadatum );
$return .= $metadatum_title_before . $item_metadatum->get_metadatum()->get_name() . $metadatum_title_after;
// Renders the metadatum value
$metadatum_value_before = $args['before_value'];
$metadatum_value_before = apply_filters( 'tainacan-get-item-metadatum-as-html-before-value', $metadatum_value_before, $item_metadatum );
$metadatum_value_after = $args['after_value'];
$metadatum_value_after = apply_filters( 'tainacan-get-item-metadatum-as-html-after-value', $metadatum_value_after, $item_metadatum );
$return .= $metadatum_value_before . ( $item_metadatum->has_value() ? $item_metadatum->get_value_as_html() : $args['empty_value_message'] ) . $metadatum_value_after;
$after = $args['after'];
// Let theme authors tweak the wrapper closer
if ( is_numeric($metadatum_index) ) {
$after = apply_filters( 'tainacan-get-item-metadatum-as-html-after--index-' . $metadatum_index, $after, $item_metadatum );
}
$after = apply_filters( 'tainacan-get-item-metadatum-as-html-after--id-' . $item_metadatum->get_metadatum()->get_id(), $after, $item_metadatum );
$after = apply_filters( 'tainacan-get-item-metadatum-as-html-after--type-' . $item_metadatum->get_metadatum()->get_metadata_type(), $after, $item_metadatum );
$after = apply_filters( 'tainacan-get-item-metadatum-as-html-after', $after, $item_metadatum );
// Closes the wrapper
$return .= $after;
}
// Returns the html content created by the function
return $return;
}
/**
* Gets the document as a html. May be a text, link, iframe, image, audio...
*/
public function get_document_as_html($img_size = 'large') {
$type = $this->get_document_type();
$document_options = $this->get_document_options();
$output = '';
if ( $type == 'url' ) {
global $wp_embed;
$_embed = $wp_embed->autoembed($this->get_document());
$url = $this->get_document();
if ( $_embed == $url ) {
if ( esc_url($_embed) == esc_url($url) ) {
if ( $document_options && isset($document_options['forced_iframe']) && $document_options['forced_iframe'] === true ) {
// URL points to an image file
if (isset($document_options['is_image']) && $document_options['is_image'] === true) {
$_embed = sprintf('<a href="%s" target="blank"><img src="%s" /></a>', $url, $url);
@ -761,7 +865,7 @@ class Item extends Entity {
$embed = $wp_embed->autoembed($url);
if ( $embed == $url ) {
if ( esc_url($embed) == esc_url($url) ) {
$output .= sprintf("<a href='%s' target='blank'>%s</a>", $url, $url);
} else {
$tainacan_embed = \Tainacan\Embed::get_instance();
@ -772,12 +876,11 @@ class Item extends Entity {
}
return apply_filters("tainacan-item-get-document-as-html", $output, $img_size, $this);
return apply_filters("tainacan-item-get-document-as-html", wp_kses_tainacan($output), $img_size, $this);
}
/**
* Gets the attachment as a html, can be iframe, image, audio...
* Gets the attachment as a html. May be an iframe, image, audio...
*/
public function get_attachment_as_html($attachment, $img_size = 'large') {
@ -798,7 +901,7 @@ class Item extends Entity {
$embed = $wp_embed->autoembed($url);
if ( $embed == $url ) {
if ( esc_url($embed) == esc_url($url) ) {
$output .= sprintf("<a href='%s' target='blank'>%s</a>", $url, $url);
} else {
$tainacan_embed = \Tainacan\Embed::get_instance();
@ -806,8 +909,7 @@ class Item extends Entity {
$output .= $embed;
}
}
return $output;
return wp_kses_tainacan($output);
}
@ -841,7 +943,7 @@ class Item extends Entity {
}
}
return $link;
return esc_url($link);
}
/**
@ -854,4 +956,343 @@ class Item extends Entity {
$related_items = $Tainacan_Items->fetch_related_items($this, $args);
return $related_items;
}
/**
* Return the item metadata sections as a HTML string to be used as output.
*
* Each metadata section is a label with the list of its metadata name and value.
*
* If an ID, a slug or a Tainacan\Entities\Metadata_Section object is passed in the 'metadata_section' argument, it returns only one metadata section, otherwise
* it returns all metadata section
*
* @param array|string $args {
* Optional. Array or string of arguments.
*
* @type mixed $metadata_section Metadatum object, ID or slug to retrieve only one metadatum. empty returns all metadata_sections
*
* @type array $metadata_sections__in Array of metadata_sections IDs or Slugs to be retrieved. Default none
*
* @type array $metadata_sections__not_in Array of metadata_sections IDs (slugs not accepted) to excluded. Default none
*
* @type bool $hide_name Do not display the Metadata Section name. Default false
*
* @type bool $hide_description Do not display the Metadata Section description. Default true
*
* @type bool $hide_empty Wether to hide or not metadata sections if there are no metadata list or they are empty
* Default: true
* @type string $empty_metadata_list_message Message string to display if $hide_empty is false and there is not metadata section metadata list.
* Default: ''
* @type string $before String to be added before each metadata section block
* Default '<section class="metadata-section-slug-$slug" id="$id">'
* @type string $after String to be added after each metadata section block
* Default '</section>'
* @type string $before_name String to be added before each metadata section name
* Default '<h2 id="metadata-section-$slug">'
* @type string $after_name String to be added after each metadata section name
* Default '</h2>'
* @type string $before_description String to be added before each metadata section description
* Default '<p>'
* @type string $after_description String to be added after each metadata section description
* Default '</p>'
* @type string $before_metadata_list String to be added before each metadata section inner metadata list
* Default '<div class="metadata-section__metadata-list" aria-labelledby="metadata-section-$slug">'
* @type string $after_metadata_list String to be added after each metadata section inner metadata list
* Default '</div>'
* @type array $metadata_list_args Arguments to be passed to the get_metadata_as_html function when calling section metadata
* }
*
* @return string The HTML output
*/
public function get_metadata_sections_as_html($args = array()) {
$Tainacan_Metadata_Sections = \Tainacan\Repositories\Metadata_Sections::get_instance();
$return = '';
$defaults = array(
'metadata_section' => null,
'metadata_sections__in' => null,
'metadata_sections__not_in' => null,
'hide_name' => false,
'hide_description' => true,
'hide_empty' => true,
'empty_metadata_list_message' => '',
'before' => '<section class="metadata-section-slug-$slug" id="$id">',
'after' => '</section>',
'before_name' => '<h2 id="metadata-section-$slug">',
'after_name' => '</h2>',
'before_metadata_list' => '<div class="metadata-section__metadata-list" aria-labelledby="metadata-section-$slug">',
'after_metadata_list' => '</div>',
'metadata_list_args' => []
);
$args = wp_parse_args($args, $defaults);
$metadata_sections = array();
// If a single metadata section is passed, we use it instead of fetching more
if ( !is_null($args['metadata_section']) ) {
$metadata_section = $args['metadata_section'];
$metadata_section_object = null;
// A metadata section object was passed
if ( $metadata_section instanceof \Tainacan\Entities\Metadata_Section ) {
$metadata_section_object = $metadata_section;
// A metadata section ID was passed
} elseif ( is_int($metadata_section) ) {
$metadata_section_object = $Tainacan_Metadata_Sections->fetch($metadata_section);
// A metadata section slug was passed
} elseif ( is_string($metadata_section) ) {
$query = $Tainacan_Metadata_Sections->fetch(['slug' => $metadata_section], 'OBJECT');
if ( is_array($query) && sizeof($query) == 1 && isset($metadata_section[0]) ) {
$metadata_section_object = $metadata_section[0];
}
}
// Some checks to see if things are really ok
if ( !($metadata_section_object instanceof \Tainacan\Entities\Metadata_Section) ) {
return $return;
} else {
// Makes sure the current Metadata Section is desired
if ( is_array($args['metadata_sections__not_in'])
&& (
in_array($metadata_section_object->get_slug(), $args['metadata_sections__not_in']) ||
in_array($metadata_section_object->get_id(), $args['metadata_sections__not_in'])
)
) {
return $return;
}
}
// Add it to the array which will be looped bellow
$metadata_sections[] = $metadata_section_object;
// If not single metadata section is passed, we query them
} else {
// Build query args ready to be passed to the API fetch
$query_args = [];
$post__in = [];
$post__not_in = [];
$post__name_in = [];
if (is_array($args['metadata_sections__in'])) {
$post__in[] = -1; // If metadata_sections__in is an empty array, this forces empty result
foreach ($args['metadata_sections__in'] as $metadata_section) {
if (is_numeric($metadata_section) || $metadata_section === \Tainacan\Entities\Metadata_Section::$default_section_slug) {
$post__in[] = $metadata_section;
} elseif (is_string($metadata_section)) {
$post__name_in[] = $metadata_section;
}
}
}
if (is_array($args['metadata_sections__not_in'])) {
foreach ($args['metadata_sections__not_in'] as $metadata_section) {
if (is_integer($metadata_section) || $metadata_section === \Tainacan\Entities\Metadata_Section::$default_section_slug) {
$post__not_in[] = $metadata_section;
}
}
}
if (sizeof($post__in) > 0) {
$query_args['post__in'] = $post__in;
}
if (sizeof($post__not_in) > 0) {
$query_args['post__not_in'] = $post__not_in;
}
if (sizeof($post__name_in) > 0) {
$query_args['post__name_in'] = $post__name_in;
}
// Get metadata section objects from the metadata sections repository
$metadata_sections = $Tainacan_Metadata_Sections->fetch_by_collection($this->get_collection(), $query_args);
}
// Loop metadata sections to print their "values" as html
$section_index = 0;
foreach ( $metadata_sections as $metadata_section_object ) {
$return .= $this->get_metadata_section_as_html($metadata_section_object, $args, $section_index);
$section_index++;
}
// Returns the html content created by the function
return $return;
}
/**
* Return a single item metadata section as a HTML string to be used as output.
*
* A metadata section is a label with the list of its metadata name and value.
*
* This function expects a $metadata_section object. For a more generic approach, check the get_metadata_sections_as_html function
*
* @param object $metadata_section The Metadata Section object
* @param array|string $args {
* Optional. Array or string of arguments.
*
* @type bool $hide_name Do not display the Metadata Section name. Default false
*
* @type bool $hide_description Do not display the Metadata Section description. Default true
*
* @type bool $hide_empty Wether to hide or not metadata sections if there are no metadata list or they are empty
* Default: true
* @type string $empty_metadata_list_message Message string to display if $hide_empty is false and there is not metadata section metadata list.
* Default: ''
* @type string $before String to be added before each metadata section block
* Default '<section class="metadata-section-slug-$slug" id="$id">'
* @type string $after String to be added after each metadata section block
* Default '</section>'
* @type string $before_name String to be added before each metadata section name
* Default '<h2 id="metadata-section-$slug">'
* @type string $after_name String to be added after each metadata section name
* Default '</h2>'
* @type string $before_description String to be added before each metadata section description
* Default '<p>'
* @type string $after_description String to be added after each metadata section description
* Default '</p>'
* @type string $before_metadata_list String to be added before each metadata section inner metadata list
* Default '<div class="metadata-section__metadata-list" aria-labelledby="metadata-section-$slug">'
* @type string $after_metadata_list String to be added after each metadata section inner metadata list
* Default '</div>'
*
* @type array $metadata_list_args Arguments to be passed to the get_metadata_as_html function when calling section metadata
* }
* @param int $section_index The Metadata Section index, if passed from an array
*
* @return string The HTML output
*/
public function get_metadata_section_as_html($metadata_section, $args = array(), $section_index = null) {
$return = '';
$defaults = array(
'hide_name' => false,
'hide_description' => true,
'hide_empty' => true,
'empty_metadata_list_message' => '',
'before' => '<section class="metadata-section-slug-$slug" id="$id">',
'after' => '</section>',
'before_name' => '<h2 id="metadata-section-$slug">',
'after_name' => '</h2>',
'before_metadata_list' => '<div class="metadata-section__metadata-list" aria-labelledby="metadata-section-$slug">',
'after_metadata_list' => '</div>',
'metadata_list_args' => []
);
$args = wp_parse_args($args, $defaults);
// Gets the metadata section inner metadata list
$metadata_section_metadata_list = $metadata_section->get_metadata_object_list();
$has_metadata_list = (is_array($metadata_section_metadata_list) && count($metadata_section_metadata_list) > 0 );
if ( $has_metadata_list || !$args['hide_empty'] ) {
// Slug and ID are used in numerous situations
$section_slug = $metadata_section->get_slug();
$section_id = $metadata_section->get_id();
// Get section wrapper tag
$before = $args['before'];
$before = str_replace('$id', $section_id, $before);
$before = str_replace('$slug', $section_slug, $before);
// Let theme authors tweak the wrapper opener
$before = apply_filters( 'tainacan-get-metadata-section-as-html-before', $before, $metadata_section );
$before = apply_filters( 'tainacan-get-metadata-section-as-html-before--id-' . $section_id, $before, $metadata_section );
if ( is_numeric($section_index) && $section_index >= 0 ) {
$before = apply_filters( 'tainacan-get-metadata-section-as-html-before--index-' . $section_index, $before, $metadata_section );
}
// Renders the wrapper opener
$return .= $before;
// Adds section label (name)
if ( !$args['hide_name'] ) {
// Get section name wrapper
$before_name = $args['before_name'];
$before_name = str_replace('$id', $section_id, $before_name);
$before_name = str_replace('$slug', $section_slug, $before_name);
// Let theme authors tweak the name wrapper
$before_name = apply_filters( 'tainacan-get-metadata-section-as-html-before-name', $before_name, $metadata_section );
$before_name = apply_filters( 'tainacan-get-metadata-section-as-html-before-name--id-' . $section_id, $before_name, $metadata_section );
if ( is_numeric($section_index) && $section_index >= 0 ) {
$before_name = apply_filters( 'tainacan-get-metadata-section-as-html-before-name--index-' . $section_index, $before_name, $metadata_section );
}
// Get section name closer
$after_name = $args['after_name'];
// Let theme authors tweak the name wrapper
$after_name = apply_filters( 'tainacan-get-metadata-section-as-html-after-name', $after_name, $metadata_section );
$after_name = apply_filters( 'tainacan-get-metadata-section-as-html-after-name--id-' . $section_id, $after_name, $metadata_section );
if ( is_numeric($section_index) && $section_index >= 0 ) {
$after_name = apply_filters( 'tainacan-get-metadata-section-as-html-after-name--index-' . $section_index, $after_name, $metadata_section );
}
// Renders the metadata section name
$return .= $before_name . $metadata_section->get_name() . $after_name;
}
// Adds section description
if ( !$args['hide_description'] ) {
$return .= $args['before_description'] . $metadata_section->get_description() . $args['after_description'];
}
// Gets the section metadata list wrapper
$before_metadata_list = $args['before_metadata_list'];
$before_metadata_list = str_replace('$id', $section_id, $before_metadata_list);
$before_metadata_list = str_replace('$slug', $section_slug, $before_metadata_list);
// Let theme authors tweak the metadata list wrapper
$before_description = isset($args['before_description']) ? $args['before_description'] : '';
$before_description = apply_filters( 'tainacan-get-metadata-section-as-html-before-metadata-list', $before_description, $metadata_section );
$before_description = apply_filters( 'tainacan-get-metadata-section-as-html-before-metadata-list--id-' . $section_id, $before_description, $metadata_section );
if ( is_numeric($section_index) && $section_index >= 0 ) {
$before_description = apply_filters( 'tainacan-get-metadata-section-as-html-before-metadata-list--index-' . $section_index, $before_description, $metadata_section );
}
// Renders the section metadata list wrapper
$return .= $before_metadata_list;
// Renders the section metadata list, using Items' get_metadata_as_html()
// Note that this is already escaped in the calling function
if ($has_metadata_list) {
foreach( $metadata_section_metadata_list as $metadata_object) {
$return .= $this->get_metadata_as_html( wp_parse_args($args['metadata_list_args'], [ 'metadata' => $metadata_object ]) );
}
} else {
$return .= $args['empty_metadata_list_message'];
}
// Gets the wrapper closer
$after_metadata_list = $args['after_metadata_list'];
// Let theme authors tweak the metadata list closer
$after_description = isset($args['after_description']) ? $args['after_description'] : '';
$after_description = apply_filters( 'tainacan-get-metadata-section-as-html-after-metadata-list', $after_description, $metadata_section );
$after_description = apply_filters( 'tainacan-get-metadata-section-as-html-after-metadata-list--id-' . $section_id, $after_description, $metadata_section );
if ( is_numeric($section_index) && $section_index >= 0 ) {
$after_description = apply_filters( 'tainacan-get-metadata-section-as-html-after-metadata-list--index-' . $section_index, $after_description, $metadata_section );
}
// Renders the section metadata list wrapper
$return .= $after_metadata_list;
// Gets the wrapper closer
$after = $args['after'];
// Let theme authors tweak the wrapper closer
if ( is_numeric($section_index) && $section_index >= 0 ) {
$after = apply_filters( 'tainacan-get-metadata-section-as-html-after--index-' . $section_index, $after, $metadata_section );
}
$after = apply_filters( 'tainacan-get-metadata-section-as-html-after--id-' . $section_id, $after, $metadata_section );
$after = apply_filters( 'tainacan-get-metadata-section-as-html-after', $after, $metadata_section );
// Closes the wrapper
$return .= $after;
}
// Returns the html content created by the function
return $return;
}
}

View File

@ -0,0 +1,185 @@
<?php
namespace Tainacan\Entities;
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
/**
* Represents the Entity Metadatum
*/
class Metadata_Section extends Entity {
// Collection getter and setter declared here
use \Tainacan\Traits\Entity_Collection_Relation;
static $post_type = 'tainacan-metasection';
static $default_section_slug = 'default_section';
protected
$name,
$slug,
$description,
$description_bellow_name;
/**
* {@inheritDoc}
* @see \Tainacan\Entities\Entity::repository
* @var string
*/
protected $repository = 'Metadata_Sections';
public $enabled_for_collection = true;
public function __toString() {
return apply_filters("tainacan-metadata-section-to-string", $this->get_name(), $this);
}
/**
* Get the entity ID
*
* @return integer
*/
public function get_id() {
$id = $this->get_mapped_property('id');
return isset($id) ? $id : static::$default_section_slug;
}
/**
* Return the metadata section name
*
* @return string
*/
function get_name() {
return $this->get_mapped_property('name');
}
/**
* Get metadata section slug
*
* @return string
*/
function get_slug() {
return $this->get_mapped_property('slug');
}
/**
* Return the metadata section description
*
* @return string
*/
function get_description() {
return $this->get_mapped_property('description');
}
/**
* Return the metadatum description_bellow_name
*
* @return string
*/
function get_description_bellow_name() {
return $this->get_mapped_property('description_bellow_name');
}
/**
* Return the metadata_list of section
*
* @return [int]
*/
function get_metadata_object_list($args = []) {
$tainacan_metadata_sections = \Tainacan\Repositories\Metadata_Sections::get_instance();
$metadata_section_id = $this->get_id();
if ($metadata_section_id == static::$default_section_slug)
return $tainacan_metadata_sections->get_default_section_metadata_object_list($this->get_collection(), $args);
return $tainacan_metadata_sections->get_metadata_object_list($this->get_id(), $args);
}
/**
* Set the metadata section name
*
* @param [string] $value
* @return void
*/
function set_name($value) {
$this->set_mapped_property('name', $value);
}
/**
* Set the metadata section slug
*
* If you dont set the metadata slug, it will be set automatically based on the name and
* following WordPress default behavior of creating slugs for posts.
*
* If you set the slug for an existing one, WordPress will append a number at the end of in order
* to make it unique (e.g slug-1, slug-2)
*
* @param [string] $value
* @return void
*/
function set_slug($value) {
$this->set_mapped_property('slug', $value);
}
/**
* Set metadata section description
*
* @param [string] $value The text description
* @return void
*/
function set_description($value) {
$this->set_mapped_property('description', $value);
}
/**
* Set metadatum description_bellow_name
*
* @param [string] $value If the description will be displayed bellow the name instead of inside a tooltip (yes/no)
* @return void
*/
function set_description_bellow_name($value) {
$this->set_mapped_property('description_bellow_name', $value);
}
/**
* Transient property used to store the status of the metadatum section for a particular collection
*
* Used by the API to tell front end when a metadatum section is disabled
*
*/
public function get_enabled_for_collection() {
return $this->enabled_for_collection;
}
public function set_enabled_for_collection($value) {
$this->enabled_for_collection = $value;
}
/**
* {@inheritdoc }
*
* Also validates the metadata, calling the validate_options callback of the Metadatum Type
*
* @return bool valid or not
* @throws \Exception
*/
public function validate() {
$no_errors = true;
$name = $this->get_name();
$collection = $this->get_collection();
if( empty($collection) ) {
$this->add_error($this->get_id(), __("collection is required", 'tainacan'));
$no_errors = false;
}
if ( !isset($name) ) {
$this->add_error($this->get_id(), __("name is required", 'tainacan'));
$no_errors = false;
}
if($no_errors) {
$this->set_as_valid();
}
return $no_errors;
}
}

View File

@ -24,7 +24,8 @@ class Metadatum extends Entity {
$mask,
$default_value,
$metadata_type,
$metadata_type_options;
$metadata_type_options,
$metadata_section_id;
// Collection getter and setter declared here
use \Tainacan\Traits\Entity_Collection_Relation;
@ -252,6 +253,15 @@ class Metadatum extends Entity {
return $this->get_mapped_property('semantic_uri');
}
/**
* Return the metadata_section_id
*
* @return string
*/
function get_metadata_section_id(){
return $this->get_mapped_property('metadata_section_id');
}
/**
* Set the metadatum name
*
@ -432,6 +442,20 @@ class Metadatum extends Entity {
function set_semantic_uri( $value ){
$this->set_mapped_property('semantic_uri', $value);
}
/**
* Set metadatum section ID for the metadatum
*
* @param [string] $value
* @return void
*/
function set_metadata_section_id($value) {
if( !is_array($value) ) {
$value = [$value];
}
return $this->set_mapped_property('metadata_section_id', $value);
}
/**
* Transient property used to store the status of the metadatum for a particular collection
@ -476,6 +500,15 @@ class Metadatum extends Entity {
return $this->get_required() === 'yes';
}
/**
* Return true if is repository level, else return false
*
* @return boolean
*/
function is_repository_level() {
return $this->get_collection_id() === 'default';
}
/**
* {@inheritdoc }
*

View File

@ -162,7 +162,7 @@ class CSV extends Exporter {
$last_id = get_post_meta( $item_id, '_user_edit_lastr', true );
if ( $last_id ) {
$last_user = get_userdata( $last_id );
return apply_filters( 'the_modified_author', $last_user->display_name );
return apply_filters( 'tainacan-the-modified-author', $last_user->display_name );
}
return "";
}
@ -312,7 +312,7 @@ class CSV extends Exporter {
</div>
</span>
<div class="control is-clearfix">
<input class="input" type="text" name="delimiter" maxlength="1" value="<?php echo $this->get_option('delimiter'); ?>">
<input class="input" type="text" name="delimiter" maxlength="1" value="<?php echo esc_attr($this->get_option('delimiter')); ?>">
</div>
</div>
@ -334,7 +334,7 @@ class CSV extends Exporter {
</div>
</span>
<div class="control is-clearfix">
<input class="input" type="text" name="multivalued_delimiter" value="<?php echo $this->get_option('multivalued_delimiter'); ?>">
<input class="input" type="text" name="multivalued_delimiter" value="<?php echo esc_attr($this->get_option('multivalued_delimiter')); ?>">
</div>
</div>

View File

@ -250,7 +250,7 @@ abstract class Exporter {
}
public function get_step_length_items() {
return apply_filters('exporter_step_length_items', 20, $this->get_current_step());
return apply_filters('tainacan-exporter-step-length-items', 20, $this->get_current_step());
}
public function set_current_collection_item($value) {

View File

@ -98,7 +98,7 @@ class Term_Exporter extends Exporter {
</div>
</span>
<div class="control is-clearfix">
<input class="input" type="text" name="delimiter" value="<?php echo $this->get_option('delimiter'); ?>">
<input class="input" type="text" name="delimiter" value="<?php echo esc_attr($this->get_option('delimiter')); ?>">
</div>
</div>
@ -127,7 +127,7 @@ class Term_Exporter extends Exporter {
$taxonomies = $Tainacan_Taxonomies->fetch( ['nopaging' => true], 'OBJECT' );
foreach( $taxonomies as $taxonomie) {
?>
<option value="<?php echo $taxonomie->get_db_identifier();?>"><?php echo $taxonomie->get_name() ?> </option>
<option value="<?php echo esc_attr($taxonomie->get_db_identifier());?>"><?php echo esc_attr($taxonomie->get_name()); ?> </option>
<?php
}
?>

View File

@ -148,7 +148,7 @@ class Exposers_Handler {
$type_responde = $exposer->rest_request_after_callbacks($response, $handler, $request);
if(self::request_has_url_param($request)) {
header(implode('', $response->get_headers()));
echo stripcslashes($response->get_data());
echo wp_kses_tainacan(stripcslashes($response->get_data()));
exit();
}
return $type_responde;

View File

@ -334,7 +334,7 @@ class CSV extends Importer {
</div>
</span>
<div class="control is-clearfix">
<input class="input" type="text" name="multivalued_delimiter" value="<?php echo $this->get_option('multivalued_delimiter'); ?>">
<input class="input" type="text" name="multivalued_delimiter" value="<?php echo esc_attr($this->get_option('multivalued_delimiter')); ?>">
</div>
</div>
</div>
@ -357,7 +357,7 @@ class CSV extends Importer {
</div>
</span>
<div class="control is-clearfix">
<input class="input" type="text" name="enclosure" value="<?php echo $this->get_option('enclosure'); ?>">
<input class="input" type="text" name="enclosure" value="<?php echo esc_attr($this->get_option('enclosure')); ?>">
</div>
</div>
</div>
@ -410,7 +410,7 @@ class CSV extends Importer {
</div>
</span>
<div class="control is-clearfix">
<input class="input" type="text" name="escape_empty_value" value="<?php echo $this->get_option('escape_empty_value'); ?>">
<input class="input" type="text" name="escape_empty_value" value="<?php echo esc_attr($this->get_option('escape_empty_value')); ?>">
</div>
</div>
</div>
@ -467,7 +467,7 @@ class CSV extends Importer {
</div>
</span>
<div class="control is-clearfix">
<input class="input" type="text" name="server_path" value="<?php echo $this->get_option('server_path'); ?>">
<input class="input" type="text" name="server_path" value="<?php echo esc_attr($this->get_option('server_path')); ?>">
</div>
<p class="help">
<strong><?php _e('Importing attachments', 'tainacan'); ?>: </strong><?php echo nl2br(__('Check the documentation to learn how to set up your .csv file correctly for importing files <a href="https://tainacan.github.io/tainacan-wiki/#/importers?id=importador-csv-items">on this link.</a>', 'tainacan')); ?>
@ -815,12 +815,16 @@ class CSV extends Importer {
if( (!empty( $itemMetadataArray ) || $special_columns) && $collection instanceof Entities\Collection ) {
$item->set_collection( $collection );
if( $item->validate() ) {
$insertedItem = $Tainacan_Items->insert( $item );
if ( !$updating_item ) {
if( $item->validate() ) {
$insertedItem = $Tainacan_Items->insert( $item );
} else {
$this->add_error_log( 'Error inserting Item Title: ' . $item->get_title() );
$this->add_error_log( $item->get_errors() );
return false;
}
} else {
$this->add_error_log( 'Error inserting Item Title: ' . $item->get_title() );
$this->add_error_log( $item->get_errors() );
return false;
$insertedItem = $item;
}
global $wpdb;
$wpdb->query( 'SET autocommit = 0;' );

View File

@ -188,7 +188,9 @@ class Flickr_Importer extends Importer {
$this->add_log('url ' . $api_url);
$json = json_decode(file_get_contents($api_url));
$response = wp_remote_get( $api_url );
$body = wp_remote_retrieve_body( $response );
$json = json_decode($body);
if( $json && isset($json->photoset) ){
return $json;
}
@ -203,7 +205,10 @@ class Flickr_Importer extends Importer {
$this->add_log('url ' . $api_url);
$json = json_decode(file_get_contents($api_url));
$response = wp_remote_get( $api_url );
$body = wp_remote_retrieve_body( $response );
$json = json_decode($body);
if( $json && isset($json->photos) ){
return $json;
@ -218,7 +223,9 @@ class Flickr_Importer extends Importer {
$this->add_log('url ' . $api_url);
$json = json_decode(file_get_contents($api_url));
$response = wp_remote_get( $api_url );
$body = wp_remote_retrieve_body( $response );
$json = json_decode($body);
if( $json && isset($json->photo) ){
return $json;
@ -428,8 +435,9 @@ class Flickr_Importer extends Importer {
. $id . $this->format;
$this->add_log('url ' . $api_url);
$json = json_decode(file_get_contents($api_url));
$response = wp_remote_get( $api_url );
$body = wp_remote_retrieve_body( $response );
$json = json_decode($body);
if( $json && isset($json->photo) ){
return $json;

View File

@ -921,7 +921,7 @@ abstract class Importer {
}
return [$parent_compound, $children_mapping];
}
$properties = array_filter( explode('|', $metadata_description) );
$properties = array_filter( explode('|', $metadata_description) );
if( is_array($properties) && count($properties) < 2 ){
$properties[1] = 'text';
@ -974,7 +974,7 @@ abstract class Importer {
} else {
$newMetadatum->set_metadata_type_options([
'taxonomy_id' => $inserted_tax->get_id(),
'allow_new_terms' => 'no',
'allow_new_terms' => 'yes',
'input_type' => 'tainacan-taxonomy-radio'
]);
}

View File

@ -125,7 +125,7 @@ class Test_Importer extends Importer {
</div>
</span>
<div class="control is-clearfix">
<input class="input" type="number" name="items_col_1" value="<?php echo $this->get_option('items_col_1'); ?>">
<input class="input" type="number" name="items_col_1" value="<?php echo esc_attr($this->get_option('items_col_1')); ?>">
</div>
</div>
</div>
@ -149,7 +149,7 @@ class Test_Importer extends Importer {
</div>
</span>
<div class="control is-clearfix">
<input class="input" type="number" name="additonal_metadata" value="<?php echo $this->get_option('additonal_metadata'); ?>">
<input class="input" type="number" name="additonal_metadata" value="<?php echo esc_attr($this->get_option('additonal_metadata')); ?>">
</div>
</div>
</div>
@ -204,7 +204,7 @@ class Test_Importer extends Importer {
</div>
</span>
<div class="control is-clearfix">
<input class="input" type="number" name="items_col_2" value="<?php echo $this->get_option('items_col_2'); ?>">
<input class="input" type="number" name="items_col_2" value="<?php echo esc_attr($this->get_option('items_col_2')); ?>">
</div>
</div>
</div>
@ -266,7 +266,7 @@ class Test_Importer extends Importer {
</div>
</span>
<div class="control is-clearfix">
<input class="input" type="text" name="keyword_images" value="<?php echo $this->get_option('keyword_images'); ?>">
<input class="input" type="text" name="keyword_images" value="<?php echo esc_attr($this->get_option('keyword_images')); ?>">
</div>
</div>
</div>
@ -290,7 +290,7 @@ class Test_Importer extends Importer {
</div>
</span>
<div class="control is-clearfix">
<input class="input" type="number" name="horizontal_image_size" value="<?php echo $this->get_option('horizontal_image_size'); ?>">
<input class="input" type="number" name="horizontal_image_size" value="<?php echo esc_attr($this->get_option('horizontal_image_size')); ?>">
</div>
</div>
@ -312,7 +312,7 @@ class Test_Importer extends Importer {
</div>
</span>
<div class="control is-clearfix">
<input class="input" type="number" name="vertical_image_size" value="<?php echo $this->get_option('vertical_image_size'); ?>">
<input class="input" type="number" name="vertical_image_size" value="<?php echo esc_attr($this->get_option('vertical_image_size')); ?>">
</div>
</div>
</div>
@ -649,8 +649,10 @@ class Test_Importer extends Importer {
$keyword = ( $this->get_option('keyword_images') ) ? $this->get_option('keyword_images') : '';
$url = "https://loremflickr.com/$horizontal_size/$vertical_size/$keyword";
$response = wp_remote_get( $url );
$content = wp_remote_retrieve_body( $response );
$id = $TainacanMedia->insert_attachment_from_blob(file_get_contents($url), time() . '.jpg', $inserted_item->get_id());
$id = $TainacanMedia->insert_attachment_from_blob($content, time() . '.jpg', $inserted_item->get_id());
if(!$id){
$this->add_error_log('Error in imported URL ' . $url);

View File

@ -231,7 +231,9 @@ class Youtube_Importer extends Importer {
$api_url = 'https://www.googleapis.com/youtube/v3/channels?part=statistics,snippet,contentDetails&id='
. $id . '&key=' . $api_key;
$json = json_decode(file_get_contents($api_url));
$response = wp_remote_get( $api_url );
$body = wp_remote_retrieve_body( $response );
$json = json_decode($body);
if( $json && isset($json->items) ){
$item = $json->items[0];
@ -239,7 +241,9 @@ class Youtube_Importer extends Importer {
. $pageToken . '&maxResults=1&playlistId='
. $item->contentDetails->relatedPlaylists->uploads . '&key=' . $api_key;
$json = json_decode(file_get_contents($api_url));
$response = wp_remote_get( $api_url );
$body = wp_remote_retrieve_body( $response );
$json = json_decode($body);
if( $json && isset($json->items) ){
return $json;
@ -251,8 +255,10 @@ class Youtube_Importer extends Importer {
case 'user':
$api_url = 'https://www.googleapis.com/youtube/v3/channels?part=statistics,snippet,contentDetails&forUsername='
. $id . '&key=' . $api_key;
$json = json_decode(file_get_contents($api_url));
$response = wp_remote_get( $api_url );
$body = wp_remote_retrieve_body( $response );
$json = json_decode($body);
if( $json && isset($json->items) ){
$item = $json->items[0];
@ -260,7 +266,9 @@ class Youtube_Importer extends Importer {
. $pageToken . '&maxResults=1&playlistId='
. $item->contentDetails->relatedPlaylists->uploads . '&key=' . $api_key;
$json = json_decode(file_get_contents($api_url));
$response = wp_remote_get( $api_url );
$body = wp_remote_retrieve_body( $response );
$json = json_decode($body);
if( $json && isset($json->items) ){
return $json;
@ -274,7 +282,9 @@ class Youtube_Importer extends Importer {
. $pageToken . '&maxResults=1&playlistId='
. $id . '&key=' . $api_key;
$json = json_decode(file_get_contents($api_url));
$response = wp_remote_get( $api_url );
$body = wp_remote_retrieve_body( $response );
$json = json_decode($body);
if( $json && isset($json->items) ){
return $json;
@ -285,7 +295,9 @@ class Youtube_Importer extends Importer {
$api_url = 'https://www.googleapis.com/youtube/v3/videos?part=snippet%2CcontentDetails&maxResults=1&id='
. $id . '&key=' . $api_key;
$json = json_decode(file_get_contents($api_url));
$response = wp_remote_get( $api_url );
$body = wp_remote_retrieve_body( $response );
$json = json_decode($body);
if( $json && isset($json->items) ){
return $json;
@ -399,7 +411,7 @@ class Youtube_Importer extends Importer {
</p>
<div class="control is-clearfix">
<input class="input" type="text" name="api_id" value="<?php echo $this->get_option('api_id'); ?>">
<input class="input" type="text" name="api_id" value="<?php echo esc_attr($this->get_option('api_id')); ?>">
</div>
</div>
</div>

View File

@ -64,7 +64,7 @@ class ScriptTainacanOld {
define( 'WP_USE_THEMES', false );
define( 'SHORTINIT', false );
require( dirname(__FILE__) . '/../../../../wp-blog-header.php' );
// require( dirname(__FILE__) . '/../../../../wp-blog-header.php' );
$old_tainacan = new \Tainacan\Importer\Old_Tainacan();
$id = $old_tainacan->get_id();

View File

@ -60,7 +60,7 @@ class Term_Importer extends Importer {
</div>
</span>
<div class="control is-clearfix">
<input class="input" type="text" name="delimiter" value="<?php echo $this->get_option('delimiter'); ?>">
<input class="input" type="text" name="delimiter" value="<?php echo esc_attr($this->get_option('delimiter')); ?>">
</div>
</div>
</div>
@ -93,7 +93,7 @@ class Term_Importer extends Importer {
$taxonomies = $Tainacan_Taxonomies->fetch( ['nopaging' => true], 'OBJECT' );
foreach( $taxonomies as $taxonomie) {
?>
<option value="<?php echo $taxonomie->get_db_identifier();?>"><?php echo $taxonomie->get_name() ?> </option>
<option value="<?php echo esc_attr($taxonomie->get_db_identifier());?>"><?php echo esc_attr($taxonomie->get_name()) ?> </option>
<?php
}
?>
@ -101,7 +101,7 @@ class Term_Importer extends Importer {
</div>
<input class="input new_taxonomy" type="text" name="new_taxonomy" value="<?php echo $this->get_option('new_taxonomy'); ?>" placeholder="<?php _e('New taxonomy name', 'tainacan'); ?>" >
<input class="input new_taxonomy" type="text" name="new_taxonomy" value="<?php echo esc_attr($this->get_option('new_taxonomy')); ?>" placeholder="<?php _e('New taxonomy name', 'tainacan'); ?>" >
</div>
</div>

View File

@ -130,7 +130,6 @@
'timeout' => 0.01,
'blocking' => false,
'body' => $this->data,
'cookies' => $_COOKIE,
'sslverify' => apply_filters( 'https_local_ssl_verify', false ),
);
}

View File

@ -131,6 +131,13 @@ class Collections extends Repository {
'items' => [ 'type' => 'string' ],
//'validation' => v::stringType(),
],
'metadata_section_order' => [
'map' => 'meta',
'title' => __( 'Metadata order', 'tainacan' ),
'type' => ['array', 'object', 'string'],
'items' => [ 'type' => ['array', 'string', 'integer', 'object'] ],
'description' => __( 'The order of the metadata section in the collection', 'tainacan' ),
],
'metadata_order' => [
'map' => 'meta',
'title' => __( 'Metadata order', 'tainacan' ),
@ -179,21 +186,21 @@ class Collections extends Repository {
'title' => __( 'Thumbnail', 'tainacan' ),
'description' => __( 'Squared reduced-size version of a picture that helps recognizing and organizing files', 'tainacan' )
],
'comment_status' => [
'map' => 'comment_status',
'title' => __( 'Comment Status', 'tainacan' ),
'type' => 'string',
'description' => __( 'Collection comment status: "open" means comments are allowed, "closed" means comments are not allowed.', 'tainacan' ),
'default' => get_default_comment_status(Entities\Collection::get_post_type()),
'validation' => v::optional(v::stringType()->in( [ 'open', 'closed' ] )),
],
'allow_comments' => [
'map' => 'meta',
'comment_status' => [
'map' => 'comment_status',
'title' => __( 'Comment Status', 'tainacan' ),
'type' => 'string',
'description' => __( 'Collection comment status: "open" means comments are allowed, "closed" means comments are not allowed.', 'tainacan' ),
'default' => get_default_comment_status(Entities\Collection::get_post_type()),
'validation' => v::optional(v::stringType()->in( [ 'open', 'closed' ] )),
],
'allow_comments' => [
'map' => 'meta',
'title' => __( 'Allow enabling comments on items', 'tainacan' ),
'type' => 'string',
'description' => __( 'If this option is enabled, items of this collection can be set to enable a comments section on their page. "open" means comments are allowed, "closed" means comments are not allowed.', 'tainacan' ),
'default' => 'closed',
'validation' => v::optional(v::stringType()->in( [ 'open', 'closed' ] )),
'type' => 'string',
'description' => __( 'If this option is enabled, items of this collection can be set to enable a comments section on their page. "open" means comments are allowed, "closed" means comments are not allowed.', 'tainacan' ),
'default' => 'closed',
'validation' => v::optional(v::stringType()->in( [ 'open', 'closed' ] )),
],
'submission_anonymous_user' => [
'map' => 'meta',
@ -238,7 +245,13 @@ class Collections extends Repository {
'on_error' => __( 'Value should be yes or no', 'tainacan' ),
'validation' => v::stringType()->in( [ 'yes', 'no' ] ), // yes or no
],
'default_metadata_section_properties' => [
'map' => 'meta',
'title' => __( 'Default metadata section properties', 'tainacan' ),
'type' => 'object',
'items' => [ 'name' => 'string', 'description' => 'string', 'description_bellow_name' => 'string' ],
'description' => __( 'The default metadata section properties', 'tainacan' ),
],
] );
}
@ -304,7 +317,7 @@ class Collections extends Repository {
* @see \Tainacan\Repositories\Repository::insert()
*/
public function insert( $collection ) {
$this->pre_process( $collection );
$this->pre_process( $collection );
$this->handle_parent_order_clone( $collection );
$new_collection = parent::insert( $collection );
@ -366,7 +379,7 @@ class Collections extends Repository {
// TODO: Pegar coleções registradas via código
$args = apply_filters( 'tainacan_fetch_args', $args, 'collections' );
$args = apply_filters( 'tainacan-fetch-args', $args, 'collections' );
$wp_query = new \WP_Query( $args );
@ -429,6 +442,7 @@ class Collections extends Repository {
function handle_parent_order_clone( &$collection ) {
if ($collection instanceof Entities\Collection && $collection->get_parent() != 0) {
$parent_collection = $this->fetch( $collection->get_parent() );
$collection->set_metadata_section_order($parent_collection->get_metadata_section_order());
$collection->set_metadata_order($parent_collection->get_metadata_order());
$collection->set_filters_order($parent_collection->get_filters_order());

View File

@ -242,7 +242,7 @@ class Filters extends Repository {
$args['post_type'] = Entities\Filter::get_post_type();
$args = apply_filters( 'tainacan_fetch_args', $args, 'filters' );
$args = apply_filters( 'tainacan-fetch-args', $args, 'filters' );
$wp_query = new \WP_Query( $args );

View File

@ -437,7 +437,7 @@ class Item_Metadata extends Repository {
if ( is_array( $ids ) ) {
foreach ( $ids as $id ) {
$post_meta_object = get_metadata_by_mid( 'post', $id );
if ( is_object( $post_meta_object ) && get_post($post_meta_object->meta_key) !== null ) {
if ( is_object( $post_meta_object ) && get_post($post_meta_object->meta_key) !== null && get_post_status($post_meta_object->meta_key) !== 'trash' ) {
$metadatum = new Entities\Metadatum( $post_meta_object->meta_key );
$return_value[ $metadatum->get_id() ] = new Entities\Item_Metadata_Entity( $item, $metadatum, $id, (int)$compound_meta_id );
}

View File

@ -356,7 +356,7 @@ class Items extends Repository {
$args = $this->parse_relationship_metaquery($args);
}
$args = apply_filters( 'tainacan_fetch_args', $args, 'items' );
$args = apply_filters( 'tainacan-fetch-args', $args, 'items' );
$should_filter = is_user_logged_in() && sizeof($cpt) > 1;

View File

@ -214,7 +214,7 @@ class Logs extends Repository {
$args['post_type'] = Entities\Log::get_post_type();
$args = apply_filters( 'tainacan_fetch_args', $args, 'logs' );
$args = apply_filters( 'tainacan-fetch-args', $args, 'logs' );
$wp_query = new \WP_Query( $args );

View File

@ -0,0 +1,510 @@
<?php
namespace Tainacan\Repositories;
use Tainacan\Entities;
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
use \Respect\Validation\Validator as v;
/**
* Class Metadata
*/
class Metadata_Sections extends Repository {
public $entities_type = '\Tainacan\Entities\Metadata_Section';
private static $instance = null;
protected function __construct() {
parent::__construct();
}
public static function get_instance() {
if ( ! isset( self::$instance ) ) {
self::$instance = new self();
}
return self::$instance;
}
/**
* {@inheritDoc}
* @see \Tainacan\Repositories\Repository::get_map()
*/
protected function _get_map() {
return apply_filters( 'tainacan-get-map-' . $this->get_name(), [
'name' => [
'map' => 'post_title',
'title' => __( 'Name', 'tainacan' ),
'type' => 'string',
'description' => __( 'Name of the metadata section', 'tainacan' ),
'on_error' => __( 'The name should be a text value and not empty', 'tainacan' ),
'validation' => v::stringType()->notEmpty(),
],
'slug' => [
'map' => 'post_name',
'title' => __( 'Slug', 'tainacan' ),
'type' => 'string',
'description' => __( 'A unique and sanitized string representation of the metadata sction', 'tainacan' ),
],
'status' => [
'map' => 'post_status',
'title' => __( 'Status', 'tainacan' ),
'type' => 'string',
'default' => 'publish',
'description' => __( 'Status', 'tainacan' )
],
'description' => [
'map' => 'post_content',
'title' => __( 'Description', 'tainacan' ),
'type' => 'string',
'description' => __( 'The metadata section description.', 'tainacan' ),
'default' => '',
],
'description_bellow_name' => [
'map' => 'meta',
'title' => __( 'Description bellow name', 'tainacan' ),
'type' => 'string',
'description' => __( 'Whether the section metadata description should be displayed bellow the name instead of inside a tooltip.', 'tainacan' ),
'on_error' => __( 'Please set the "Description bellow name" value as "yes" or "no"', 'tainacan' ),
'validation' => v::stringType()->in( [ 'yes', 'no' ] ), // yes or no
'default' => 'no'
],
'collection_id' => [
'map' => 'meta',
'title' => __( 'Collection', 'tainacan' ),
'type' => ['integer', 'string'],
'description' => __( 'The collection ID', 'tainacan' ),
]
] );
}
/**
* Get the labels for the custom post type of this repository
*
* @return array Labels in the format expected by register_post_type()
*/
public function get_cpt_labels() {
return array(
'name' => __( 'Metadata Sections', 'tainacan' ),
'singular_name' => __( 'Metadata Section', 'tainacan' ),
'add_new' => __( 'Add new', 'tainacan' ),
'add_new_item' => __( 'Add new Metadata Section', 'tainacan' ),
'edit_item' => __( 'Edit Metadata Section', 'tainacan' ),
'new_item' => __( 'New Metadata Section', 'tainacan' ),
'view_item' => __( 'View Metadata Section', 'tainacan' ),
'search_items' => __( 'Search Metadata Section', 'tainacan' ),
'not_found' => __( 'No Metadata Section found ', 'tainacan' ),
'not_found_in_trash' => __( 'No Metadata Section found in trash', 'tainacan' ),
'parent_item_colon' => __( 'Parent Metadata Section:', 'tainacan' ),
'menu_name' => __( 'Metadata Section', 'tainacan' )
);
}
public function register_post_type() {
$labels = $this->get_cpt_labels();
$args = array(
'labels' => $labels,
'hierarchical' => true,
'public' => true,
'show_ui' => tnc_enable_dev_wp_interface(),
'show_in_menu' => tnc_enable_dev_wp_interface(),
'publicly_queryable' => true,
'exclude_from_search' => true,
'has_archive' => false,
'query_var' => true,
'can_export' => true,
'rewrite' => true,
'map_meta_cap' => true,
'show_in_nav_menus' => false,
'capabilities' => (array) $this->get_capabilities(),
'supports' => [
'title',
'editor',
'page-attributes'
]
);
register_post_type( Entities\Metadata_Section::get_post_type(), $args );
}
/**
* Get the default metadata section of the collection
*
* @param \Tainacan\Entities\Collection|int the collection of the metadata section
*
* @return \Tainacan\Entities\Metadata_Section|false return de the default metadata section or false otherwise.
*/
public function get_default_section ($collection) {
if($collection instanceof Entities\Collection) {
$collection_id = $collection->get_id();
} else if (is_int($collection)) {
$collection_id = $collection;
$collection = \tainacan_collections()->fetch($collection, 'OBJECT');
} else {
return false;
}
$name = __('Metadata', 'tainacan');
$description = __('Metadata section', 'tainacan');
$description_bellow_name = 'no';
$default_metadata_section_properties = $collection->get_default_metadata_section_properties();
if( !empty($default_metadata_section_properties) ) {
$name = isset($default_metadata_section_properties['name']) ? $default_metadata_section_properties['name'] : $name;
$description = isset($default_metadata_section_properties['description']) ? $default_metadata_section_properties['description'] : $description;
$description_bellow_name = isset($default_metadata_section_properties['description_bellow_name']) ? $default_metadata_section_properties['description_bellow_name'] : $description_bellow_name;
}
$defaul_section = new Entities\Metadata_Section();
$defaul_section->set_slug(\Tainacan\Entities\Metadata_Section::$default_section_slug);
$defaul_section->set_name($name);
$defaul_section->set_description($description);
$defaul_section->set_description_bellow_name($description_bellow_name);
$defaul_section->set_collection_id($collection_id);
return $defaul_section;
}
/**
* fetch metadata section based on ID or WP_Query args
*
* metadata section are stored as posts. Check WP_Query docs
* to learn all args accepted in the $args parameter (@see https://developer.wordpress.org/reference/classes/wp_query/)
* You can also use a mapped property, such as name and description, as an argument and it will be mapped to the
* appropriate WP_Query argument
*
* If a number is passed to $args, it will return a \Tainacan\Entities\Metadata_Section object. But if the post is not found or
* does not match the entity post type, it will return an empty array
*
* @param array $args WP_Query args || int $args the metadata section id
* @param string $output The desired output format (@see \Tainacan\Repositories\Repository::fetch_output() for possible values)
*
* @return Entities\Metadata_Section|\WP_Query|Array an instance of wp query OR array of entities;
* @throws \Exception
*/
public function fetch( $args, $output = null ) {
if ( is_numeric( $args ) ) {
$existing_post = get_post( $args );
if ( $existing_post instanceof \WP_Post ) {
try {
return new Entities\Metadata_Section( $existing_post );
} catch (\Exception $e) {
return [];
}
} else {
return [];
}
} elseif ( is_array( $args ) ) {
$args = array_merge( [
'posts_per_page' => - 1,
], $args );
$args = $this->parse_fetch_args( $args );
$args['post_type'] = Entities\Metadata_Section::get_post_type();
$args = apply_filters( 'tainacan-fetch-args', $args, 'metadata-section' );
$wp_query = new \WP_Query( $args );
return $this->fetch_output( $wp_query, $output );
}
}
/**
* fetch metadata sections IDs based on WP_Query args
*
* to learn all args accepted in the $args parameter (@see https://developer.wordpress.org/reference/classes/wp_query/)
* You can also use a mapped property, such as name and description, as an argument and it will be mapped to the
* appropriate WP_Query argument
*
* @param array $args WP_Query args || int $args the item id
*
* @return Array array of IDs;
* @throws \Exception
*/
public function fetch_ids( $args = [] ) {
$args['fields'] = 'ids';
return $this->fetch( $args )->get_posts();
}
/**
* fetch metadata section by collection
*
* @param Entities\Collection $collection
* @param array $args WP_Query args
*
* @return array Entities\Metadata_Section
* @throws \Exception
*/
public function fetch_by_collection( Entities\Collection $collection, $args = [] ) {
$collection_id = $collection->get_id();
//get parent collections
$parents = get_post_ancestors( $collection_id );
//insert the actual collection
if ( is_numeric($collection_id) ) {
$parents[] = $collection_id;
}
$results = [];
$args = array_merge( [
'parent' => 0
], $args );
$original_meta_q = isset( $args['meta_query'] ) ? $args['meta_query'] : [];
/**
* Since we introduced roles & capabalities management, we cannot rely
* on WordPress behavior when handling default post status values.
* WordPress checks if the current user can read_priva_posts, but this is
* not enough for us. We have to handle this ourselves to mimic WordPress behavior
* considering how tainacan manages metadata capabilities
*/
if ( ! isset($args['post_status']) ) {
foreach ( $parents as $parent_id ) {
// Add public states.
$statuses = get_post_stati( array( 'public' => true ) );
$read_private_cap = 'tnc_col_' . $parent_id . '_read_private_metadata_section';
if ( current_user_can($read_private_cap) ) {
$statuses = array_merge( $statuses, get_post_stati( array( 'private' => true ) ) );
}
$args['post_status'] = $statuses;
$meta_query = array(
'key' => 'collection_id',
'value' => $parent_id,
);
$args['meta_query'] = $original_meta_q;
$args['meta_query'][] = $meta_query;
$results = array_merge($results, $this->fetch( $args, 'OBJECT' ));
}
} else {
$meta_query = array(
'key' => 'collection_id',
'value' => $parents,
'compare' => 'IN',
);
$args['meta_query'] = $original_meta_q;
$args['meta_query'][] = $meta_query;
$results = $this->fetch( $args, 'OBJECT' );
}
$results[] = $this->get_default_section($collection->get_id());
return $this->order_result(
$results,
$collection,
isset( $args['include_disabled'] ) ? $args['include_disabled'] : false
);
}
/**
* @param \Tainacan\Entities\Metadata_Section $metadata_section
*
* @return \Tainacan\Entities\Metadata_Section
* {@inheritDoc}
* @see \Tainacan\Repositories\Repository::insert()
*/
public function insert( $metadata_section ) {
$new_metadata_section = parent::insert( $metadata_section );
return $new_metadata_section;
}
/**
* @param \Tainacan\Entities\Metadata_Section $object
* @param $new_values
*
* @return mixed|string|Entities\Entity
* @throws \Exception
*/
public function update( $object, $new_values = null ) {
if($object->get_id() == \Tainacan\Entities\Metadata_Section::$default_section_slug) {
$collection = $object->get_collection();
if($collection instanceof \Tainacan\Entities\Collection) {
$properties = array(
'name' => $object->get_name(),
'description' => $object->get_description(),
'description_bellow_name' => $object->get_description_bellow_name()
);
$collection->set_default_metadata_section_properties($properties);
if($collection->validate()) {
\tainacan_collections()->update($collection);
return $object;
}
}
return false;
}
return $this->insert( $object );
}
public function add_metadata($metadata_section_id, $metadata_list) {
$metadata_section = $this->fetch($metadata_section_id, 'OBJECT');
if ($metadata_section) {
foreach($metadata_list as $metadata_id) {
//update_post_meta($metadata_id, 'metadata_section_id', $metadata_section_id);
add_post_meta($metadata_id, 'metadata_section_id', $metadata_section_id);
}
return $metadata_section;
}
return false;
}
public function delete_metadata($metadata_section_id, $metadata_list) {
$metadata_section = $this->fetch($metadata_section_id, 'OBJECT');
if ($metadata_section) {
foreach($metadata_list as $metadata_id) {
delete_post_meta($metadata_id, 'metadata_section_id', $metadata_section_id);
}
return $metadata_section;
}
return false;
}
public function get_metadata_object_list($metadata_section_id, $args = []) {
$metadata_section = $this->fetch($metadata_section_id);
if ($metadata_section) {
$metadata_repository = \Tainacan\Repositories\Metadata::get_instance();
$metadata_list = $metadata_repository->fetch_by_metadata_section($metadata_section, $args);
return $metadata_list;
}
return false;
}
public function get_default_section_metadata_object_list (Entities\Collection $collection, $args = []) {
$metadata_repository = \Tainacan\Repositories\Metadata::get_instance();
$metadata_sections_ids = $this->fetch_ids();
$collection_metadata_sections_id = array_diff(array_map(function($el) {return $el->get_id();} , $this->fetch_by_collection($collection)), [\Tainacan\Entities\Metadata_Section::$default_section_slug]);
$args_exclude = array_merge(
array(
'meta_query' => array(
'relation' => 'AND',
array(
array(
'key' => 'collection_id',
'value' => 'default',
'compare' => '='
),
array(
'key' => 'metadata_section_id',
'value' => $collection_metadata_sections_id,
'compare' => 'IN'
)
)
)
)
);
$list_exclude = $metadata_repository->fetch_by_collection($collection, $args_exclude);
$not_post_ids = array_map(function($el) { return $el->get_id(); }, $list_exclude);
$args = array_merge(
$args,
array(
'post__not_in' => $not_post_ids,
'meta_query' => array(
array(
'relation' => 'OR',
array(
'key' => 'metadata_section_id',
'value' => \Tainacan\Entities\Metadata_Section::$default_section_slug,
'compare' => '='
),
array(
array(
'key' => 'metadata_section_id',
'compare' => 'NOT EXISTS'
)
),
array(
'key' => 'metadata_section_id',
'value' => $metadata_sections_ids,
'compare' => 'NOT IN'
)
)
)
)
);
$metadata_list = $metadata_repository->fetch_by_collection($collection, $args);
return $metadata_list;
}
/**
* @inheritDoc
*/
public function delete( Entities\Entity $entity, $permanent = true ) {
//test if not exist a metadata using this section
if ( !empty( $this->get_metadata_object_list($entity->get_id() ) ) ) {
throw new \Exception( 'The metadata section must not contain metadata before deleted' );
}
return parent::delete($entity, $permanent);
}
public function order_result( $result, Entities\Collection $collection, $include_disabled = false ) {
$order = $collection->get_metadata_section_order();
if ( $order ) {
$order = ( is_array( $order ) ) ? $order : unserialize( $order );
if ( is_array( $result ) ) {
$result_ordinate = [];
$not_ordinate = [];
foreach ( $result as $item ) {
$id = $item->get_id();
$index = array_search( $id, array_column( $order, 'id' ) );
if ( $index !== false ) {
// skipping metadata disabled if the arg is set
if ( ! $include_disabled && isset( $order[ $index ]['enabled'] ) && ! $order[ $index ]['enabled'] ) {
continue;
}
$enable = ( isset( $order[ $index ]['enabled'] ) ) ? $order[ $index ]['enabled'] : true;
$item->set_enabled_for_collection( $enable );
$result_ordinate[ $index ] = $item;
} else {
$not_ordinate[] = $item;
}
}
ksort( $result_ordinate );
$result_ordinate = array_merge( $result_ordinate, $not_ordinate );
return $result_ordinate;
}
}
return $result;
}
/**
* Check if $user can read the entity
*
* @param Entities\Metadata_Section|Entities\Entity $entity
* @param int|\WP_User|null $user default is null for the current user
*
* @return boolean
* @throws \Exception
*/
public function can_read( Entities\Entity $entity, $user = null ) {
if ( is_null($entity) )
return false;
if ($entity instanceof Entities\Metadata_Section && $entity->get_id() == Entities\Metadata_Section::$default_section_slug ) {
$collection = $entity->get_collection();
if($collection instanceof Entities\Collection) {
return $collection->can_read();
}
return false;
}
return parent::can_read($entity, $user);
}
}

View File

@ -42,6 +42,7 @@ class Metadata extends Repository {
add_action('tainacan-insert-tainacan-taxonomy', [$this, 'hook_taxonomies_saved_as_private']);
add_action('tainacan-insert-tainacan-taxonomy', [$this, 'hook_taxonomies_saved_not_allow_insert_new_terms']);
add_action('tainacan-insert-tainacan-metadatum', [$this, 'hook_metadata_update_order']);
}
@ -49,7 +50,7 @@ class Metadata extends Repository {
* {@inheritDoc}
* @see \Tainacan\Repositories\Repository::get_map()
*/
protected function _get_map() {
protected function _get_map() {
return apply_filters( 'tainacan-get-map-' . $this->get_name(), [
'name' => [
'map' => 'post_title',
@ -225,6 +226,13 @@ class Metadata extends Repository {
// yes or no. It cant be multiple if its collection_key
'default' => 'no'
],
'metadata_section_id' => [
'map' => 'meta_multi',
'title' => __( 'Metadata section', 'tainacan' ),
'type' => ['integer', 'string', 'array'],
'description' => __( 'The metadata section ID', 'tainacan' ),
'default' => \Tainacan\Entities\Metadata_Section::$default_section_slug
],
] );
}
@ -368,7 +376,7 @@ class Metadata extends Repository {
$args['post_type'] = Entities\Metadatum::get_post_type();
$args = apply_filters( 'tainacan_fetch_args', $args, 'metadata' );
$args = apply_filters( 'tainacan-fetch-args', $args, 'metadata' );
$wp_query = new \WP_Query( $args );
@ -456,6 +464,33 @@ class Metadata extends Repository {
$args['meta_query'] = $original_meta_q;
$args['meta_query'][] = $meta_query;
if ($this->get_default_metadata_attribute() != $parent_id) {
$read_private_metasection_cap = "tnc_col_{$parent_id}_read_private_metasection";
if ( !current_user_can($read_private_metasection_cap) ) {
$private_metadata_sections_ids = \tainacan_metadata_sections()->fetch_ids(
array(
'post_status' => get_post_stati( array( 'private' => true ) ),
'meta_query' => array(
'key' => 'collection_id',
'value' => $parent_id,
)
));
$args['meta_query'][] = array(
'relation' => 'OR',
array(
'key' => 'metadata_section_id',
'value' => $private_metadata_sections_ids,
'compare' => 'NOT IN'
),
array( //note: using the comparete 'NOT EXISTS' to cases where metadata section id is not present in mapped property (meta) of metadatum
'key' => 'metadata_section_id',
'compare' => 'NOT EXISTS'
)
);
}
}
$results = array_merge($results, $this->fetch( $args, 'OBJECT' ));
}
@ -586,6 +621,40 @@ class Metadata extends Repository {
return $results;
}
/**
* fetch metadatum by metadata section, considering order
*
* @param Entities\Metadata_Section $collection
* @param array $args WP_Query args plus disabled_metadata
*
* @return array Entities\Metadatum
* @throws \Exception
*/
public function fetch_by_metadata_section( Entities\Metadata_Section $metadata_section, $args = [] ) {
$results = [];
if ($metadata_section && $metadata_section->can_read()) {
$metadata_section_id = $metadata_section->get_id();
$collection = $metadata_section->get_collection();
$args = array_merge($args, array(
'parent' => 0,
'meta_query' => [
[
'key' => 'metadata_section_id',
'value' => $metadata_section_id,
'compare' => '='
]
]
));
$results = $this->fetch($args, 'OBJECT');
return $this->order_result(
$results,
$collection,
isset( $args['include_disabled'] ) ? $args['include_disabled'] : false
);
}
return $results;
}
/**
* Ordinate the result from fetch response if $collection has an ordination,
* metadata not ordinated appear on the end of the list
@ -599,6 +668,7 @@ class Metadata extends Repository {
*/
public function order_result( $result, Entities\Collection $collection, $include_disabled = false ) {
$order = $collection->get_metadata_order();
$section_order = $collection->get_metadata_section_order();
if ( $order ) {
$order = ( is_array( $order ) ) ? $order : unserialize( $order );
@ -610,11 +680,23 @@ class Metadata extends Repository {
foreach ( $result as $item ) {
$id = $item->WP_Post->ID;
$index = array_search( $id, array_column( $order, 'id' ) );
$metadata_section_ids = get_post_meta( $id, 'metadata_section_id');
$enabled_metadata_section = true;
if(!empty($metadata_section_ids) && $metadata_section_ids !== false && !empty($section_order)) {
foreach( $metadata_section_ids as $metadata_section_id) {
$section_order_index = array_search( $metadata_section_id, array_column( $section_order, 'id' ) );
if ( $section_order_index !== false ) {
$enabled_metadata_section = boolval($section_order[$section_order_index]['enabled']);
break;
}
}
}
if ( $index !== false ) {
// skipping metadata disabled if the arg is set
if ( ! $include_disabled && isset( $order[ $index ]['enabled'] ) && ! $order[ $index ]['enabled'] ) {
if ( ! $include_disabled && (!$enabled_metadata_section || isset( $order[ $index ]['enabled'] ) && ! $order[ $index ]['enabled'] )) {
continue;
}
@ -623,6 +705,14 @@ class Metadata extends Repository {
$result_ordinate[ $index ] = $item;
} else {
// skipping if metadata coumpound is disabled if the arg is set
if ($item->get_parent() > 0) {
$parent_metadatum = new \Tainacan\Entities\Metadatum($item->get_parent());
$parent_index = array_search( $parent_metadatum->get_id(), array_column( $order, 'id' ) );
if ( ! $include_disabled && (!$enabled_metadata_section || isset( $order[ $parent_index ]['enabled'] ) && ! $order[ $parent_index ]['enabled'] )) {
continue;
}
}
$not_ordinate[] = $item;
}
}
@ -648,13 +738,35 @@ class Metadata extends Repository {
public function insert( $metadatum ) {
$this->pre_update_taxonomy_metadatum( $metadatum );
$new_metadatum = parent::insert( $metadatum );
$this->update_taxonomy_metadatum( $new_metadatum );
$this->update_metadata_type_index( $new_metadatum );
return $new_metadatum;
}
public function pre_update_metadata_repository_level($metadatum, $attributes) {
if (isset($attributes['target_collection_id']) && is_numeric($attributes['target_collection_id']) && $metadatum->is_repository_level()) {
$collection = \tainacan_collections()->fetch($attributes['target_collection_id'], 'OBJECT');
$new_metadata_section_id = $metadatum->get_metadata_section_id();
$new_metadata_section_id = is_array($new_metadata_section_id) ? $new_metadata_section_id[0] : $new_metadata_section_id;
if($collection instanceof \Tainacan\Entities\Collection) {
$collection_metadata_sections_id = array_filter(
array_map(function($el) {return $el->get_id();} , \tainacan_metadata_sections()->fetch_by_collection($collection)),
function($el) {return $el != \Tainacan\Entities\Metadata_Section::$default_section_slug;}
);
$old_value = get_post_meta($metadatum->get_id(), 'metadata_section_id');
$new_value = array_diff($old_value, $collection_metadata_sections_id);
$new_value[] = (string)$new_metadata_section_id;
$metadatum->set_metadata_section_id($new_value);
if(!$metadatum->validate()) {
throw new \Exception( $metadatum->get_errors() );
}
}
}
return $metadatum;
}
/**
* @param $object
* @param $new_values
@ -663,6 +775,7 @@ class Metadata extends Repository {
* @throws \Exception
*/
public function update( $object, $new_values = null ) {
$object = $this->pre_update_metadata_repository_level($object, $new_values);
return $this->insert( $object );
}
@ -751,7 +864,7 @@ class Metadata extends Repository {
*/
private function get_data_core_metadata( Entities\Collection $collection ) {
return $data_core_metadata = [
$data_core_metadata = [
'core_title' => [
'name' => __('Title', 'tainacan'),
'collection_id' => $collection->get_id(),
@ -766,6 +879,7 @@ class Metadata extends Repository {
'status' => 'publish',
]
];
return $data_core_metadata;
}
@ -1715,4 +1829,38 @@ class Metadata extends Repository {
return false;
}
/**
* When a metadata is saved, if the metadata section changes, the ordering needs to be updated
*
* @param \Tainacan\Entities\Metadatum $metadata
* @return void
*/
public function hook_metadata_update_order($metadata) {
$tainacan_metadata_sections_repository = \tainacan_metadata_sections();
$tainacan_collections_repository = \tainacan_collections();
$metadata_section_id = $metadata->get_metadata_section_id();
$metadata_section = $tainacan_metadata_sections_repository->fetch($metadata_section_id);
if ( $metadata_section instanceof \Tainacan\Entities\Metadata_Section ) {
$collection = $metadata_section->get_collection();
$metadata_sections_order = $collection->get_metadata_section_order();
if( empty($metadata_sections_order) ) {
return;
}
foreach( $metadata_sections_order as &$metadata_section_order ) {
$pos = array_search($metadata->get_id(), array_column($metadata_section_order['metadata_order'], 'id'));
if($pos !== false) {
if( $metadata_section_id != $metadata_section_order['id']) {
array_splice($metadata_section_order['metadata_order'], $pos, 1);
}
} else if($metadata_section_id == $metadata_section_order['id']) {
$metadata_section_order['metadata_order'][] = ["id" => $metadata->get_id(), "enabled" => $metadata->get_enabled_for_collection()];
}
}
$collection->set_metadata_section_order($metadata_sections_order);
if($collection->validate()) {
$tainacan_collections_repository->update($collection);
}
}
}
}

View File

@ -515,6 +515,7 @@ abstract class Repository {
$Tainacan_Metadata = Repositories\Metadata::get_instance();
$Tainacan_Taxonomies = Repositories\Taxonomies::get_instance();
$Tainacan_Terms = Repositories\Terms::get_instance();
$Tainacan_Metadata_Sections = Repositories\Metadata_Sections::get_instance();
$tnc_globals = [
$Tainacan_Collections,
@ -522,7 +523,8 @@ abstract class Repository {
$Tainacan_Filters,
$Tainacan_Taxonomies,
$Tainacan_Terms,
$Tainacan_Logs
$Tainacan_Logs,
$Tainacan_Metadata_Sections
];
foreach ( $tnc_globals as $tnc_repository ) {
$tnc_entity = new $tnc_repository->entities_type();

View File

@ -191,7 +191,7 @@ class Taxonomies extends Repository {
$args['post_type'] = Entities\Taxonomy::get_post_type();
$args = apply_filters( 'tainacan_fetch_args', $args, 'taxonomies' );
$args = apply_filters( 'tainacan-fetch-args', $args, 'taxonomies' );
$wp_query = new \WP_Query( $args );

View File

@ -133,6 +133,8 @@ $Tainacan_Item_Metadata = \Tainacan\Repositories\Item_Metadata::get_instance();
$Metadata_Type_Helper = \Tainacan\Metadata_Types\Metadata_Type_Helper::get_instance();
$Tainacan_Metadata_Section = \Tainacan\Repositories\Metadata_Sections::get_instance();
$Filter_Type_Helper = \Tainacan\Filter_Types\Filter_Type_Helper::get_instance();
$Tainacan_Taxonomies = \Tainacan\Repositories\Taxonomies::get_instance();

View File

@ -71,3 +71,12 @@ function tainacan_terms() {
function tainacan_roles() {
return \Tainacan\Roles::get_instance();
}
/**
* Retrieve the singleton Metadata Sections Repository instance
* @return \Tainacan\Repositories\Metadata_Sections The Tainacan Metadata Repository
*/
function tainacan_metadata_sections() {
return \Tainacan\Repositories\Metadata_Sections::get_instance();
}

View File

@ -362,8 +362,6 @@ class Theme_Helper {
}
public function item_submission_shortcode($args) {
global $TAINACAN_BASE_URL;
$props = ' ';
// Passes arguments to custom props
@ -377,7 +375,36 @@ class Theme_Helper {
wp_enqueue_media();
return "<div data-module='item-submission-form' id='tainacan-item-submission-form' $props ></div>";
$allowed_html = [
'div' => [
'id' => true,
'data-module' => true,
'collection-id' => true,
'hide-file-modal-button' => true,
'hide-text-modal-button' => true,
'hide-link-modal-button' => true,
'hide-thumbnail-section' => true,
'hide-attachments-section' => true,
'show-allow-comments-section' => true,
'hide-collapses' => true,
'hide-help-buttons' => true,
'hide-metadata-types' => true,
'help-info-bellow-label' => true,
'document-section-label' => true,
'thumbnail-section-label' => true,
'attachments-section-label' => true,
'metadata-section-label' => true,
'sent-form-heading' => true,
'sent-form-message' => true,
'item-link-button-label' => true,
'show-item-link-button' => true,
'show-terms-agreement-checkbox' => true,
'terms-agreement-message' => true,
'enabled-metadata' => true,
]
];
return wp_kses("<div data-module='item-submission-form' id='tainacan-item-submission-form' $props ></div>", $allowed_html);
}
/**
@ -489,7 +516,40 @@ class Theme_Helper {
}
}
return "<div data-module='faceted-search' id='tainacan-items-page' $props ></div>";
$allowed_html = [
'div' => [
'id' => true,
'data-module' => true,
'collection-id' => true,
'term-id' => true,
'taxonomy' => true,
'default-view-mode' => true,
'is-forced-view-mode' => true,
'enabled-view-modes' => true,
'default-order' => true,
'default-orderby' => true,
'hide-filters' => true,
'hide-hide-filters-button' => true,
'hide-search' => true,
'hide-advanced-search' => true,
'hide-displayed-metadata-button' => true,
'hide-sorting-area' => true,
'hide-items-thumbnail' => true,
'hide-sort-by-button' => true,
'hide-exposers-button' => true,
'hide-items-per-page-button' => true,
'hide-go-to-page-button' => true,
'hide-pagination-area' => true,
'default-items-per-page' => true,
'show-filters-button-inside-search-control' => true,
'start-with-filters-hidden' => true,
'filters-as-modal' => true,
'show-inline-view-mode-options' => true,
'show-fullscreen-with-view-modes' => true
]
];
return wp_kses("<div data-module='faceted-search' id='tainacan-items-page' $props ></div>", $allowed_html);
}
function get_items_list_slug() {
@ -680,7 +740,7 @@ class Theme_Helper {
$logo = get_template_directory_uri() . '/assets/images/social-logo.png';
$excerpt = get_bloginfo( 'description' );
$url_src = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
$url_src = esc_url((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]");
global $wp;
if ( is_post_type_archive() ) {
@ -749,13 +809,13 @@ class Theme_Helper {
?>
<meta property="og:type" content="article"/>
<meta property="og:title" content="<?php echo $title; ?>"/>
<meta property="og:site_name" content="<?php echo get_bloginfo(); ?>"/>
<meta property="og:description" content="<?php echo $excerpt; ?>"/>
<meta property="og:url" content="<?php echo $url_src; ?>"/>
<meta property="og:image" content="<?php echo $image['url']; ?>"/>
<meta property="og:image:width" content="<?php echo $image['width']; ?>"/>
<meta property="og:image:height" content="<?php echo $image['height']; ?>"/>
<meta property="og:title" content="<?php echo esc_attr($title); ?>"/>
<meta property="og:site_name" content="<?php echo esc_attr(get_bloginfo()); ?>"/>
<meta property="og:description" content="<?php echo esc_html($excerpt); ?>"/>
<meta property="og:url" content="<?php echo esc_url($url_src); ?>"/>
<meta property="og:image" content="<?php echo esc_url($image['url']); ?>"/>
<meta property="og:image:width" content="<?php echo esc_attr($image['width']); ?>"/>
<meta property="og:image:height" content="<?php echo esc_attr($image['height']); ?>"/>
<?php } else { return; } // End if().
@ -854,7 +914,7 @@ class Theme_Helper {
* @type integer $auto_play_speed The time in s to translate to the next slide automatically
* @type bool $loop_slides Should slides loop when reached the end of the Carousel?
* @type bool $hide_title Should the title of the items be displayed?
* @type bool $crop_images_to_square Should it use the `tainacan-medium-size` instead of the `tainacan-medium-large-size`?
* @type string $image_size Item image size. Defaults to 'tainacan-medium'
* @type bool $show_collection_header Should it display a small version of the collection header?
* @type bool $show_collection_label Should it displar a 'Collection' label before the collection name on the collection header?
* @type string $collection_background_color Color of the collection header background
@ -877,7 +937,7 @@ class Theme_Helper {
'auto_play_speed' => 3,
'loop_slides' => false,
'hide_title' => false,
'crop_images_to_square' => true,
'image_size' => 'tainacan-medium',
'show_collection_header' => false,
'show_collection_label' => false,
'collection_background_color' => '#454647',
@ -888,6 +948,11 @@ class Theme_Helper {
);
$args = wp_parse_args($args, $defaults);
/* Compatibility with previous version */
if ( isset($args['crop_images_to_square ']) && !$args['crop_images_to_square'] ) {
$args['image_size'] = 'tainacan-medium-full';
}
$props = ' ';
// Always pass the class needed by Vue to mount the component;
@ -895,14 +960,22 @@ class Theme_Helper {
unset($args['class_name']);
// Builds parameters to the html div rendered by Vue
$allowed_html = [
'div' => [
'data-module' => true,
'id' => true
]
];
foreach ($args as $key => $value) {
if (is_bool($value))
$value = $value ? 'true' : 'false';
// Changes from PHP '_' notation to HTML '-' notation
$props .= (str_replace('_', '-', $key) . "='" . $value . "' ");
$key_attr = str_replace('_', '-', $key);
$props .= "$key_attr='$value' ";
$allowed_html['div'][$key_attr] = true;
}
return "<div data-module='carousel-items-list' id='tainacan-items-carousel-shortcode_" . uniqid() . "' $props ></div>";
return wp_kses( "<div data-module='carousel-items-list' id='tainacan-items-carousel-shortcode_" . uniqid() . "' $props ></div>", $allowed_html);
}
/**
@ -920,7 +993,7 @@ class Theme_Helper {
* @type string $show_name Show the item title
* @type string $show_image Show the item thumbnail
* @type string $layout Either 'grid', 'list' or 'mosaic'
* @type string $crop_images_to_square Force images shape to be squared
* @type string $image_size Item image size. Defaults to 'tainacan-medium'
* @type bool $show_collection_header Should it display a small version of the collection header?
* @type bool $show_collection_label Should it displar a 'Collection' label before the collection name on the collection header?
* @type string $collection_background_color Color of the collection header background
@ -947,7 +1020,7 @@ class Theme_Helper {
'show_name' => true,
'show_image' => true,
'layout' => 'grid',
'crop_images_to_square' => true,
'image_size' => 'tainacan-medium',
'show_collection_header' => false,
'show_collection_label' => false,
'collection_background_color' => '#454647',
@ -964,21 +1037,35 @@ class Theme_Helper {
);
$args = wp_parse_args($args, $defaults);
/* Compatibility with previous version */
if ( isset($args['crop_images_to_square ']) && !$args['crop_images_to_square'] ) {
$args['image_size'] = 'tainacan-medium-full';
}
$props = ' ';
// Always pass the class needed by Vue to mount the component;
$args['class'] = $args['class_name'] . ' wp-block-tainacan-dynamic-items-list';
unset($args['class_name']);
// Builds parameters to the html div rendered by Vue
$allowed_html = [
'div' => [
'data-module' => true,
"id" => true
]
];
// Builds parameters to the html div rendered by Vue
foreach ($args as $key => $value) {
if (is_bool($value))
$value = $value ? 'true' : 'false';
// Changes from PHP '_' notation to HTML '-' notation
$props .= (str_replace('_', '-', $key) . "='" . $value . "' ");
$key_attr = str_replace('_', '-', $key);
$props .= "$key_attr='$value' ";
$allowed_html['div'][$key_attr] = true;
}
return "<div data-module='dynamic-items-list' id='tainacan-dynamic-items-list-shortcode_" . uniqid(). "' $props ></div>";
return wp_kses("<div data-module='dynamic-items-list' id='tainacan-dynamic-items-list-shortcode_" . uniqid(). "' $props ></div>", $allowed_html);
}
/**
@ -1000,9 +1087,6 @@ class Theme_Helper {
* @return string The HTML div to be used for rendering the related items vue component
*/
public function get_tainacan_related_items_list($args = []) {
global $TAINACAN_BASE_URL;
global $TAINACAN_VERSION;
$defaults = array(
'class_name' => '',
'collection_heading_class_name' => '',
@ -1025,22 +1109,21 @@ class Theme_Helper {
return;
// Always pass the default class. We force passing the wp-block-tainacan-carousel-related-items because themes might have used it to style before the other layouts exist;
$output = '<div data-module="related-items-list" class="' . $args['class_name'] . ' wp-block-tainacan-carousel-related-items wp-block-tainacan-related-items' . '">';
$output = '<div data-module="related-items-list" class="' . esc_attr($args['class_name']) . ' wp-block-tainacan-carousel-related-items wp-block-tainacan-related-items' . '">';
foreach($related_items as $collection_id => $related_group) {
if ( isset($related_group['items']) && isset($related_group['total_items']) && $related_group['total_items'] ) {
// Adds a heading with the collection name
$collection_heading = '';
if ( isset($related_group['collection_name']) ) {
$collection_heading = '<' . $args['collection_heading_tag'] . ' class="' . $args['collection_heading_class_name'] . '">' . $related_group['collection_name'] . '</' . $args['collection_heading_tag'] . '>';
$collection_heading = wp_kses_post('<' . $args['collection_heading_tag'] . ' class="' . $args['collection_heading_class_name'] . '">' . $related_group['collection_name'] . '</' . $args['collection_heading_tag'] . '>');
}
// Adds a paragraph with the metadata name
$metadata_label = '';
if ( isset($related_group['metadata_name']) ) {
$metadata_label = '<' . $args['metadata_label_tag'] . ' class="' . $args['metadata_label_class_name'] . '">' . $related_group['metadata_name'] . '</' . $args['metadata_label_tag'] . '>';
$metadata_label = wp_kses_post('<' . $args['metadata_label_tag'] . ' class="' . $args['metadata_label_class_name'] . '">' . $related_group['metadata_name'] . '</' . $args['metadata_label_tag'] . '>');
}
// Sets the carousel, from the items carousel template tag.
@ -1069,6 +1152,10 @@ class Theme_Helper {
$output .= '<div class="wp-block-group">
<div class="wp-block-group__inner-container">' .
/**
* Note to code reviewers: This lines doesn't need to be escaped.
* Functions get_tainacan_items_carousel() and get_tainacan_dynamic_items_list used here escape the return value.
*/
$collection_heading .
$metadata_label .
$items_list_div .
@ -1076,7 +1163,7 @@ class Theme_Helper {
$related_group['total_items'] > 1 ?
'<div class="wp-block-buttons">
<div class="wp-block-button">
<a class="wp-block-button__link" href="/' . $related_group['collection_slug'] . '?metaquery[0][key]=' . $related_group['metadata_id'] . '&metaquery[0][value][0]=' . $item->get_ID() . '&metaquery[0][compare]=IN">
<a class="wp-block-button__link" href="' . esc_url('/' . $related_group['collection_slug']) . '?metaquery[0][key]=' . esc_attr($related_group['metadata_id']) . '&metaquery[0][value][0]=' . esc_attr($item->get_ID()) . '&metaquery[0][compare]=IN">
' . sprintf( __('View all %s related items', 'tainacan'), $related_group['total_items'] ) . '
</a>
</div>
@ -1306,9 +1393,10 @@ class Theme_Helper {
if ( $media_sources['document'] && !empty(tainacan_get_the_document($item_id)) ) {
$is_document_type_attachment = tainacan_get_the_document_type($item_id) === 'attachment';
$media_items_thumbnails[] =
tainacan_get_the_media_component_slide(array(
'media_content' => get_the_post_thumbnail(null, 'tainacan-medium'),
'media_content' => get_the_post_thumbnail($item_id, 'tainacan-medium'),
'media_content_full' => $open_lightbox_on_click ? ($is_document_type_attachment ? tainacan_get_the_document($item_id, 'full') : sprintf('<div class="attachment-without-image">%s</div>', tainacan_get_the_document($item_id, 'full')) ) : '',
'media_title' => $is_document_type_attachment ? get_the_title(tainacan_get_the_document_raw($item_id)) : '',
'media_description' => $is_document_type_attachment ? get_the_content(null, false, tainacan_get_the_document_raw($item_id)) : '',

View File

@ -3,6 +3,7 @@
use \Tainacan\Entities;
use \Tainacan\Repositories;
/**
* To be used inside The Loop
*
@ -126,7 +127,7 @@ function tainacan_the_item_document_download_link($item_id = 0) {
if (!$link || $item->get_document_type() == 'text' || $item->get_document_type() == 'url')
return;
return '<a name="' . __('Download the item document', 'tainacan') . '" download="'. $link . '" href="' . $link . '">' . __('Download', 'tainacan') . '</a>';
return '<a name="' . __('Download the item document', 'tainacan') . '" download="'. esc_url($link) . '" href="' . esc_url($link) . '" target="_blank">' . __('Download', 'tainacan') . '</a>';
}
@ -137,7 +138,7 @@ function tainacan_the_item_attachment_download_link($attachment_id) {
$link = wp_get_attachment_url($attachment_id);
return '<a name="' . __('Download the item attachment', 'tainacan') . '" download="'. $link . '" href="' . $link . '">' . __('Download', 'tainacan') . '</a>';
return '<a name="' . __('Download the item attachment', 'tainacan') . '" download="'. esc_url($link) . '" href="' . esc_url($link) . '">' . __('Download', 'tainacan') . '</a>';
}
function tainacan_the_document() {
@ -212,7 +213,7 @@ function tainacan_get_the_collection_name() {
if ( $collection ) {
$name = $collection->get_name();
}
return apply_filters('tainacan-get-collection-name', $name, $collection);
return apply_filters('tainacan-get-collection-name', esc_html($name), $collection);
}
/**
@ -234,7 +235,7 @@ function tainacan_get_adjacent_items() {
* @return void
*/
function tainacan_the_collection_name() {
echo tainacan_get_the_collection_name();
echo esc_html(tainacan_get_the_collection_name());
}
/**
@ -248,7 +249,7 @@ function tainacan_get_the_collection_description() {
if ( $collection ) {
$description = $collection->get_description();
}
return apply_filters('tainacan-get-collection-description', $description, $collection);
return apply_filters('tainacan-get-collection-description', esc_html($description), $collection);
}
/**
@ -257,7 +258,7 @@ function tainacan_get_the_collection_description() {
* @return void
*/
function tainacan_the_collection_description() {
echo tainacan_get_the_collection_description();
echo esc_html(tainacan_get_the_collection_description());
}
/**
@ -344,9 +345,34 @@ function tainacan_get_the_media_component(
$args['media_main_id'] = $media_id . '-main';
$args['media_thumbs_id'] = $media_id . '-thumbs';
$args['media_id'] = $media_id;
ob_start();
if (!function_exists('tainacan_get_default_allowed_styles')) {
function tainacan_get_default_allowed_styles ( $styles ) {
$styles[] = 'display';
$styles[] = 'position';
$styles[] = 'visibility';
return $styles;
}
}
$allowed_html = array(
'svg' => array(
'xmlns' => true,
'fill' => true,
'viewbox' => true,
'role' => true,
'aria-hidden' => true,
'focusable' => true,
'width' => true,
'height' => true,
),
'path' => array(
'd' => true,
'fill' => true,
)
);
add_filter( 'safe_style_css', 'tainacan_get_default_allowed_styles');
ob_start();
if ( $args['has_media_main'] || $args['has_media_thumbs'] ) :
wp_enqueue_style( 'tainacan-media-component', $TAINACAN_BASE_URL . '/assets/css/tainacan-gutenberg-block-item-gallery.css', array(), TAINACAN_VERSION);
@ -359,39 +385,41 @@ function tainacan_get_the_media_component(
tainacan_plugin = {};
}
tainacan_plugin.tainacan_media_components = (typeof tainacan_plugin.tainacan_media_components != "undefined") ? tainacan_plugin.tainacan_media_components : {};
tainacan_plugin.tainacan_media_components['<?php echo $args['media_id'] ?>'] = <?php echo json_encode($args) ?>;
tainacan_plugin.tainacan_media_components['<?php echo esc_attr($args['media_id']) ?>'] = <?php echo json_encode($args) ?>;
</script>
<div id="<?php echo $media_id ?>" <?php echo $args['wrapper_attributes']; ?> data-module='item-gallery'>
<div id="<?php echo esc_attr($media_id) ?>" data-module="item-gallery" <?php echo wp_kses_post($args['wrapper_attributes']); ?>>
<?php if ( $args['has_media_main'] ) : ?>
<!-- Slider main container -->
<?php echo $args['before_main_div'] ?>
<div id="<?php echo $args['media_main_id'] ?>" class="tainacan-media-component__swiper-main swiper <?php echo $args['class_main_div'] ?>">
<?php echo wp_kses_post($args['before_main_div']) ?>
<div id="<?php echo esc_attr($args['media_main_id']) ?>" class="tainacan-media-component__swiper-main swiper <?php echo esc_attr($args['class_main_div']) ?>">
<!-- Additional required wrapper -->
<?php echo $args['before_main_ul'] ?>
<ul class="swiper-wrapper <?php echo $args['class_main_ul'] ?>">
<?php echo wp_kses_post($args['before_main_ul']) ?>
<ul class="swiper-wrapper <?php echo esc_attr($args['class_main_ul']) ?>">
<?php foreach($media_items_main as $media_item) { ?>
<li class="swiper-slide <?php echo $args['class_main_li'] ?>">
<?php echo $media_item ?>
<li class="swiper-slide <?php echo esc_attr($args['class_main_li']) ?>">
<?php
echo wp_kses_tainacan($media_item);
?>
</li>
<?php }; ?>
</ul>
<?php echo $args['before_main_ul'] ?>
<?php echo wp_kses_post($args['before_main_ul']) ?>
<?php if ( $args['swiper_main_options'] && isset($args['swiper_main_options']['pagination']) ) : ?>
<!-- If we need pagination -->
<div class="swiper-pagination swiper-pagination_<?php echo $args['media_main_id'] ?>"></div>
<div class="swiper-pagination swiper-pagination_<?php echo esc_attr($args['media_main_id']) ?>"></div>
<?php endif; ?>
<?php if ( $args['swiper_main_options'] && isset($args['swiper_main_options']['navigation']) ) : ?>
<!-- If we need navigation buttons -->
<div class="swiper-button-prev swiper-navigation-prev_<?php echo $args['media_main_id'] ?> <?php echo ($args['swiper_arrows_as_svg'] ? 'swiper-button-has-svg' : '' ) ?>">
<div class="swiper-button-prev swiper-navigation-prev_<?php echo esc_attr($args['media_main_id']) ?> <?php echo ($args['swiper_arrows_as_svg'] ? 'swiper-button-has-svg' : '' ) ?>">
<?php if ( $args['swiper_arrows_as_svg'] ): ?>
<?php if ( $args['swiper_arrow_prev_custom_svg'] ): ?>
<?php echo $args['swiper_arrow_prev_custom_svg']; ?>
<?php echo wp_kses($args['swiper_arrow_prev_custom_svg'], $allowed_html); ?>
<?php else: ?>
<svg width="var(--swiper-navigation-size)" height="var(--swiper-navigation-size)" viewBox="0 0 24 24">
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
@ -400,10 +428,10 @@ function tainacan_get_the_media_component(
<?php endif; ?>
<?php endif; ?>
</div>
<div class="swiper-button-next swiper-navigation-next_<?php echo $args['media_main_id'] ?> <?php echo ($args['swiper_arrows_as_svg'] ? 'swiper-button-has-svg' : '' ) ?>">
<div class="swiper-button-next swiper-navigation-next_<?php echo esc_attr($args['media_main_id']) ?> <?php echo ($args['swiper_arrows_as_svg'] ? 'swiper-button-has-svg' : '' ) ?>">
<?php if ( $args['swiper_arrows_as_svg'] ): ?>
<?php if ( $args['swiper_arrow_next_custom_svg'] ): ?>
<?php echo $args['swiper_arrow_next_custom_svg']; ?>
<?php echo wp_kses($args['swiper_arrow_next_custom_svg'], $allowed_html); ?>
<?php else: ?>
<svg width="42" height="42" viewBox="0 0 24 24">
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
@ -412,39 +440,42 @@ function tainacan_get_the_media_component(
<?php endif; ?>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
<?php echo $args['after_main_div'] ?>
<?php echo wp_kses_post($args['after_main_div']) ?>
<?php endif; ?>
<?php if ( $args['has_media_thumbs'] ) : ?>
<!-- Slider thumbs container -->
<?php echo $args['before_thumbs_div'] ?>
<div id="<?php echo $args['media_thumbs_id'] ?>" class="tainacan-media-component__swiper-thumbs swiper <?php echo $args['class_thumbs_div'] ?>">
<?php echo wp_kses_post($args['before_thumbs_div']) ?>
<div id="<?php echo esc_attr($args['media_thumbs_id']) ?>" class="tainacan-media-component__swiper-thumbs swiper <?php echo esc_attr($args['class_thumbs_div']) ?>">
<!-- Additional required wrapper -->
<?php echo $args['before_thumbs_ul'] ?>
<ul class="swiper-wrapper <?php echo $args['class_thumbs_ul'] ?>">
<?php echo wp_kses_post($args['before_thumbs_ul']) ?>
<ul class="swiper-wrapper <?php echo esc_attr($args['class_thumbs_ul']) ?>">
<?php foreach($media_items_thumbs as $media_item) { ?>
<li class="swiper-slide <?php echo $args['class_thumbs_li'] ?>">
<?php echo $media_item ?>
<li class="swiper-slide <?php echo esc_attr($args['class_thumbs_li']) ?>">
<?php echo wp_kses_tainacan($media_item); ?>
</li>
<?php }; ?>
</ul>
<?php echo $args['before_thumbs_ul'] ?>
<?php echo wp_kses_post($args['before_thumbs_ul']) ?>
<?php if ( $args['swiper_thumbs_options'] && isset($args['swiper_thumbs_options']['pagination']) ) : ?>
<!-- If we need pagination -->
<div class="swiper-paginations swiper-pagination_<?php echo $args['media_thumbs_id'] ?>"></div>
<div class="swiper-paginations swiper-pagination_<?php echo esc_attr($args['media_thumbs_id']) ?>"></div>
<?php endif; ?>
<?php if ( $args['swiper_thumbs_options'] && isset($args['swiper_thumbs_options']['navigation']) ) : ?>
<!-- If we need navigation buttons -->
<div class="swiper-button-prev swiper-navigation-prev_<?php echo $args['media_thumbs_id'] ?> <?php echo ($args['swiper_arrows_as_svg'] ? 'swiper-button-has-svg' : '' ) ?>">
<div class="swiper-button-prev swiper-navigation-prev_<?php echo esc_attr($args['media_thumbs_id']) ?> <?php echo ($args['swiper_arrows_as_svg'] ? 'swiper-button-has-svg' : '' ) ?>">
<?php if ( $args['swiper_arrows_as_svg'] ): ?>
<?php if ( $args['swiper_arrow_prev_custom_svg'] ): ?>
<?php echo $args['swiper_arrow_prev_custom_svg']; ?>
<?php echo wp_kses($args['swiper_arrow_prev_custom_svg'], $allowed_html); ?>
<?php else: ?>
<svg width="var(--swiper-navigation-size)" height="var(--swiper-navigation-size)" viewBox="0 0 24 24">
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"/>
@ -453,10 +484,10 @@ function tainacan_get_the_media_component(
<?php endif; ?>
<?php endif; ?>
</div>
<div class="swiper-button-next swiper-navigation-next_<?php echo $args['media_thumbs_id'] ?> <?php echo ($args['swiper_arrows_as_svg'] ? 'swiper-button-has-svg' : '' ) ?>">
<div class="swiper-button-next swiper-navigation-next_<?php echo esc_attr($args['media_thumbs_id']) ?> <?php echo ($args['swiper_arrows_as_svg'] ? 'swiper-button-has-svg' : '' ) ?>">
<?php if ( $args['swiper_arrows_as_svg'] ): ?>
<?php if ( $args['swiper_arrow_next_custom_svg'] ): ?>
<?php echo $args['swiper_arrow_next_custom_svg']; ?>
<?php echo wp_kses($args['swiper_arrow_next_custom_svg'], $allowed_html); ?>
<?php else: ?>
<svg width="42" height="42" viewBox="0 0 24 24">
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
@ -471,15 +502,13 @@ function tainacan_get_the_media_component(
<div class="swiper-start-border"></div>
<div class="swiper-end-border"></div>
</div>
<?php echo $args['after_thumbs_div'] ?>
<?php echo wp_kses_post($args['after_thumbs_div']) ?>
<?php endif; ?>
</div>
<?php endif; ?> <!-- End of if ($args['has_media_main'] || $args['has_media_thumbs'] ) -->
<?php
endif; // <!-- End of if ($args['has_media_main'] || $args['has_media_thumbs'] ) -->
remove_filter( 'safe_style_css', 'tainacan_get_default_allowed_styles');
$content = ob_get_contents();
ob_end_clean();
@ -528,49 +557,51 @@ function tainacan_get_the_media_component_slide( $args = array() ) {
ob_start();
?>
<?php echo $args['before_slide_content'] ?>
<?php echo wp_kses_post($args['before_slide_content']) ?>
<div class="swiper-slide-content <?php echo $args['class_slide_content'] ?>">
<div class="swiper-slide-content <?php echo esc_attr($args['class_slide_content']) ?>">
<?php if ( isset($args['media_content']) && !empty($args['media_content']) && $args['media_content'] !== false ) :?>
<?php echo $args['media_content'] ?>
<?php echo wp_kses_tainacan($args['media_content']) ?>
<?php else: ?>
<img src="<?php echo tainacan_get_the_mime_type_icon($args['media_type']) ?>" alt="<?php echo ( !empty($args['media_title']) ? $args['media_title'] : __('File', 'tainacan') ) ?>" >
<img src="<?php echo esc_url(tainacan_get_the_mime_type_icon($args['media_type'])) ?>" alt="<?php echo ( !empty($args['media_title']) ? esc_attr($args['media_title']) : __('File', 'tainacan') ) ?>" >
<?php endif; ?>
<?php echo $args['before_slide_metadata'] ?>
<?php echo wp_kses_post($args['before_slide_metadata']); ?>
<?php if ( !empty($args['media_title']) || !empty($args['description']) || !empty($args['media_caption']) ) : ?>
<div class="swiper-slide-metadata <?php echo $args['class_slide_metadata'] ?>">
<div class="swiper-slide-metadata <?php echo wp_kses_post($args['class_slide_metadata']); ?>">
<?php if ( !empty($args['media_caption']) ) :?>
<span class="swiper-slide-metadata__caption">
<?php echo $args['media_caption'] ?>
<?php echo wp_kses_post($args['media_caption']); ?>
<br>
</span>
<?php endif; ?>
<?php if ( !empty($args['media_title']) ) :?>
<span class="swiper-slide-metadata__name">
<?php echo $args['media_title'] ?>
<?php echo wp_kses_post($args['media_title']); ?>
</span>
<?php endif; ?>
<br>
<?php if ( !empty($args['media_description']) ) :?>
<span class="swiper-slide-metadata__description">
<?php echo $args['media_description'] ?>
<?php echo wp_kses_post($args['media_description']); ?>
</span>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if ( !empty($args['media_content_full']) ) : ?>
<div class="media-full-content" style="display: none; position: absolute; visibility: hidden;"><?php echo $args['media_content_full'] ?></div>
<div class="media-full-content" style="display: none; position: absolute; visibility: hidden;">
<?php echo wp_kses_tainacan($args['media_content_full']) ?>
</div>
<?php endif; ?>
<?php echo $args['after_slide_metadata'] ?>
<?php echo wp_kses_post($args['after_slide_metadata']) ?>
</div>
<?php echo $args['after_slide_content'] ?>
<?php echo wp_kses_post($args['after_slide_content']) ?>
<?php
@ -592,7 +623,7 @@ function tainacan_get_the_collection_url() {
if ( $collection ) {
$url = $collection->get_url();
}
return apply_filters('tainacan-get-collection-url', $url, $collection);
return apply_filters('tainacan-get-collection-url', esc_url($url), $collection);
}
@ -602,7 +633,7 @@ function tainacan_get_the_collection_url() {
* @return void
*/
function tainacan_the_collection_url() {
echo tainacan_get_the_collection_url();
echo esc_url(tainacan_get_the_collection_url());
}
@ -726,7 +757,7 @@ function tainacan_get_the_term_name() {
if ( $term ) {
$name = $term->name;
}
return apply_filters('tainacan-get-term-name', $name, $term);
return apply_filters('tainacan-get-term-name', esc_html($name), $term);
}
/**
@ -735,7 +766,7 @@ function tainacan_get_the_term_name() {
* @return void
*/
function tainacan_the_term_name() {
echo tainacan_get_the_term_name();
echo esc_html(tainacan_get_the_term_name());
}
/**
@ -749,7 +780,7 @@ function tainacan_get_the_term_description() {
if ( $term ) {
$description = $term->description;
}
return apply_filters('tainacan-get-term-description', $description, $term);
return apply_filters('tainacan-get-term-description', esc_html($description), $term);
}
/**
@ -758,7 +789,7 @@ function tainacan_get_the_term_description() {
* @return void
*/
function tainacan_the_term_description() {
echo tainacan_get_the_term_description();
echo esc_html(tainacan_get_the_term_description());
}
/**
@ -861,9 +892,9 @@ function tainacan_the_item_edit_link( $text = null, $before = '', $after = '', $
$text = __( 'Edit this item', 'tainacan' );
}
$link = '<a class="' . esc_attr( $class ) . '" href="' . esc_url( $url ) . '">' . $text . '</a>';
$link = '<a class="' . esc_attr($class) . '" href="' . esc_url( $url ) . '">' . $text . '</a>';
echo $before . $link . $after;
echo wp_kses_post($before . $link . $after);
}
/**
@ -1008,7 +1039,7 @@ function tainacan_get_the_mime_type_icon($mime_type, $image_size = 'medium') {
* @type integer $auto_play_speed The time in s to translate to the next slide automatically
* @type bool $loop_slides Should slides loop when reached the end of the Carousel?
* @type bool $hide_title Should the title of the items be displayed?
* @type bool $crop_images_to_square Should it use the `tainacan-medium-size` instead of the `tainacan-medium-large-size`?
* @type string $image_size Item image size. Defaults to 'tainacan-medium'
* @type bool $show_collection_header Should it display a small version of the collection header?
* @type bool $show_collection_label Should it displar a 'Collection' label before the collection name on the collection header?
* @type string $collection_background_color Color of the collection header background
@ -1102,4 +1133,69 @@ function tainacan_has_related_items($item_id = false) {
*/
function tainacan_the_item_gallery($args = []) {
echo \Tainacan\Theme_Helper::get_instance()->get_tainacan_item_gallery($args);
}
/**
* Render the item metadata sections as a HTML string.
*
* Each metadata section is a label with the list of its metadata name and value.
*
* If an ID, a slug or a Tainacan\Entities\Metadata_Section object is passed in the 'metadata_section' argument, it returns only one metadata section, otherwise
* it returns all metadata section
*
* @param array|string $args {
* Optional. Array or string of arguments.
*
* @type mixed $metadata_section Metadatum object, ID or slug to retrieve only one metadatum. empty returns all metadata_sections
*
* @type array $metadata_sections__in Array of metadata_sections IDs or Slugs to be retrieved. Default none
*
* @type array $metadata_sections__not_in Array of metadata_sections IDs (slugs not accepted) to excluded. Default none
*
* @type bool $hide_name Do not display the Metadata Section name. Default false
*
* @type bool $hide_description Do not display the Metadata Section description. Default true
*
* @type bool $hide_empty Wether to hide or not metadata sections if there are no metadata list or they are empty
* Default: true
* @type string $empty_metadata_list_message Message string to display if $hide_empty is false and there is not metadata section metadata list.
* Default: ''
* @type bool $display_slug_as_class Show metadata slug as a class in the div before the metadata block
* Default: true
* @type string $before String to be added before each metadata section block
* Default '<section $id>'
* @type string $after String to be added after each metadata section block
* Default '</section>'
* @type string $before_name String to be added before each metadata section name
* Default '<h2>'
* @type string $after_name String to be added after each metadata section name
* Default '</h2>'
* @type string $before_description String to be added before each metadata section description
* Default '<p>'
* @type string $after_description String to be added after each metadata section description
* Default '</p>'
* @type string $before_metadata_list String to be added before each metadata section inner metadata list
* Default '<div class="metadata-section__metadata-list">'
* @type string $after_metadata_list String to be added after each metadata section inner metadata list
* Default '</div>'
* @type array $metadata_list_args Arguments to be passed to the get_metadata_as_html function when calling section metadata
* }
*
* @return string The HTML output
*/
function tainacan_get_the_metadata_sections($args = array(), $item_id = 0) {
$item = tainacan_get_item( $item_id );
if ($item instanceof \Tainacan\Entities\Item) {
return $item->get_metadata_sections_as_html($args);
}
return '';
}
function tainacan_the_metadata_sections($args = array()) {
echo tainacan_get_the_metadata_sections($args);
}

View File

@ -480,6 +480,23 @@ class Migrations {
}
}
static function insert_meta_default_metadata_section() {
global $wpdb;
// create metadata
$wpdb->query(
$wpdb->prepare(
"INSERT INTO $wpdb->postmeta (post_id,meta_key,meta_value)
SELECT ID,'metadata_section_id', %s FROM $wpdb->posts
WHERE post_type = %s AND ID NOT IN (
SELECT post_id FROM $wpdb->postmeta WHERE meta_key = %s
)"
,\Tainacan\Entities\Metadata_Section::$default_section_slug
,\Tainacan\Entities\Metadatum::$post_type
,\Tainacan\Entities\Metadata_Section::$default_section_slug
)
);
}
}

View File

@ -2,9 +2,9 @@
Contributors: andrebenedito, daltonmartins, fabianobn, jacsonp, leogermani, weryques, wetah, eduardohumberto, ravipassos, jessicafpx, marinagiolo, omarceloavila, vnmedeiros, tainacan, r-guimaraes, suelanesilva, ccaio, alanargomes, ateneagarcia123, rodrigo0freire, clarandreozzi
Tags: museums, libraries, archives, GLAM, collections, repository
Requires at least: 5.0
Tested up to: 5.9
Tested up to: 6.0
Requires PHP: 5.6
Stable tag: 0.18.8
Stable tag: 0.19
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-3.0.html

View File

@ -4,17 +4,17 @@ Plugin Name: Tainacan
Plugin URI: https://tainacan.org/
Description: Open source, powerful 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 repository platform.
Author: Tainacan.org
Version: 0.18.8
Version: 0.19
Requires at least: 5.0
Tested up to: 5.9
Tested up to: 6.0
Requires PHP: 5.6
Stable tag: 0.18.8
Stable tag: 0.19
Text Domain: tainacan
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-3.0.html
*/
const TAINACAN_VERSION = '0.18.8';
const TAINACAN_VERSION = '0.19';
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
$TAINACAN_BASE_URL = plugins_url('', __FILE__);
@ -35,8 +35,20 @@ add_action( 'after_setup_theme', function() {
add_image_size( 'tainacan-small', 40, 40, true );
add_image_size( 'tainacan-medium', 275, 275, true );
add_image_size( 'tainacan-medium-full', 205, 1500 );
add_image_size( 'tainacan-large-full', 480, 860 );
} );
// This enables Tainacan media sizes in the admin interface, including Gutenberg blocks
add_filter( 'image_size_names_choose', function ( $sizes ) {
return array_merge( $sizes, array(
'tainacan-small' => __( 'Tainacan small (40x40 - cropped)', 'tainacan' ),
'tainacan-medium' => __( 'Tainacan medium (275x275 - cropped)', 'tainacan' ),
'tainacan-medium-full' => __( 'Tainacan medium full (205x1500 - not cropped)', 'tainacan' ),
'tainacan-large-full' => __( 'Tainacan large full (480x860 - not cropped)', 'tainacan' )
) );
} );
add_action('init', ['Tainacan\Migrations', 'run_migrations']);
//https://core.trac.wordpress.org/ticket/23022
@ -122,4 +134,28 @@ function tainacan_add_admin_bar_items ( WP_Admin_Bar $admin_bar ) {
}
}
}
add_action( 'admin_bar_menu', 'tainacan_add_admin_bar_items', 500 );
add_action( 'admin_bar_menu', 'tainacan_add_admin_bar_items', 500 );
function wp_kses_tainacan($content, $context='tainacan_content') {
$allowed_html = wp_kses_allowed_html($context);
return wp_kses($content, $allowed_html);
}
add_filter('wp_kses_allowed_html', function($allowedposttags, $context) {
switch ( $context ) {
case 'tainacan_content':
$post_allowed_html = wp_kses_allowed_html('post');
return array_merge(
$post_allowed_html,
['iframe' => array(
'src' => true,
'height' => true,
'width' => true,
'frameborder' => true,
'allowfullscreen' => true,
)]
);
default:
return $allowedposttags;
}
}, 10, 2);

View File

@ -41,7 +41,8 @@
:is-menu-compressed="isMenuCompressed"/>
<div
id="repository-container"
class="column is-main-content">
class="column is-main-content"
:style="$adminOptions.hidePrimaryMenu ? '--tainacan-sidebar-width: 0px' : ''">
<router-view />
</div>
</template>
@ -79,14 +80,14 @@
switch (amountOfElementsAbove) {
case 3:
return 'calc(3.05em + 12px)';
return 'calc(2.05em + 12px)';
case 2:
return 'calc(5.65em + 12px)';
return 'calc(4.65em + 12px)';
case 1:
return 'calc(8.5em + 12px)';
return 'calc(7.5em + 12px)';
case 0:
default:
return 'calc(11.125em + 12px)';
return 'calc(10.125em + 12px)';
}
}
},
@ -167,7 +168,7 @@
}
}
#primary-menu.is-compressed~.is-main-content {
--tainacan-sidebar-width: 3.25em;
--tainacan-sidebar-width: 3.0em;
}
#primary-menu:not(.is-compressed)~.is-main-content {
--tainacan-sidebar-width: 10em;
@ -175,15 +176,15 @@
.is-secondary-content {
padding: 0px !important;
margin: 5.875em auto 0 auto;
margin: 5.4em auto 0 auto;
position: relative;
overflow-y: hidden;
overflow-x: hidden;
height: calc(100% - 5.875em);
height: calc(100vh - 5.4em);
@media screen and (max-width: 769px) {
overflow-y: visible;
margin: 40px auto 0 auto;
margin: 38px auto 0 auto;
}
@ -196,7 +197,7 @@
#menu-compress-button {
position: absolute;
z-index: 999;
top: calc(11.125em + 12px);
top: calc(10.125em + 12px);
left: 0px;
max-width: 1.5625em;
height: 1.5625em;

View File

@ -31,7 +31,7 @@ class Admin_Hooks {
}
public function get_available_contexts() {
return apply_filters('tainacan-admin-hooks-contexts', ['collection', 'metadatum', 'item', 'taxonomy', 'term', 'filter', 'role']);
return apply_filters('tainacan-admin-hooks-contexts', ['collection', 'metadatum', 'item', 'taxonomy', 'term', 'filter', 'role', 'metadataSection']);
}
public function get_registered_hooks() {
@ -49,7 +49,7 @@ class Admin_Hooks {
$contexts = $this->get_available_contexts();
$positions = $this->get_available_positions();
if ( !in_array($context, $contexts) || !in_array($position, $positions) ) {
return false;
}

View File

@ -11,7 +11,7 @@
label-width="120px">
<div class="columns">
<div class="column is-5">
<div class="column">
<!-- Name -------------------------------- -->
<b-field
@ -25,6 +25,7 @@
:message="$i18n.getHelperMessage('collections', 'name')"/>
<b-input
id="tainacan-text-name"
:placeholder="$i18n.get('instruction_collection_name')"
v-model="form.name"
@blur="updateSlug"
@focus="clearErrors('name')"/>
@ -38,22 +39,410 @@
v-html="getBeginLeftForm"/>
</template>
<!-- Thumbnail -------------------------------- -->
<!-- Description -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_description')"
:type="editFormErrors['description'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['description'] != undefined ? editFormErrors['description'] : ''">
<help-button
:title="$i18n.getHelperTitle('collections', 'description')"
:message="$i18n.getHelperMessage('collections', 'description')"/>
<b-input
id="tainacan-text-description"
type="textarea"
rows="3"
:placeholder="$i18n.get('instruction_collection_description')"
v-model="form.description"
@focus="clearErrors('description')"/>
</b-field>
<!-- Slug -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_slug')"
:type="editFormErrors['slug'] != undefined ? 'is-danger' : ''"
:message="isUpdatingSlug ? $i18n.get('info_validating_slug') : (editFormErrors['slug'] != undefined ? editFormErrors['slug'] : '')">
<help-button
:title="$i18n.getHelperTitle('collections', 'slug')"
:message="$i18n.getHelperMessage('collections', 'slug')"/>
<b-input
id="tainacan-text-slug"
@input="updateSlug"
v-model="form.slug"
@focus="clearErrors('slug')"
:disabled="isUpdatingSlug"
:loading="isUpdatingSlug"/>
</b-field>
<!-- Change Default OrderBy Select and Order Button-->
<b-field
:addons="false"
:label="$i18n.get('label_default_orderby')"
:type="editFormErrors['default_orderby'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['default_orderby'] != undefined ? editFormErrors['default_orderby'] : $i18n.get('info_default_orderby')">
<help-button
:title="$i18n.getHelperTitle('collections', 'default_orderby')"
:message="$i18n.getHelperMessage('collections', 'default_orderby')"/>
<div class="control sorting-options">
<label class="label">{{ $i18n.get('label_sort') }}&nbsp;</label>
<b-select
id="tainacan-select-default_order"
v-model="form.default_order">
<option
role="button"
:class="{ 'is-active': form.default_order == 'DESC' }"
:value="'DESC'">
{{ $i18n.get('label_descending') }}
</option>
<option
role="button"
:class="{ 'is-active': form.default_order == 'ASC' }"
:value="'ASC'">
{{ $i18n.get('label_ascending') }}
</option>
</b-select>
<span
class="label"
style="padding: 0 0.65em;">
{{ $i18n.get('info_by_inner') }}
</span>
<b-select
expanded
:loading="isLoadingMetadata"
v-model="localDefaultOrderBy"
id="tainacan-select-default_orderby">
<option
v-for="metadatum of sortingMetadata"
:value="metadatum.id"
:key="metadatum.id">
{{ metadatum.name }}
</option>
</b-select>
</div>
</b-field>
<label class="label">{{ $i18n.get('label_view_modes_public_list') }}</label>
<div class="items-view-mode-options">
<!-- Enabled View Modes ------------------------------- -->
<div class="field">
<label class="label">{{ $i18n.get('label_view_modes_available') }}</label>
<help-button
:title="$i18n.getHelperTitle('collections', 'enabled_view_modes')"
:message="$i18n.getHelperMessage('collections', 'enabled_view_modes')"/>
<div class="control">
<b-dropdown
class="enabled-view-modes-dropdown"
ref="enabledViewModesDropdown"
:mobile-modal="true"
:disabled="Object.keys(registeredViewModes).length < 0"
aria-role="list"
trap-focus
position="is-top-right">
<button
class="button is-white"
slot="trigger"
position="is-top-right"
type="button">
<span>{{ $i18n.get('label_enabled_view_modes') }}</span>
<span class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-arrowdown"/>
</span>
</button>
<b-dropdown-item
v-for="(viewMode, index) in Object.keys(registeredViewModes)"
:key="index"
custom
aria-role="listitem">
<b-checkbox
v-if="registeredViewModes[viewMode] != undefined"
@input="updateViewModeslist(viewMode)"
:value="checkIfViewModeEnabled(viewMode)"
:disabled="checkIfViewModeEnabled(viewMode) && form.enabled_view_modes.filter((aViewMode) => (registeredViewModes[aViewMode] && registeredViewModes[aViewMode].full_screen != true)).length <= 1">
<p>
<strong>
<span
class="gray-icon"
:class="{
'has-text-secondary' : checkIfViewModeEnabled(viewMode),
'has-text-gray4' : !checkIfViewModeEnabled(viewMode)
}"
v-html="registeredViewModes[viewMode].icon"/>
&nbsp;{{ registeredViewModes[viewMode].label }}
</strong>
</p>
<p v-if="registeredViewModes[viewMode].description">{{ registeredViewModes[viewMode].description }}</p>
</b-checkbox>
</b-dropdown-item>
</b-dropdown>
</div>
</div>
<!-- Default View Mode -------------------------------- -->
<b-field
v-if="form.enabled_view_modes.length > 0"
:addons="false"
:label="$i18n.get('label_default')"
:type="editFormErrors['default_view_mode'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['default_view_mode'] != undefined ? editFormErrors['default_view_mode'] : ''">
<help-button
:title="$i18n.getHelperTitle('collections', 'default_view_mode')"
:message="$i18n.getHelperMessage('collections', 'default_view_mode')"/>
<b-select
expanded
id="tainacan-select-default_view_mode"
v-model="form.default_view_mode"
@focus="clearErrors('default_view_mode')">
<option
v-for="(viewMode, index) of form.enabled_view_modes"
v-if="registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen != true"
:key="index"
:value="viewMode">
{{ registeredViewModes[viewMode].label }}
</option>
</b-select>
</b-field>
</div>
<!-- Hide Items Thumbnail on Lists ------------------------ -->
<b-field
:addons="false"
:label="$i18n.getHelperTitle('collections', 'hide_items_thumbnail_on_lists')">
&nbsp;
<b-switch
id="tainacan-checkbox-hide-items-thumbnail-on-lists"
size="is-small"
true-value="yes"
false-value="no"
v-model="form.hide_items_thumbnail_on_lists" />
<help-button
:title="$i18n.getHelperTitle('collections', 'hide_items_thumbnail_on_lists')"
:message="$i18n.getHelperMessage('collections', 'hide_items_thumbnail_on_lists')"/>
</b-field>
<!-- Comment Status ------------------------ -->
<b-field
:addons="false"
:label="$i18n.getHelperTitle('collections', 'allow_comments')">
&nbsp;
<b-switch
id="tainacan-checkbox-comment-status"
size="is-small"
true-value="open"
false-value="closed"
v-model="form.allow_comments" />
<help-button
:title="$i18n.getHelperTitle('collections', 'allow_comments')"
:message="$i18n.getHelperMessage('collections', 'allow_comments')"/>
</b-field>
<!-- Allows Submissions ------------------------ -->
<b-field
:addons="false"
:label="$i18n.getHelperTitle('collections', 'allows_submission')"
:type="editFormErrors['allows_submission'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['allows_submission'] != undefined ? editFormErrors['allows_submission'] : ''">
&nbsp;
<b-switch
id="tainacan-checkbox-allow-submission"
size="is-small"
true-value="yes"
false-value="no"
v-model="form.allows_submission" />
<help-button
:title="$i18n.getHelperTitle('collections', 'allows_submission')"
:message="$i18n.getHelperMessage('collections', 'allows_submission')"/>
</b-field>
<transition name="filter-item">
<div
v-if="form.allows_submission === 'yes'"
class="item-submission-options">
<!-- Allows Submissions by anonynmous user ------------------------ -->
<b-field
:addons="false"
:label="$i18n.getHelperTitle('collections', 'submission_anonymous_user')"
:type="editFormErrors['submission_anonymous_user'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['submission_anonymous_user'] != undefined ? editFormErrors['submission_anonymous_user'] : ''">
&nbsp;
<b-switch
id="tainacan-checkbox-allow-submission"
size="is-small"
true-value="yes"
false-value="no"
v-model="form.submission_anonymous_user" />
<help-button
:title="$i18n.getHelperTitle('collections', 'submission_anonymous_user')"
:message="$i18n.getHelperMessage('collections', 'submission_anonymous_user')"/>
</b-field>
<!-- Item submission default Status -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.getHelperTitle('collections', 'submission_default_status')"
:type="editFormErrors['submission_default_status'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['submission_default_status'] != undefined ? editFormErrors['submission_default_status'] : ''">
<help-button
:title="$i18n.getHelperTitle('collections', 'submission_default_status')"
:message="$i18n.getHelperMessage('collections', 'submission_default_status')"/>
<div class="status-radios">
<b-radio
v-model="form.submission_default_status"
v-for="(statusOption, index) of $statusHelper.getStatuses().filter((status) => status.slug != 'trash')"
:key="index"
:native-value="statusOption.slug">
<span class="icon has-text-gray">
<i
class="tainacan-icon tainacan-icon-18px"
:class="$statusHelper.getIcon(statusOption.slug)"/>
</span>
{{ statusOption.name }}
</b-radio>
</div>
</b-field>
<!-- Submission process uses reCAPTCHA ------------------------ -->
<b-field
:addons="false"
:label="$i18n.getHelperTitle('collections', 'submission_use_recaptcha')"
:type="editFormErrors['submission_use_recaptcha'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['submission_use_recaptcha'] != undefined ? editFormErrors['submission_use_recaptcha'] : ''">
&nbsp;
<b-switch
id="tainacan-checkbox-submission-use-recaptcha"
size="is-small"
true-value="yes"
false-value="no"
v-model="form.submission_use_recaptcha" />
<help-button
:title="$i18n.getHelperTitle('collections', 'submission_use_recaptcha')"
:message="$i18n.getHelperMessage('collections', 'submission_use_recaptcha')"/>
<p
v-if="form.submission_use_recaptcha == 'yes'"
v-html="$i18n.getWithVariables('info_recaptcha_link_%s', [ reCAPTCHASettingsPagePath ])" />
</b-field>
</div>
</transition>
<!-- Hook for extra Form options -->
<template v-if="hasEndLeftForm">
<form
ref="form-collection-end-left"
id="form-collection-end-left"
class="form-hook-region"
v-html="getEndLeftForm"/>
</template>
</div>
<div class="column">
<!-- Status -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_status')"
:type="editFormErrors['status'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['status'] != undefined ? editFormErrors['status'] : ''">
<help-button
:title="$i18n.getHelperTitle('collections', 'status')"
:message="$i18n.getHelperMessage('collections', 'status')"/>
<div class="status-radios">
<b-radio
v-model="form.status"
v-for="(statusOption, index) of $statusHelper.getStatuses().filter((status) => status.slug != 'draft')"
:key="index"
:native-value="statusOption.slug">
<span class="icon has-text-gray">
<i
class="tainacan-icon tainacan-icon-18px"
:class="$statusHelper.getIcon(statusOption.slug)"/>
</span>
{{ statusOption.name }}
</b-radio>
</div>
</b-field>
<!-- Hook for extra Form options -->
<template v-if="hasBeginRightForm">
<form
id="form-collection-begin-right"
class="form-hook-region"
v-html="getBeginRightForm"/>
</template>
<!-- Image thumbnail & Header Image -------------------------------- -->
<b-field :addons="false">
<label class="label">{{ $i18n.get('label_thumbnail') }}</label>
<label class="label">
{{ $i18n.get('label_thumbnail') }} & {{ $i18n.get('label_header_image') }}
<help-button
:title="$i18n.get('label_thumbnail') + ' & ' + $i18n.get('label_header_image')"
:message="$i18n.get('info_collection_thumbnail_and_header')"/>
</label>
<!-- Header Image -------------------------------- -->
<div class="header-field">
<figure class="image">
<span
v-if="collection.header_image == undefined || collection.header_image == false"
class="image-placeholder">{{ $i18n.get('label_empty_header_image') }}</span>
<img
:alt="$i18n.get('label_thumbnail')"
:src="(collection.header_image == undefined || collection.header_image == false) ? headerPlaceholderPath : collection.header_image">
</figure>
<div class="header-buttons-row">
<a
class="button is-rounded is-secondary"
id="button-edit-header-image"
:aria-label="$i18n.get('label_button_edit_header_image')"
@click="headerImageMediaFrame.openFrame($event)">
<span
v-tooltip="{
content: $i18n.get('edit'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-edit"/>
</span>
</a>
<a
class="button is-rounded is-secondary"
id="button-delete-header-image"
:aria-label="$i18n.get('label_button_delete_thumb')"
@click="deleteHeaderImage()">
<span
v-tooltip="{
content: $i18n.get('delete'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-delete"/>
</span>
</a>
</div>
</div>
<!-- Thumbnail -------------------------------- -->
<div class="thumbnail-field">
<file-item
v-if="collection.thumbnail != undefined && ((collection.thumbnail['tainacan-medium'] != undefined && collection.thumbnail['tainacan-medium'] != false) || (collection.thumbnail.medium != undefined && collection.thumbnail.medium != false))"
:show-name="false"
:modal-on-click="true"
:size="178"
:size="146"
:file="{
media_type: 'image',
thumbnails: { 'tainacan-medium': [ $thumbHelper.getSrc(collection['thumbnail'], 'tainacan-medium') ] },
title: $i18n.get('label_thumbnail'),
description: `<img alt='` + $i18n.get('label_thumbnail') + `' src='` + $thumbHelper.getSrc(collection['thumbnail'], 'full') + `'/>`
}"/>
<figure
<figure
v-if="collection.thumbnail == undefined || ((collection.thumbnail.medium == undefined || collection.thumbnail.medium == false) && (collection.thumbnail['tainacan-medium'] == undefined || collection.thumbnail['tainacan-medium'] == false))"
class="image">
<span class="image-placeholder">{{ $i18n.get('label_empty_thumbnail') }}</span>
@ -200,257 +589,6 @@
{{ $i18n.get('label_create_new_page') }}</a>
</b-field>
<!-- Change Default OrderBy Select and Order Button-->
<b-field
:addons="false"
:label="$i18n.get('label_default_orderby')"
:type="editFormErrors['default_orderby'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['default_orderby'] != undefined ? editFormErrors['default_orderby'] : $i18n.get('info_default_orderby')">
<help-button
:title="$i18n.getHelperTitle('collections', 'default_orderby')"
:message="$i18n.getHelperMessage('collections', 'default_orderby')"/>
<div class="control sorting-options">
<label class="label">{{ $i18n.get('label_sort') }}&nbsp;</label>
<b-select
id="tainacan-select-default_order"
v-model="form.default_order">
<option
role="button"
:class="{ 'is-active': form.default_order == 'DESC' }"
:value="'DESC'">
{{ $i18n.get('label_descending') }}
</option>
<option
role="button"
:class="{ 'is-active': form.default_order == 'ASC' }"
:value="'ASC'">
{{ $i18n.get('label_ascending') }}
</option>
</b-select>
<span
class="label"
style="padding: 0 0.65em;">
{{ $i18n.get('info_by_inner') }}
</span>
<b-select
expanded
:loading="isLoadingMetadata"
v-model="localDefaultOrderBy"
id="tainacan-select-default_orderby">
<option
v-for="metadatum of sortingMetadata"
:value="metadatum.id"
:key="metadatum.id">
{{ metadatum.name }}
</option>
</b-select>
</div>
</b-field>
<!-- Hide Items Thumbnail on Lists ------------------------ -->
<b-field
:addons="false"
:label="$i18n.getHelperTitle('collections', 'hide_items_thumbnail_on_lists')">
&nbsp;
<b-switch
id="tainacan-checkbox-hide-items-thumbnail-on-lists"
size="is-small"
true-value="yes"
false-value="no"
v-model="form.hide_items_thumbnail_on_lists" />
<help-button
:title="$i18n.getHelperTitle('collections', 'hide_items_thumbnail_on_lists')"
:message="$i18n.getHelperMessage('collections', 'hide_items_thumbnail_on_lists')"/>
</b-field>
<!-- Enabled View Modes ------------------------------- -->
<div class="field">
<label class="label">{{ $i18n.get('label_view_modes_available') }}</label>
<help-button
:title="$i18n.getHelperTitle('collections', 'enabled_view_modes')"
:message="$i18n.getHelperMessage('collections', 'enabled_view_modes')"/>
<div class="control">
<b-dropdown
class="two-columns-dropdown enabled-view-modes-dropdown"
ref="enabledViewModesDropdown"
:mobile-modal="true"
:disabled="Object.keys(registeredViewModes).length < 0"
aria-role="list"
trap-focus>
<button
class="button is-white"
slot="trigger"
position="is-top-right"
type="button">
<span>{{ $i18n.get('label_enabled_view_modes') }}</span>
<span class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-arrowdown"/>
</span>
</button>
<b-dropdown-item
v-for="(viewMode, index) in Object.keys(registeredViewModes)"
:key="index"
custom
aria-role="listitem">
<b-checkbox
v-if="registeredViewModes[viewMode] != undefined"
@input="updateViewModeslist(viewMode)"
:value="checkIfViewModeEnabled(viewMode)"
:disabled="checkIfViewModeEnabled(viewMode) && form.enabled_view_modes.filter((aViewMode) => (registeredViewModes[aViewMode] && registeredViewModes[aViewMode].full_screen != true)).length <= 1">
<p>
<strong>
<span
class="gray-icon"
:class="{
'has-text-secondary' : checkIfViewModeEnabled(viewMode),
'has-text-gray4' : !checkIfViewModeEnabled(viewMode)
}"
v-html="registeredViewModes[viewMode].icon"/>
&nbsp;{{ registeredViewModes[viewMode].label }}
</strong>
</p>
<p v-if="registeredViewModes[viewMode].description">{{ registeredViewModes[viewMode].description }}</p>
</b-checkbox>
</b-dropdown-item>
</b-dropdown>
</div>
</div>
<!-- Default View Mode -------------------------------- -->
<b-field
v-if="form.enabled_view_modes.length > 0"
:addons="false"
:label="$i18n.get('label_default_view_mode')"
:type="editFormErrors['default_view_mode'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['default_view_mode'] != undefined ? editFormErrors['default_view_mode'] : ''">
<help-button
:title="$i18n.getHelperTitle('collections', 'default_view_mode')"
:message="$i18n.getHelperMessage('collections', 'default_view_mode')"/>
<b-select
expanded
id="tainacan-select-default_view_mode"
v-model="form.default_view_mode"
@focus="clearErrors('default_view_mode')">
<option
v-for="(viewMode, index) of form.enabled_view_modes"
v-if="registeredViewModes[viewMode] != undefined && registeredViewModes[viewMode].full_screen != true"
:key="index"
:value="viewMode">
{{ registeredViewModes[viewMode].label }}
</option>
</b-select>
</b-field>
<!-- Hook for extra Form options -->
<template v-if="hasEndLeftForm">
<form
ref="form-collection-end-left"
id="form-collection-end-left"
class="form-hook-region"
v-html="getEndLeftForm"/>
</template>
</div>
<div class="column">
<!-- Status -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_status')"
:type="editFormErrors['status'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['status'] != undefined ? editFormErrors['status'] : ''">
<help-button
:title="$i18n.getHelperTitle('collections', 'status')"
:message="$i18n.getHelperMessage('collections', 'status')"/>
<div class="status-radios">
<b-radio
v-model="form.status"
v-for="(statusOption, index) of $statusHelper.getStatuses().filter((status) => status.slug != 'draft')"
:key="index"
:native-value="statusOption.slug">
<span class="icon has-text-gray">
<i
class="tainacan-icon tainacan-icon-18px"
:class="$statusHelper.getIcon(statusOption.slug)"/>
</span>
{{ statusOption.name }}
</b-radio>
</div>
</b-field>
<!-- Header Image -------------------------------- -->
<b-field :addons="false">
<label class="label">{{ $i18n.get('label_header_image') }}</label>
<div class="header-field">
<figure class="image">
<span
v-if="collection.header_image == undefined || collection.header_image == false"
class="image-placeholder">{{ $i18n.get('label_empty_header_image') }}</span>
<img
:alt="$i18n.get('label_thumbnail')"
:src="(collection.header_image == undefined || collection.header_image == false) ? headerPlaceholderPath : collection.header_image">
</figure>
<div class="header-buttons-row">
<a
class="button is-rounded is-secondary"
id="button-edit-header-image"
:aria-label="$i18n.get('label_button_edit_header_image')"
@click="headerImageMediaFrame.openFrame($event)">
<span
v-tooltip="{
content: $i18n.get('edit'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-edit"/>
</span>
</a>
<a
class="button is-rounded is-secondary"
id="button-delete-header-image"
:aria-label="$i18n.get('label_button_delete_thumb')"
@click="deleteHeaderImage()">
<span
v-tooltip="{
content: $i18n.get('delete'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-delete"/>
</span>
</a>
</div>
</div>
</b-field>
<!-- Hook for extra Form options -->
<template v-if="hasBeginRightForm">
<form
id="form-collection-begin-right"
class="form-hook-region"
v-html="getBeginRightForm"/>
</template>
<!-- Description -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_description')"
:type="editFormErrors['description'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['description'] != undefined ? editFormErrors['description'] : ''">
<help-button
:title="$i18n.getHelperTitle('collections', 'description')"
:message="$i18n.getHelperMessage('collections', 'description')"/>
<b-input
id="tainacan-text-description"
type="textarea"
v-model="form.description"
@focus="clearErrors('description')"/>
</b-field>
<!-- Parent Collection -------------------------------- -->
<!-- DISABLED IN 0.18 AS WE DISCUSS BETTER IMPLEMENTATION FOR COLLECTIONS HIERARCHY -->
<!-- <b-field
@ -478,130 +616,6 @@
</b-select>
</b-field> -->
<!-- Slug -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.get('label_slug')"
:type="editFormErrors['slug'] != undefined ? 'is-danger' : ''"
:message="isUpdatingSlug ? $i18n.get('info_validating_slug') : (editFormErrors['slug'] != undefined ? editFormErrors['slug'] : '')">
<help-button
:title="$i18n.getHelperTitle('collections', 'slug')"
:message="$i18n.getHelperMessage('collections', 'slug')"/>
<b-input
id="tainacan-text-slug"
@input="updateSlug"
v-model="form.slug"
@focus="clearErrors('slug')"
:disabled="isUpdatingSlug"
:loading="isUpdatingSlug"/>
</b-field>
<!-- Comment Status ------------------------ -->
<b-field
:addons="false"
:label="$i18n.getHelperTitle('collections', 'allow_comments')">
&nbsp;
<b-switch
id="tainacan-checkbox-comment-status"
size="is-small"
true-value="open"
false-value="closed"
v-model="form.allow_comments" />
<help-button
:title="$i18n.getHelperTitle('collections', 'allow_comments')"
:message="$i18n.getHelperMessage('collections', 'allow_comments')"/>
</b-field>
<!-- Allows Submissions ------------------------ -->
<b-field
:addons="false"
:label="$i18n.getHelperTitle('collections', 'allows_submission')"
:type="editFormErrors['allows_submission'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['allows_submission'] != undefined ? editFormErrors['allows_submission'] : ''">
&nbsp;
<b-switch
id="tainacan-checkbox-allow-submission"
size="is-small"
true-value="yes"
false-value="no"
v-model="form.allows_submission" />
<help-button
:title="$i18n.getHelperTitle('collections', 'allows_submission')"
:message="$i18n.getHelperMessage('collections', 'allows_submission')"/>
</b-field>
<transition name="filter-item">
<div
v-if="form.allows_submission === 'yes'"
class="item-submission-options">
<!-- Allows Submissions by anonynmous user ------------------------ -->
<b-field
:addons="false"
:label="$i18n.getHelperTitle('collections', 'submission_anonymous_user')"
:type="editFormErrors['submission_anonymous_user'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['submission_anonymous_user'] != undefined ? editFormErrors['submission_anonymous_user'] : ''">
&nbsp;
<b-switch
id="tainacan-checkbox-allow-submission"
size="is-small"
true-value="yes"
false-value="no"
v-model="form.submission_anonymous_user" />
<help-button
:title="$i18n.getHelperTitle('collections', 'submission_anonymous_user')"
:message="$i18n.getHelperMessage('collections', 'submission_anonymous_user')"/>
</b-field>
<!-- Item submission default Status -------------------------------- -->
<b-field
:addons="false"
:label="$i18n.getHelperTitle('collections', 'submission_default_status')"
:type="editFormErrors['submission_default_status'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['submission_default_status'] != undefined ? editFormErrors['submission_default_status'] : ''">
<help-button
:title="$i18n.getHelperTitle('collections', 'submission_default_status')"
:message="$i18n.getHelperMessage('collections', 'submission_default_status')"/>
<div class="status-radios">
<b-radio
v-model="form.submission_default_status"
v-for="(statusOption, index) of $statusHelper.getStatuses().filter((status) => status.slug != 'trash')"
:key="index"
:native-value="statusOption.slug">
<span class="icon has-text-gray">
<i
class="tainacan-icon tainacan-icon-18px"
:class="$statusHelper.getIcon(statusOption.slug)"/>
</span>
{{ statusOption.name }}
</b-radio>
</div>
</b-field>
<!-- Submission process uses reCAPTCHA ------------------------ -->
<b-field
:addons="false"
:label="$i18n.getHelperTitle('collections', 'submission_use_recaptcha')"
:type="editFormErrors['submission_use_recaptcha'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['submission_use_recaptcha'] != undefined ? editFormErrors['submission_use_recaptcha'] : ''">
&nbsp;
<b-switch
id="tainacan-checkbox-submission-use-recaptcha"
size="is-small"
true-value="yes"
false-value="no"
v-model="form.submission_use_recaptcha" />
<help-button
:title="$i18n.getHelperTitle('collections', 'submission_use_recaptcha')"
:message="$i18n.getHelperMessage('collections', 'submission_use_recaptcha')"/>
<p
v-if="form.submission_use_recaptcha == 'yes'"
v-html="$i18n.getWithVariables('info_recaptcha_link_%s', [ reCAPTCHASettingsPagePath ])" />
</b-field>
</div>
</transition>
<!-- Hook for extra Form options -->
<template v-if="hasEndRightForm">
@ -1305,7 +1319,7 @@ export default {
}
}
.header-field {
padding-top: 24px;
padding-top: 1px;
.image-placeholder {
position: absolute;
@ -1328,38 +1342,53 @@ export default {
top: -15px;
position: relative;
}
}
.thumbnail-field {
padding: 24px;
// margin-top: 16px;
// margin-bottom: 38px;
&+.thumbnail-field {
opacity: 1.0;
transition: opacity 0.2s ease;
}
&:hover+.thumbnail-field {
opacity: 0.3;
}
}
.thumbnail-field {
display: inline-block;
padding: 1rem;
margin-top: -120px;
margin-bottom: -30px;
position: relative;
z-index: 99;
.content {
padding: 10px;
font-size: 0.8em;
}
img {
height: 178px;
width: 178px;
img,
/deep/ .image-wrapper {
height: 146px;
width: 146px;
border: 6px solid var(--tainacan-background-color);
}
.image-placeholder {
position: absolute;
margin-left: 45px;
margin-right: 45px;
margin-left: 26px;
margin-right: 26px;
font-size: 0.8em;
font-weight: bold;
z-index: 99;
text-align: center;
color: var(--tainacan-info-color);
top: 70px;
top: 64px;
max-width: 90px;
}
.thumbnail-buttons-row {
position: relative;
left: 90px;
bottom: 1.0em;
left: 52px;
bottom: calc(1.0em + 12px);
}
}
.switch {
position: relative;
top: -1px;
@ -1387,11 +1416,18 @@ export default {
.sorting-options {
display: flex;
align-items: center;
width: 100%;
.label {
font-weight: normal;
margin-bottom: 0;
}
.control.is-expanded {
width: 100%;
}
}
.sorting-options+.help {
opacity: 0.7;
}
.status-radios {
display: flex;
@ -1401,6 +1437,30 @@ export default {
align-items: center;
}
}
.items-view-mode-options {
display: flex;
&>.field:first-child {
width: 66.66%;
margin-right: 12px;
.dropdown-trigger>.button {
min-height: 35px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
&>.field:last-child {
width: 33.33%;
}
@media screen and (min-width: 1024px) {
.dropdown-trigger>.button {
min-height: 40px;
}
}
}
.item-submission-options {
padding-left: 1em;
padding-top: 1.25em;
@ -1408,6 +1468,9 @@ export default {
border-left: 1px solid var(--tainacan-gray2);
}
.enabled-view-modes-dropdown {
position: relative;
z-index: 101;
/deep/ .dropdown-item {
display: flex !important;
}

View File

@ -103,7 +103,7 @@
:class="{'is-loading': runButtonLoading}"
@click.prevent="runExporter()"
:disabled="!formIsValid()"
class="button is-pulled-right is-turquoise5">
class="button is-pulled-right is-success">
{{ $i18n.get('run') }}
</button>
</div>

View File

@ -269,12 +269,10 @@ export default {
'updateFilter'
]),
saveEdition(filter) {
if ((filter.filter_type_object && filter.filter_type_object.form_component) || filter.edit_form == '') {
this.isLoading = true;
for (let [key, value] of this.form) {
for (let [key, value] of Object.entries(this.form)) {
if (key === 'begin_with_filter_collapsed')
this.form[key] = (value == 'yes' || value == true) ? 'yes' : 'no';
}

View File

@ -803,7 +803,7 @@ export default {
<style lang="scss" scoped>
.tainacan-page-title {
margin-bottom: 40px;
margin-bottom: 38px;
h1, h2 {
font-size: 1.25em;

View File

@ -0,0 +1,70 @@
<template>
<div>
<!-- Attachments -------------------------------- -->
<div
v-if="!$adminOptions.hideItemEditionAttachments"
class="section-label">
<label>
<span class="icon has-text-gray4">
<i class="tainacan-icon tainacan-icon-attachments"/>
</span>
{{ $i18n.get('label_attachments') }}&nbsp;
<span
v-if="totalAttachments"
class="has-text-gray has-text-weight-normal"
style="font-size: 0.875em;">
({{ totalAttachments }})
</span>
</label>
<help-button
:title="$i18n.get('label_attachments')"
:message="$i18n.get('info_edit_attachments')"/>
<button
style="float: right; font-size: 0.875em; margin: 2px 5px;"
type="button"
class="link-style"
@click.prevent="($event) => $emit('openAttachmentsMediaFrame', $event)"
:disabled="isLoading">
<span class="icon">
<i class="tainacan-icon tainacan-icon-edit"/>
</span>
{{ $i18n.get('label_add_or_update_attachments') }}
</button>
</div>
<div
v-if="item != undefined && item.id != undefined && !$adminOptions.hideItemEditionAttachments"
class="section-box section-attachments">
<attachments-list
:item="item"
:form="form"
:is-editable="true"
:should-load-attachments="shouldLoadAttachments"
@onDeleteAttachment="($event) => $emit('onDeleteAttachment', $event)"/>
</div>
</div>
</template>
<script>
import AttachmentsList from '../lists/attachments-list.vue';
export default {
components: {
AttachmentsList
},
props: {
item: Object,
form: Object,
totalAttachments: Number,
isLoading: Boolean,
shouldLoadAttachments: Boolean
}
}
</script>
<style lang="scss" scoped>
.section-attachments {
padding-left: 0 !important;
padding-right: 0 !important;
}
</style>

View File

@ -377,7 +377,7 @@ export default {
}
.tainacan-page-title {
margin-bottom: 32px;
margin-bottom: 28px;
display: flex;
flex-wrap: wrap;
align-items: flex-end;
@ -506,13 +506,13 @@ export default {
}
.footer {
padding: 18px var(--tainacan-one-column);
padding: 14px var(--tainacan-one-column);
position: absolute;
bottom: 0;
z-index: 999999;
background-color: var(--tainacan-gray1);
width: 100%;
height: 65px;
height: 60px;
display: flex;
justify-content: flex-end;
align-items: center;

View File

@ -0,0 +1,198 @@
<template>
<div>
<div
v-if="!$adminOptions.hideItemEditionDocument"
class="section-label">
<label>
<span class="icon has-text-gray4">
<i :class="'tainacan-icon tainacan-icon-' + ( (!form.document_type || form.document_type == 'empty' ) ? 'item' : (form.document_type == 'attachment' ? 'attachments' : form.document_type))"/>
</span>
{{ form.document != undefined && form.document != null && form.document != '' ? $i18n.get('label_document') : $i18n.get('label_document_empty') }}
</label>
<help-button
:title="$i18n.getHelperTitle('items', 'document')"
:message="$i18n.getHelperMessage('items', 'document')"/>
</div>
<div
v-if="!$adminOptions.hideItemEditionDocument"
class="section-box document-field">
<div
v-if="form.document != undefined && form.document != null &&
form.document_type != undefined && form.document_type != null &&
form.document != '' && form.document_type != 'empty'"
class="document-field-content"
:class="'document-field-content--' + form.document_type">
<div v-html="item.document_as_html" />
<div class="document-buttons-row">
<a
class="button is-rounded is-secondary"
size="is-small"
id="button-edit-document"
:aria-label="$i18n.get('label_button_edit_document')"
@click.prevent="($event) => $emit('onSetDocument', $event, form.document_type)">
<span
v-tooltip="{
content: $i18n.get('edit'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-edit"/>
</span>
</a>
<a
class="button is-rounded is-secondary"
size="is-small"
id="button-delete-document"
:aria-label="$i18n.get('label_button_delete_document')"
@click.prevent="$emit('onRemoveDocument')">
<span
v-tooltip="{
content: $i18n.get('delete'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-delete"/>
</span>
</a>
</div>
</div>
<ul
v-else
class="document-field-placeholder">
<li v-if="!$adminOptions.hideItemEditionDocumentFileInput">
<button
type="button"
@click.prevent="($event) => $emit('onSetFileDocument', $event)">
<span class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-upload"/>
</span>
</button>
<p>{{ $i18n.get('label_file') }}</p>
</li>
<li v-if="!$adminOptions.hideItemEditionDocumentTextInput">
<button
type="button"
@click.prevent="$emit('onSetTextDocument')">
<span class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-text"/>
</span>
</button>
<p>{{ $i18n.get('label_text') }}</p>
</li>
<li v-if="!$adminOptions.hideItemEditionDocumentUrlInput">
<button
type="button"
@click.prevent="$emit('onSetURLDocument')">
<span class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-url"/>
</span>
</button>
<p>{{ $i18n.get('label_url') }}</p>
</li>
</ul>
</div>
</div>
</template>
<script>
export default {
props: {
item: Object,
form: Object
}
}
</script>
<style lang="scss" scoped>
.document-field {
.document-field-content {
max-height: 32vh;
&.document-field-content--text {
padding-bottom: 2rem;
}
/deep/ img,
/deep/ video,
/deep/ figure {
max-width: 100%;
max-height: 32vh;
width: auto !important;
margin: 0;
}
/deep/ a {
min-height: 60px;
display: block;
}
/deep/ audio,
/deep/ iframe,
/deep/ blockquote {
max-width: 100%;
max-height: 32vh;
width: 100%;
margin: 0;
min-height: 150px;
}
/deep/ audio {
min-height: 80px;
}
@media screen and (max-height: 760px) {
max-height: 25vh;
/deep/ img,
/deep/ video,
/deep/ figure {
max-height: 25vh;
}
/deep/ audio,
/deep/ iframe,
/deep/ blockquote {
max-height: 25vh;
}
}
}
.document-field-placeholder {
display: flex;
justify-content: space-evenly;
padding: 1.5rem 1rem 2rem 1rem;
border: 1px solid var(--tainacan-input-border-color);
li {
text-align: center;
button {
border-radius: 1px;
height: 72px;
width: 72px;
border: none;
background-color: var(--tainacan-background-color);
color: var(--tainacan-secondary);
margin-bottom: 6px;
transition: background-color 0.3s ease;
&:hover {
background-color: var(--tainacan-primary);
cursor: pointer;
}
}
p {
color: var(--tainacan-secondary);
font-size: 0.8125em;
}
}
}
}
.document-buttons-row {
bottom: -12px;
left: 0.875em;
position: absolute;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,281 @@
<template>
<div class="form-submission-footer">
<!-- Item edition inside iframe -->
<template v-if="isEditingItemMetadataInsideIframe">
<button
@click="$emit('onSubmit')"
type="button"
class="button is-secondary">
{{ $i18n.get('label_back_to_related_item') }}
</button>
</template>
<!-- Normal item edition -->
<template v-else>
<!-- Sequence edition Previous -->
<button
v-if="isOnSequenceEdit && hasPreviousItemOnSequenceEdit"
@click="$emit('onPrevInSequence')"
type="button"
class="button sequence-button">
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-previous"/>
</span>
<span>{{ $i18n.get('previous') }}</span>
</button>
<!-- Item is an autodraft (item creation) -->
<template v-if="(status == 'auto-draft' || status == undefined)">
<button
v-if="!$adminOptions.mobileAppMode"
@click="$emit('onDiscard')"
type="button"
class="button is-outlined">{{ $i18n.get('label_discard') }}</button>
<button
@click="openItemCreationStatusDialog"
type="button"
class="button is-secondary"
:style="{ marginLeft: $adminOptions.mobileAppMode ? 'auto' : '0.5em' }">{{ $i18n.get('label_create_item') }}</button>
</template>
<!-- Item is public, draft or private -->
<template v-else>
<!-- Send items to Trash -->
<button
v-if="!isOnSequenceEdit && currentUserCanDelete && !$adminOptions.mobileAppMode"
@click="$emit('onSubmit', 'trash')"
type="button"
class="button is-outlined">
<span v-if="!isMobileScreen">{{ $i18n.get('label_send_to_trash') }}</span>
<span v-else>{{ $i18n.get('status_trash') }}</span>
</button>
<!-- Update dropdown with -->
<b-dropdown
v-if="!$adminOptions.hideItemEditionStatusOption"
ref="item-edition-footer-dropdown"
:triggers="['contextmenu']"
aria-role="list"
animation="item-appear"
:mobile-modal="false"
position="is-top-left"
class="item-edition-footer-dropdown"
:style="{ marginLeft: $adminOptions.mobileAppMode ? 'auto' : '0.5em' }">
<template #trigger>
<button
:disabled="hasSomeError && (status == 'publish' || status == 'private')"
@click="!$adminOptions.mobileAppMode && !isMobileScreen ? $emit(
'onSubmit',
( currentUserCanPublish && !$adminOptions.hideItemEditionStatusPublishOption ) ? status : 'draft',
( (isOnSequenceEdit && !isCurrentItemOnSequenceEdit) ? 'next' : null)
) : ($refs && $refs['item-edition-footer-dropdown'] && !$refs['item-edition-footer-dropdown'].isActive ? $refs['item-edition-footer-dropdown'].toggle() : null)"
type="button"
class="button"
:class="{
'is-success': status == 'publish' || status == 'private',
'is-secondary': status == 'draft'
}">
{{ $i18n.get('label_update') }}
<span
v-if="isOnSequenceEdit && !isCurrentItemOnSequenceEdit"
class="icon is-large"
style="margin-left: 0em;">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-next"/>
</span>
<span
v-if="!$adminOptions.mobileAppMode"
@mouseenter="$refs && $refs['item-edition-footer-dropdown'] && !$refs['item-edition-footer-dropdown'].isActive ? $refs['item-edition-footer-dropdown'].toggle() : null"
style="margin-left: 0.5em;"
class="icon is-small">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-arrowup" />
</span>
</button>
</template>
<b-dropdown-item
@click="$emit(
'onSubmit',
'draft',
( (isOnSequenceEdit && !isCurrentItemOnSequenceEdit) ? 'next' : null)
)"
:class="{ 'is-forced-last-option': status == 'draft' }"
aria-role="listitem">
<span class="icon has-text-gray4">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-draft"/>
</span>
{{ status == 'draft' ? $i18n.get('label_update_draft') : $i18n.get('label_change_to_draft') }}
</b-dropdown-item>
<b-dropdown-item
v-if="currentUserCanPublish && !$adminOptions.hideItemEditionStatusPublishOption"
@click="$emit(
'onSubmit',
'private',
( (isOnSequenceEdit && !isCurrentItemOnSequenceEdit) ? 'next' : null)
)"
:class="{ 'is-forced-last-option': status == 'private' }"
aria-role="listitem">
<span class="icon has-text-gray4">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-private"/>
</span>
{{ status == 'private' ? $i18n.get('label_update_as_private') : ( status == 'draft' ? $i18n.get('label_verb_publish_privately') : $i18n.get('label_change_to_private') ) }}
</b-dropdown-item>
<b-dropdown-item
v-if="currentUserCanPublish && !$adminOptions.hideItemEditionStatusPublishOption"
@click="$emit(
'onSubmit',
'publish',
( (isOnSequenceEdit && !isCurrentItemOnSequenceEdit) ? 'next' : null)
)"
aria-role="listitem">
<span class="icon has-text-gray4">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-public"/>
</span>
{{ status == 'publish' ? $i18n.get('label_update_as_public') : $i18n.get('label_verb_publish') }}
</b-dropdown-item>
</b-dropdown>
<!-- In case we do not want to show status, just an update button -->
<button
v-else
:disabled="hasSomeError && (status == 'publish' || status == 'private')"
@click="$emit('onSubmit', status)"
type="button"
class="button"
:class="{
'is-success': status == 'publish' || status == 'private',
'is-secondary': status == 'draft'
}">
{{ $i18n.get('label_update') }}
</button>
</template>
<!-- Sequence edition Next button if user cannot publish (only goes to next, without changing status) -->
<button
v-if="!currentUserCanPublish && isOnSequenceEdit && hasNextItemOnSequenceEdit"
:disabled="(status == 'publish' || status == 'private') && hasSomeError"
@click="$emit('onNextInSequence')"
type="button"
class="button is-success">
<span>{{ $i18n.get('label_next') }}</span>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-next"/>
</span>
</button>
<!-- Sequence edition Finish -->
<button
v-if="isOnSequenceEdit && isCurrentItemOnSequenceEdit"
@click="$router.push($routerHelper.getCollectionPath(collectionId))"
type="button"
class="button sequence-button">
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-approved"/>
</span>
<span>{{ $i18n.get('finish') }}</span>
</button>
</template>
</div>
</template>
<script>
import ItemCreationStatusDialog from '../other/item-creation-status-dialog.vue';
export default {
props: {
status: String,
collectionId: Number,
isOnSequenceEdit: Boolean,
isCurrentItemOnSequenceEdit: Boolean,
hasNextItemOnSequenceEdit: Boolean,
hasPreviousItemOnSequenceEdit: Boolean,
isMobileScreen: Boolean,
hasSomeError: Boolean,
currentUserCanDelete: Boolean,
currentUserCanPublish: Boolean,
isEditingItemMetadataInsideIframe: Boolean
},
mounted() {
this.$parent.$on('toggleItemEditionFooterDropdown', () => {
if (this.$refs && this.$refs['item-edition-footer-dropdown'])
this.$refs['item-edition-footer-dropdown'].toggle();
});
},
beforeDestroy() {
this.$parent.$off('toggleItemEditionFooterDropdown');
},
methods: {
openItemCreationStatusDialog() {
this.$buefy.modal.open({
parent: this,
component: ItemCreationStatusDialog,
canCancel: false,
props: {
icon: 'item',
currentUserCanPublish: this.currentUserCanPublish,
onConfirm: (selectedStatus) => {
this.$emit('onSubmit', selectedStatus);
}
},
trapFocus: true,
customClass: 'tainacan-modal',
closeButtonAriaLabel: this.$i18n.get('close')
});
},
}
}
</script>
<style lang="scss" scoped>
.form-submission-footer {
display: flex;
flex-wrap: nowrap;
.button {
margin-left: 16px;
margin-right: 6px;
}
.button:last-of-type {
margin-right: 0px;
}
/deep/ .item-edition-footer-dropdown {
.dropdown-trigger .button>.icon.is-small {
border-left: 1px solid rgba(255,255,255,0.6);
margin-left: 0.5em;
}
.dropdown-menu>.dropdown-content {
display: flex;
flex-direction: column;
.dropdown-item.is-forced-last-option {
order: 99;
}
}
}
}
@media screen and (max-width: 782px) {
.form-submission-footer {
display: flex;
justify-content: space-between;
width: 100%;
.button {
margin-left: 6px;
margin-right: 6px;
}
.button:first-of-type {
margin-left: 0px;
}
.button.is-success {
margin-left: auto;
}
}
}
</style>

View File

@ -0,0 +1,165 @@
<template>
<div>
<div
v-if="!$adminOptions.hideItemEditionThumbnail"
class="section-label">
<label>
<span class="icon has-text-gray4">
<i class="tainacan-icon tainacan-icon-image"/>
</span>
{{ $i18n.get('label_thumbnail') }}
</label>
<help-button
:title="$i18n.getHelperTitle('items', '_thumbnail_id')"
:message="$i18n.getHelperMessage('items', '_thumbnail_id')"/>
</div>
<div
v-if="!isLoading && !$adminOptions.hideItemEditionThumbnail"
class="section-box section-thumbnail">
<div class="thumbnail-field">
<file-item
v-if="item.thumbnail != undefined && ((item.thumbnail['tainacan-medium'] != undefined && item.thumbnail['tainacan-medium'] != false) || (item.thumbnail.medium != undefined && item.thumbnail.medium != false))"
:show-name="false"
:modal-on-click="false"
:size="120"
:file="{
media_type: 'image',
thumbnails: { 'tainacan-medium': [ $thumbHelper.getSrc(item['thumbnail'], 'tainacan-medium', item.document_mimetype) ] },
title: $i18n.get('label_thumbnail'),
description: `<img alt='` + $i18n.get('label_thumbnail') + `' src='` + $thumbHelper.getSrc(item['thumbnail'], 'full', item.document_mimetype) + `'/>`
}"/>
<figure
v-if="item.thumbnail == undefined || ((item.thumbnail.medium == undefined || item.thumbnail.medium == false) && (item.thumbnail['tainacan-medium'] == undefined || item.thumbnail['tainacan-medium'] == false))"
class="image">
<span
class="image-placeholder"
v-if="item.document_type == 'empty' && item.document_mimetype == 'empty'">
{{ $i18n.get('label_empty_thumbnail') }}
</span>
<img
:alt="$i18n.get('label_thumbnail')"
:src="$thumbHelper.getEmptyThumbnailPlaceholder(item.document_mimetype)">
</figure>
<b-field
v-if="item.thumbnail_id"
:addons="false"
:label="$i18n.get('label_thumbnail_alt')">
<help-button
:title="$i18n.get('label_thumbnail_alt')"
:message="$i18n.get('info_thumbnail_alt')"/>
<textarea
id="tainacan-text-description"
class="textarea"
rows="4"
:value="form.thumbnail_alt && form.thumbnail_alt != 'false' ? form.thumbnail_alt : ''"
@input="updateThumbnailAlt" />
</b-field>
<div class="thumbnail-buttons-row">
<a
class="button is-rounded is-secondary"
id="button-edit-thumbnail"
:aria-label="$i18n.get('label_button_edit_thumb')"
@click.prevent="($event) => $emit('openThumbnailMediaFrame', $event)">
<span
v-tooltip="{
content: $i18n.get('edit'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-edit"/>
</span>
</a>
<a
v-if="item.thumbnail && item.thumbnail.thumbnail != undefined && item.thumbnail.thumbnail != false"
id="button-delete-thumbnail"
class="button is-rounded is-secondary"
:aria-label="$i18n.get('label_button_delete_thumb')"
@click="$emit('onDeleteThumbnail')">
<span
v-tooltip="{
content: $i18n.get('delete'),
autoHide: true,
placement: 'bottom',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-delete"/>
</span>
</a>
</div>
</div>
</div>
</div>
</template>
<script>
import FileItem from '../other/file-item.vue';
export default {
components: {
FileItem
},
props: {
item: Object,
form: Object
},
methods: {
updateThumbnailAlt: _.debounce(function($event) {
this.$emit('onUpdateThumbnailAlt', $event.target.value);
}, 750)
}
}
</script>
<style lang="scss" scoped>
.section-thumbnaill {
padding-right: 0;
}
.thumbnail-buttons-row {
bottom: -6px;
left: 0.875em;
position: absolute;
}
.thumbnail-field {
display: flex;
.field {
margin-left: 1em;
width: 100%;
}
.content {
padding: 10px;
font-size: 0.8em;
}
img {
height: 120px;
width: 120px;
min-width: 120px;
}
.image-placeholder {
position: absolute;
margin-left: 20px;
margin-right: 20px;
font-size: 0.8em;
font-weight: bold;
z-index: 99;
text-align: center;
color: var(--tainacan-info-color);
top: 34px;
max-width: 84px;
}
.thumbnail-alt-input {
.label {
font-size: 0.875em;
font-weight: 500;
margin-left: 15px;
margin-bottom: 0;
margin-top: 0.15em;
}
}
}
</style>

View File

@ -0,0 +1,385 @@
<template>
<form
id="metadataSectionEditForm"
@submit.prevent="saveEdition(form)"
autofocus="true"
tabindex="-1"
role="dialog"
aria-modal>
<div
v-if="form && Object.keys(form).length"
class="tainacan-modal-content">
<div class="tainacan-modal-title">
<h2 v-html="form.name ? ($i18n.get('instruction_configure_the_metadata_section') + ' <em>' + form.name + '</em>') : $i18n.get('instruction_configure_new_metadata_section')" />
<hr>
</div>
<div class="tainacan-form">
<div class="options-columns">
<b-field
:addons="false"
:type="formErrors['name'] != undefined ? 'is-danger' : ''"
:message="formErrors['name'] != undefined ? formErrors['name'] : ''">
<label class="label is-inline">
{{ $i18n.get('label_name') }}
<span
class="required-metadata-section-asterisk"
:class="formErrors['name'] != undefined ? 'is-danger' : ''">*</span>
<help-button
:title="$i18n.getHelperTitle('metadata-sections', 'name')"
:message="$i18n.getHelperMessage('metadata-sections', 'name')"
:extra-classes="isRepositoryLevel ? 'tainacan-repository-tooltip' : ''" />
</label>
<b-input
v-model="form.name"
name="name"
@focus="clearErrors('name')"/>
</b-field>
<!-- Hook for extra Form options -->
<template
v-if="hasBeginLeftForm">
<form
id="form-metadataSection-begin-left"
class="form-hook-region"
v-html="getBeginLeftForm"/>
</template>
<b-field
:addons="false"
:type="formErrors['description'] != undefined ? 'is-danger' : ''"
:message="formErrors['description'] != undefined ? formErrors['description'] : ''">
<label class="label is-inline">
{{ $i18n.get('label_description') }}
<help-button
:title="$i18n.getHelperTitle('metadata-sections', 'description')"
:message="$i18n.getHelperMessage('metadata-sections', 'description')"
:extra-classes="isRepositoryLevel ? 'tainacan-repository-tooltip' : ''" />
</label>
<b-input
type="textarea"
name="description"
rows="3"
v-model="form.description"
@focus="clearErrors('description')"/>
</b-field>
<b-field
:addons="false"
:label="$i18n.getHelperTitle('metadata-sections', 'description_bellow_name')"
:type="formErrors['description_bellow_name'] != undefined ? 'is-danger' : ''"
:message="formErrors['description_bellow_name'] != undefined ? formErrors['description_bellow_name'] : ''">
&nbsp;
<b-switch
size="is-small"
@input="clearErrors('description_bellow_name')"
v-model="form.description_bellow_name"
true-value="yes"
false-value="no"
name="description_bellow_name">
<help-button
:title="$i18n.getHelperTitle('metadata-sections', 'description_bellow_name')"
:message="$i18n.getHelperMessage('metadata-sections', 'description_bellow_name')"
:extra-classes="isRepositoryLevel ? 'tainacan-repository-tooltip' : ''" />
</b-switch>
</b-field>
<b-field
v-if="form.id !== 'default_section'"
:addons="false"
:type="formErrors['status'] != undefined ? 'is-danger' : ''"
:message="formErrors['status'] != undefined ? formErrors['status'] : ''">
<label class="label is-inline">
{{ $i18n.get('label_status') }}
<help-button
:title="$i18n.getHelperTitle('metadata-sections', 'status')"
:message="$i18n.getHelperMessage('metadata-sections', 'status')"
:extra-classes="isRepositoryLevel ? 'tainacan-repository-tooltip' : ''" />
</label>
<div class="is-flex is-justify-content-space-between">
<b-radio
@focus="clearErrors('label_status')"
id="tainacan-select-status-publish"
name="status"
v-model="form.status"
native-value="publish">
<span class="icon has-text-gray3">
<i class="tainacan-icon tainacan-icon-public"/>
</span>
{{ $i18n.get('status_public') }}
</b-radio>
<b-radio
@focus="clearErrors('label_status')"
id="tainacan-select-status-private"
name="status"
v-model="form.status"
native-value="private">
<span class="icon has-text-gray3">
<i class="tainacan-icon tainacan-icon-private"/>
</span>
{{ $i18n.get('status_private') }}
</b-radio>
</div>
</b-field>
</div>
<!-- Hook for extra Form options -->
<template v-if="hasEndLeftForm" >
<form
id="form-metadataSection-end-left"
class="form-hook-region"
v-html="getEndLeftForm"/>
</template>
</div>
</div>
<div class="field is-grouped form-submit">
<div class="control">
<button
type="button"
class="button is-outlined"
@click.prevent="cancelEdition()"
slot="trigger">{{ $i18n.get('cancel') }}
</button>
</div>
<p class="help is-danger">{{ formErrorMessage }}</p>
<div class="control">
<b-button
:loading="isUpdating"
class="button is-success"
native-type="submit">
{{ $i18n.get('save') }}
</b-button>
</div>
</div>
</form>
</template>
<script>
import { mapActions } from 'vuex';
import { formHooks } from "../../js/mixins";
export default {
name: 'MetadataSectionEditionForm',
mixins: [ formHooks ],
props: {
index: '',
originalMetadataSection: Object,
collectionId: '',
isInsideImporterFlow: false
},
data() {
return {
form: {},
formErrors: {},
formErrorMessage: '',
closedByForm: false,
entityName: 'metadataSection',
isUpdating: false
}
},
created() {
this.form = JSON.parse(JSON.stringify(this.originalMetadataSection));
if (this.form.status == 'auto-draft')
this.form.status = 'publish';
this.formErrors = this.form.formErrors != undefined ? this.form.formErrors : {};
this.formErrorMessage = this.form.formErrors != undefined ? this.form.formErrorMessage : '';
},
mounted() {
// Fills hook forms with it's real values
this.$nextTick()
.then(() => {
this.updateExtraFormData(this.form);
});
},
methods: {
...mapActions('metadata', [
'updateMetadataSection'
]),
saveEdition(metadataSection) {
if ( (metadataSection.metadata_type_object && metadataSection.metadata_type_object.form_component) || metadataSection.edit_form == '') {
this.fillExtraFormData(this.form);
this.isUpdating = true;
this.updateMetadataSection({
collectionId: this.collectionId,
metadataSectionId: metadataSection.id,
index: this.index,
options: this.form
})
.then(() => {
this.form = {};
this.formErrors = {};
this.formErrorMessage = '';
this.isUpdating = false;
this.closedByForm = true;
this.$emit('onEditionFinished');
})
.catch((errors) => {
this.isUpdating = false;
for (let error of errors.errors) {
for (let attribute of Object.keys(error))
this.formErrors[attribute] = error[attribute];
}
this.formErrorMessage = errors.error_message;
this.form.formErrors = this.formErrors;
this.form.formErrorMessage = this.formErrorMessage;
});
} else {
let formElement = document.getElementById('metadataSectionEditForm');
let formData = new FormData(formElement);
let formObj = {};
for (let [key, value] of formData.entries()) {
if (key === 'description_bellow_name')
formObj[key] = value ? 'yes' : 'no';
else
formObj[key] = value;
}
this.fillExtraFormData(formObj);
this.isUpdating = true;
this.updateMetadataSection({
collectionId: this.collectionId,
metadataSectionId: metadataSection.id,
index: this.index,
options: formObj
})
.then(() => {
this.form = {};
this.formErrors = {};
this.formErrorMessage = '';
this.isUpdating = false;
this.closedByForm = true;
this.$emit('onEditionFinished');
})
.catch((errors) => {
this.isUpdating = false;
for (let error of errors.errors) {
for (let attribute of Object.keys(error))
this.formErrors[attribute] = error[attribute];
}
this.formErrorMessage = errors.error_message;
this.$emit('onErrorFound');
this.form.formErrors = this.formErrors;
this.form.formErrorMessage = this.formErrorMessage;
});
}
},
clearErrors(attribute) {
this.formErrors[attribute] = undefined;
},
cancelEdition() {
this.closedByForm = true;
this.$emit('onEditionCanceled');
},
}
}
</script>
<style lang="scss" scoped>
form#metadataSectionEditForm {
.options-columns {
-moz-column-count: 2;
-moz-column-gap: 0;
-moz-column-rule: 1px solid var(--tainacan-gray1);
-webkit-column-count: 2;
-webkit-column-gap: 0;
-webkit-column-rule: 1px solid var(--tainacan-gray1);
column-count: 2;
column-gap: 4em;
column-rule: 1px solid var(--tainacan-gray1);
padding-left: 0.25em;
padding-right: 0.25em;
padding-bottom: 0.5em;
&>.field, &>section {
-webkit-column-break-inside: avoid;
page-break-inside: avoid;
break-inside: avoid;
}
.field > .field:not(:last-child) {
margin-bottom: 0em;
}
/deep/ .field {
-webkit-column-break-inside: avoid;
page-break-inside: avoid;
break-inside: avoid;
}
section {
display: grid;
}
.field:first-child {
-webkit-column-span: all;
column-span: all;
}
.tainacan-help-tooltip-trigger {
font-size: 1.25em;
}
}
.tainacan-form .field:not(:last-child) {
margin-bottom: 1em;
}
.tainacan-form /deep/ .control-label {
white-space: normal;
}
.metadata-form-section {
margin: 1.5em 0 0.5em -1.5em;
position: relative;
cursor: pointer;
.icon {
background: var(--tainacan-background-color);
z-index: 1;
position: relative;
}
strong {
background: var(--tainacan-background-color);
color: var(--tainacan-gray4);
font-size: 0.875em;
z-index: 1;
position: relative;
padding-right: 12px;
}
hr {
position: absolute;
top: -0.75em;
width: calc(100% - 42px);
height: 1px;
background-color: var(--tainacan-gray2);
margin-left: 42px;
}
}
@media screen and (max-width: 768px) {
.options-columns {
-moz-column-count: 1;
-webkit-column-count: 1;
column-count: 1;
}
}
}
.form-submit {
background-color: var(--tainacan-gray1);
position: sticky;
bottom: 0;
padding: 16px 4.166666667vw;
display: flex;
justify-content: space-between;
z-index: 2;
font-size: 1.125em;
}
</style>

View File

@ -370,7 +370,7 @@
<p class="help is-danger">{{ formErrorMessage }}</p>
<div class="control">
<b-button
:loading="isLoading"
:loading="isUpdating"
class="button is-success"
native-type="submit">
{{ $i18n.get('save') }}
@ -437,7 +437,6 @@
'updateMetadatum'
]),
saveEdition(metadatum) {
if ( (metadatum.metadata_type_object && metadatum.metadata_type_object.form_component) || metadatum.edit_form == '') {
let repository = this.form.repository_level;
@ -449,7 +448,8 @@
isRepositoryLevel: this.isRepositoryLevel || (repository && repository === 'yes'),
index: this.index,
options: this.form,
includeOptionsAsHtml: true
includeOptionsAsHtml: true,
sectionId: metadatum.metadata_section_id
})
.then(() => {
this.form = {};
@ -493,7 +493,8 @@
isRepositoryLevel: this.isRepositoryLevel || (repository && repository === 'yes'),
index: this.index,
options: formObj,
includeOptionsAsHtml: true
includeOptionsAsHtml: true,
sectionId: metadatum.metadata_section_id
})
.then(() => {
this.form = {};

View File

@ -523,7 +523,7 @@
}
.tainacan-page-title {
margin-bottom: 30px;
margin-bottom: 28px;
display: flex;
flex-wrap: wrap;
align-items: baseline;

View File

@ -1,6 +1,6 @@
<template>
<div
:style="{ 'height': isLoadingOptions && !filtersAsModal ? (Number(filter.max_options)*28) + 'px' : 'auto' }"
:style="{ 'height': isLoadingOptions && !filtersAsModal ? (Number(filter.max_options)*1.375) + 'rem' : 'auto' }"
:class="{ 'skeleton': isLoadingOptions && !filtersAsModal }"
class="block">
<template v-if="!filtersAsModal">
@ -43,7 +43,13 @@
:is-modal="false"
:filter="filter"
:selected="selected"
@input="(newSelected) => selected = newSelected"
@input="(newSelected) => {
const existingValue = selected.indexOf(newSelected);
if (existingValue >= 0)
selected.splice(existingValue, 1);
else
selected.push(newSelected);
}"
:metadatum-id="metadatumId"
:collection-id="collectionId"
:metadatum_type="metadatumType"
@ -73,10 +79,9 @@
},
watch: {
selected(newVal, oldVal) {
const isEqual = (newVal.length == oldVal.length) && newVal.every((element, index) => {
const isEqual = (Array.isArray(newVal) && Array.isArray(oldVal) && (newVal.length == oldVal.length)) && newVal.every((element, index) => {
return element === oldVal[index];
});
if (!isEqual)
this.onSelect();
},
@ -89,7 +94,7 @@
},
},
mounted() {
if (!this.isUsingElasticSearch && !this.filtersAsModal)
if (!this.isUsingElasticSearch)
this.loadOptions();
},
created() {

View File

@ -13,7 +13,7 @@
<b-select
name="step_options"
v-model="step"
@input="onUpdate">
@input="onUpdateStep">
<option value="0.001">0.001</option>
<option value="0.01">0.01</option>
<option value="0.1">0.1</option>
@ -50,7 +50,7 @@
<b-input
name="max_options"
v-model="step"
@input="onUpdate"
@input="onUpdateStep"
type="number"
step="1" />
<button
@ -77,11 +77,7 @@
export default {
props: {
filter: {
type: Object
},
value: [String, Number, Array],
disabled: false,
value: [String, Number, Array]
},
data() {
return {
@ -93,12 +89,9 @@
this.step = this.value && this.value.step ? this.value.step : 1;
},
methods: {
onUpdate() {
this.$emit('input', {
step: this.step
});
onUpdateStep(value) {
this.$emit('input', { step: value });
},
}
}
</script>

View File

@ -90,7 +90,7 @@
methods: {
onUpdateStep(value) {
this.$emit('input', { step: value });
},
}
}
}
</script>

View File

@ -51,7 +51,13 @@
:filter="filter"
:taxonomy_id="taxonomyId"
:selected="selected"
@input="(newSelected) => selected = newSelected"
@input="(newSelected) => {
const existingValue = selected.indexOf(newSelected);
if (existingValue >= 0)
selected.splice(existingValue, 1);
else
selected.push(newSelected);
}"
:metadatum-id="metadatumId"
:taxonomy="taxonomy"
:collection-id="collectionId"
@ -94,7 +100,7 @@
},
watch: {
selected(newVal, oldVal) {
const isEqual = (newVal.length == oldVal.length) && newVal.every((element, index) => {
const isEqual = (Array.isArray(newVal) && Array.isArray(oldVal) && (newVal.length == oldVal.length)) && newVal.every((element, index) => {
return element === oldVal[index];
});
if (!isEqual)
@ -127,7 +133,7 @@
this.$eventBusSearch.$on('has-to-reload-facets', this.reloadOptions);
},
mounted(){
if (!this.isUsingElasticSearch && !this.filtersAsModal)
if (!this.isUsingElasticSearch)
this.loadOptions();
},
beforeDestroy() {

View File

@ -3,109 +3,88 @@
<div class="table-wrapper">
<table class="tainacan-table is-narrow">
<thead>
<tr>
<!-- Title -->
<th>
<div class="th-wrap">{{ $i18n.get('label_activity_title') }}</div>
</th>
<!-- Created by -->
<th>
<div class="th-wrap">{{ $i18n.get('label_created_by') }}</div>
</th>
<!-- Activity date -->
<th>
<div class="th-wrap">{{ $i18n.get('label_activity_date') }}</div>
</th>
<!--&lt;!&ndash; Approbation &ndash;&gt;-->
<!--<th>-->
<!--<div class="th-wrap">{{ $i18n.get('label_approbation') }}</div>-->
<!--</th>-->
</tr>
<tr>
<!-- Title -->
<th>
<div class="th-wrap">{{ $i18n.get('label_activity_title') }}</div>
</th>
<!-- Created by -->
<th>
<div class="th-wrap">{{ $i18n.get('label_created_by') }}</div>
</th>
<!-- Activity date -->
<th>
<div class="th-wrap">{{ $i18n.get('label_activity_date') }}</div>
</th>
<!--&lt;!&ndash; Approbation &ndash;&gt;-->
<!--<th>-->
<!--<div class="th-wrap">{{ $i18n.get('label_approbation') }}</div>-->
<!--</th>-->
</tr>
</thead>
<tbody>
<tr
:key="index"
v-for="(activity, index) of activities">
<!-- Name -->
<td
class="column-default-width column-main-content"
@click="openActivityDetailsModal(activity)"
:label="$i18n.get('label_activity_title')"
:aria-label="$i18n.get('label_activity_title') + ': ' + activity.title">
<p
v-tooltip="{
delay: {
shown: 500,
hide: 300,
},
content: activity.title,
autoHide: false,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}">
{{ activity.title }}
</p>
</td>
<!-- User -->
<td
class="table-creation column-small-width"
@click="openActivityDetailsModal(activity)"
:label="$i18n.get('label_created_by')"
:aria-label="$i18n.get('label_created_by') + ': ' + activity.user_name">
<p
v-tooltip="{
delay: {
shown: 500,
hide: 300,
},
content: activity.user_name,
autoHide: false,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}"
v-html="activity.user_name"/>
</td>
<!-- Activity Date -->
<td
class="table-creation column-small-width"
@click="openActivityDetailsModal(activity)"
:label="$i18n.get('label_activity_date')"
:aria-label="$i18n.get('label_activity_date') + ': ' + activity.date">
<p
v-tooltip="{
delay: {
shown: 500,
hide: 300,
},
content: activity.date,
autoHide: false,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}"
v-html="activity.date"/>
</td>
<!-- Approbation -->
<!--<td-->
<!--class="status-cell"-->
<!--:label="$i18n.get('label_approbation')"-->
<!--:aria-label="$i18n.get('label_approbation') + ': ' + activity.status">-->
<!--<p>-->
<!--<three-state-toggle-button-->
<!--:other-prop="activity"-->
<!--:parent="getThis()"-->
<!--:events="stateEvents"-->
<!--:state=" activity.status === 'publish' ?-->
<!--'yes_3tgbtn' : (activity.status === 'pending' ? 'neutral_3tgbtn' : 'no_3tgbtn')"/>-->
<!--&lt;!&ndash;<a v-if="activity.status !== 'pending'">&ndash;&gt;-->
<!--&lt;!&ndash;<b-icon&ndash;&gt;-->
<!--&lt;!&ndash;type="is-blue5"&ndash;&gt;-->
<!--&lt;!&ndash;custom-class="mdi-flip-h"&ndash;&gt;-->
<!--&lt;!&ndash;icon="share"/>&ndash;&gt;-->
<!--&lt;!&ndash;</a>&ndash;&gt;-->
<!--</p>-->
<!--</td>-->
</tr>
<tr
:key="index"
v-for="(activity, index) of activities">
<!-- Name -->
<td
class="column-default-width column-main-content"
@click="openActivityDetailsModal(activity)"
:label="$i18n.get('label_activity_title')"
:aria-label="$i18n.get('label_activity_title') + ': ' + activity.title">
<p
v-tooltip="{
delay: {
shown: 500,
hide: 300,
},
content: activity.title,
autoHide: false,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}">
{{ activity.title }}
</p>
</td>
<!-- User -->
<td
class="table-creation column-small-width"
@click="openActivityDetailsModal(activity)"
:label="$i18n.get('label_created_by')"
:aria-label="$i18n.get('label_created_by') + ': ' + activity.user_name">
<p
v-tooltip="{
delay: {
shown: 500,
hide: 300,
},
content: activity.user_name,
autoHide: false,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}"
v-html="activity.user_name"/>
</td>
<!-- Activity Date -->
<td
class="table-creation column-small-width"
@click="openActivityDetailsModal(activity)"
:label="$i18n.get('label_activity_date')"
:aria-label="$i18n.get('label_activity_date') + ': ' + activity.date">
<p
v-tooltip="{
delay: {
shown: 500,
hide: 300,
},
content: activity.date,
autoHide: false,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}"
v-html="activity.date"/>
</td>
</tr>
</tbody>
</table>
</div>
@ -130,7 +109,6 @@
import { mapActions } from 'vuex';
import ActivityDetailsModal from '../modals/activity-details-modal.vue';
// import ThreeStateToggleButton from '../other/three-state-toggle-button.vue';
export default {
name: 'ActivitiesList',

View File

@ -4,8 +4,8 @@
style="position: relative;"
class="table-container">
<b-loading
is-full-page="false"
:active="isLoading" />
:is-full-page="false"
:active.sync="isLoading" />
<div
v-if="attachments.length > 0"
class="table-wrapper">
@ -13,13 +13,19 @@
<div
v-for="(attachment, index) in attachments"
:key="index"
class="file-item-container">
class="file-item-container"
:class="{ 'is-file-document': form.document == attachment.id, 'is-file-thumbnail': item.thumbnail_id == attachment.id }">
<span
v-if="form.document == attachment.id"
class="file-attachment-document-tag">
{{ $i18n.get('label_document') }}
</span>
<file-item
:show-name="true"
:modal-on-click="true"
:file="attachment"/>
<span
v-if="isEditable"
v-if="isEditable && form.document != attachment.id"
class="file-item-control">
<a
@click="onDeleteAttachment(attachment)"
@ -107,13 +113,15 @@
},
props: {
item: Object,
isLoading: Boolean,
form: Object,
shouldLoadAttachments: Boolean,
isEditable: Boolean,
},
data() {
return {
attachmentsPage: 1,
attachmentsPerPage: 24
attachmentsPerPage: 12,
isLoading: false
}
},
computed: {
@ -124,6 +132,11 @@
return this.getTotalAttachments();
}
},
watch: {
shouldLoadAttachments() {
this.loadAttachments();
}
},
created() {
// Get attachments
this.loadAttachments();
@ -159,21 +172,21 @@
return last > this.totalAttachments ? this.totalAttachments : last;
},
loadAttachments() {
this.$emit('isLoadingAttachments', true);
this.isLoading = true;
this.fetchAttachments({
page: this.attachmentsPage,
attachmentsPerPage: this.attachmentsPerPage,
itemId: this.item.id,
documentId: this.item.document,
thumbnailId: this.item.thumbnail_id
// excludeDocumentId: this.form.document,
// excludeThumbnailId: this.item.thumbnail_id
})
.then((response) => {
this.$emit('isLoadingAttachments', false);
this.isLoading = false;
this.totalAttachments = response.total;
})
.catch((error) => {
this.$emit('isLoadingAttachments', false);
this.isLoading = false;
this.$console.error(error);
})
},
@ -186,12 +199,16 @@
<style lang="scss" scoped>
.table-container {
width: 100%;
}
.uploaded-files {
display: block;
display: flex;
flex-wrap: wrap;
.file-item-container {
display: inline-block;
margin: 15px;
margin: 10px 12px;
position: relative;
&:hover .file-item-control {
@ -200,10 +217,30 @@
opacity: 1;
}
.file-attachment-document-tag {
background-color: var(--tainacan-primary);
color: var(--tainacan-secondary);
display: block;
position: absolute;
z-index: 9;
padding: 0.25em 0.5em;
font-size: 0.6875em;
border-radius: 3px;
bottom: 10px;
left: 4px;
font-weight: 500;
border: 1px solid var(--tainacan-secondary);
opacity: 0.25;
transition: opacity 0.2s ease;
}
&:hover .file-attachment-document-tag {
opacity: 1.0;
}
.file-item-control {
position: absolute;
background-color: var(--tainacan-gray1);
width: 112px;
width: 94px;
margin: 6px 0;
bottom: 0px;
padding: 2px 8px 4px 8px;
@ -219,4 +256,23 @@
}
}
}
@media screen and (max-width: 769px) {
.table-container {
padding-left: 1em;
padding-right: 1em;
}
.pagination-area {
margin-left: 0;
margin-right: 0;
justify-content: center;
}
.uploaded-files {
justify-content: center;
.file-item-container {
margin: 5px 7px;
}
}
}
</style>

View File

@ -0,0 +1,907 @@
<template>
<div class="column">
<b-loading :active.sync="isLoadingMetadataSections"/>
<div class="tainacan-form sub-header">
<template v-if="activeMetadataSectionsList">
<button
aria-controls="filters-items-list"
:aria-expanded="!collapseAll"
v-if="activeMetadataSectionsList.length > 0"
class="link-style collapse-all"
@click="collapseAll = !collapseAll">
<span class="icon">
<i
:class="{ 'tainacan-icon-arrowdown' : collapseAll, 'tainacan-icon-arrowright' : !collapseAll }"
class="has-text-secondary tainacan-icon tainacan-icon-1-125em"/>
</span>
<span class="collapse-all__text">
{{ collapseAll ? $i18n.get('label_show_less_details') : $i18n.get('label_show_more_details') }}
</span>
</button>
<b-field class="header-item">
<b-dropdown
:mobile-modal="true"
:disabled="activeMetadataSectionsList.length <= 0"
class="show metadata-options-dropdown"
aria-role="list"
trap-focus>
<button
:aria-label="$i18n.get('label_filter_by_metadata_type')"
class="button is-white"
slot="trigger">
<span>{{ $i18n.get('label_filter_by_metadata_type') }}</span>
<span class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-arrowdown"/>
</span>
</button>
<div class="metadata-options-container">
<b-dropdown-item
v-for="(metadataType, index) in metadataTypeFilterOptions"
:key="index"
class="control"
custom
aria-role="listitem">
<b-checkbox
v-model="metadataType.enabled"
:native-value="metadataType.enabled">
{{ metadataType.name }}
</b-checkbox>
</b-dropdown-item>
</div>
</b-dropdown>
</b-field>
<b-field class="header-item">
<b-input
:placeholder="$i18n.get('instruction_type_search_metadata_filter')"
v-model="metadataNameFilterString"
icon="magnify"
size="is-small"
icon-right="close-circle"
icon-right-clickable
@icon-right-click="metadataNameFilterString = ''" />
</b-field>
</template>
</div>
<section
v-if="activeMetadataSectionsList.length <= 0 && !isLoadingMetadataSections"
class="field is-grouped-centered section">
<div class="content has-text-gray has-text-centered">
<p>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-metadata"/>
</span>
</p>
<p>{{ $i18n.get('info_there_is_no_metadata_section') }}</p>
</div>
</section>
<!-- The Metadata Sections list -->
<draggable
v-model="activeMetadataSectionsList"
class="active-metadata-sections-area"
@change="handleSectionChange($event)"
:group="{ name:'metadata-sections', pull: false, put: [ 'metadata-sections' ] }"
:sort="(openedMetadataSectionId == '' || openedMetadataSectionId == undefined) && (openedMetadatumId == '' || openedMetadatumId == undefined)"
:handle="'.handle'"
ghost-class="sortable-ghost"
chosen-class="sortable-chosen"
filter=".not-sortable-item"
:prevent-on-filter="false"
:animation="250">
<div
v-for="(metadataSection, sectionIndex) in activeMetadataSectionsList"
:key="metadataSection.id">
<div
class="active-metadata-sections-item"
:class="{
'is-compact-item': !isCollapseOpen(metadataSection.id),
'not-sortable-item':
metadataSection.id == undefined ||
openedMetadatumId != '' ||
openedMetadataSectionId != '' ||
isUpdatingMetadataOrder ||
isUpdatingMetadatum ||
isUpdatingMetadataSectionsOrder ||
metadataNameFilterString != '' ||
hasSomeMetadataTypeFilterApplied,
'not-focusable-item': openedMetadataSectionId == metadataSection.id,
'disabled-metadatum': metadataSection.enabled == false,
'inherited-metadatum': false
}">
<div
:ref="'metadata-section-handler-' + metadataSection.id"
class="handle">
<span class="sorting-buttons">
<button
:disabled="sectionIndex == 0"
class="link-button"
@click="moveMetadataSectionUpViaButon(sectionIndex)">
<span class="icon">
<i class="tainacan-icon tainacan-icon-previous tainacan-icon-rotate-90" />
</span>
</button>
<button
:disabled="sectionIndex == activeMetadataSectionsList.length - 1"
class="link-button"
@click="moveMetadataSectionDownViaButton(sectionIndex)">
<span class="icon">
<i class="tainacan-icon tainacan-icon-next tainacan-icon-rotate-90" />
</span>
</button>
</span>
<span
:style="{ opacity: !(metadataSection.id == undefined || openedMetadatumId != '' || isUpdatingMetadataOrder || openedMetadataSectionId != '' || isUpdatingMetadataSectionsOrder || metadataNameFilterString != '' || hasSomeMetadataTypeFilterApplied) ? '1.0' : '0.0' }"
v-tooltip="{
content: metadataSection.id == undefined || openedMetadatumId != '' || isUpdatingMetadataOrder || openedMetadataSectionId != '' || isUpdatingMetadataSectionsOrder || isUpdatingMetadatum ? $i18n.get('info_not_allowed_change_order_metadata_sections') : $i18n.get('instruction_drag_and_drop_metadata_sections_sort'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip'],
placement: 'auto-start'
}"
class="icon grip-icon">
<svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 0 24 24"
width="24px"
fill="currentColor">
<path
d="M0 0h24v24H0V0z"
fill="transparent"/>
<path d="M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
</svg>
</span>
<span class="metadatum-name">
<h3>{{ metadataSection.name }}</h3>
</span>
<span
v-if="metadataSection.id != undefined"
class="label-details"
:class="{ 'has-text-weight-bold': metadataSection.id === 'default_section' }">
<span
v-if="metadataSection.id === 'default_section'"
v-tooltip="{
content: $i18n.get('label_required'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip'],
placement: 'auto-start'
}">
*&nbsp;({{ $i18n.get('label_default_section') }})
</span>
<span
v-if="metadataSection.status === 'private'"
class="icon"
v-tooltip="{
content: $i18n.get('status_private'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip'],
placement: 'auto-start'
}">
<i class="tainacan-icon tainacan-icon-private"/>
</span>
</span>
<span
class="loading-spinner"
v-if="metadataSection.id == undefined"/>
<span
class="controls"
v-if="metadataSection.id !== undefined">
<b-switch
:disabled="isUpdatingMetadataSectionsOrder"
size="is-small"
:value="metadataSection.enabled"
@input="onChangeEnableSection($event, sectionIndex)"/>
<a
v-if="metadataSection.current_user_can_edit"
:style="{ visibility:
metadataSection.collection_id != collectionId
? 'hidden' : 'visible'
}"
@click.prevent="toggleMetadataSectionEdition(metadataSection)">
<span
v-tooltip="{
content: $i18n.get('edit'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip'],
placement: 'auto-start'
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-edit"/>
</span>
</a>
<a
v-if="metadataSection.current_user_can_delete"
:disabled="metadataSection.metadata_object_list.length"
:style="{ visibility: metadataSection.collection_id != collectionId || metadataSection.id === 'default_section' || metadataSection.metadata_object_list.length ? 'hidden' : 'visible' }"
@click.prevent="removeMetadataSection(metadataSection)">
<span
v-tooltip="{
content: $i18n.get('delete'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip'],
placement: 'auto-start'
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-delete"/>
</span>
</a>
</span>
</div>
</div>
<section
v-if="metadataSection.metadata_object_list && metadataSection.metadata_object_list.length <= 0"
class="field is-grouped-centered section">
<div class="content has-text-gray has-text-centered">
<p>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-metadata"/>
</span>
</p>
<p>{{ $i18n.get('info_there_is_no_metadatum' ) }}</p>
<p>{{ $i18n.get('info_create_metadata' ) }}</p>
</div>
</section>
<b-loading :active.sync="isUpdatingMetadatum"/>
<!-- The Metadata list, inside each metadata section -->
<template v-if="metadataSection.metadata_object_list && Array.isArray(metadataSection.metadata_object_list)">
<draggable
v-model="metadataSection.metadata_object_list"
class="active-metadata-area"
@change="handleChange($event, sectionIndex)"
:group="{ name:'metadata', pull: [ 'metadata' ], put: [ 'metadata' ] }"
:sort="(openedMetadatumId == '' || openedMetadatumId == undefined)"
:handle="'.handle'"
ghost-class="sortable-ghost"
chosen-class="sortable-chosen"
filter=".not-sortable-item"
:prevent-on-filter="false"
:animation="250">
<div
v-for="(metadatum, index) in metadataSection.metadata_object_list.filter((meta) => meta != undefined && meta.parent == 0)"
:key="metadatum.id"
v-show="(metadataNameFilterString == '' || filterByMetadatumName(metadatum)) && filterByMetadatumType(metadatum)">
<div
class="active-metadatum-item"
:class="{
'is-compact-item': !isCollapseOpen(metadatum.id),
'not-sortable-item': metadatum.id == undefined || openedMetadatumId != '' || isUpdatingMetadataOrder || metadataNameFilterString != '' || hasSomeMetadataTypeFilterApplied || isUpdatingMetadatum,
'not-focusable-item': openedMetadatumId == metadatum.id,
'disabled-metadatum': metadataSection.enabled == false || metadatum.enabled == false,
'inherited-metadatum': metadatum.inherited,
'child-metadatum': metadatum.parent > 0
}">
<div
:ref="'metadatum-handler-' + metadatum.id"
class="handle">
<span class="sorting-buttons">
<button
:disabled="index == 0"
class="link-button"
@click="moveMetadatumUpViaButton(index, sectionIndex)"
:aria-label="$i18n.get('label_move_up')">
<span class="icon">
<i class="tainacan-icon tainacan-icon-previous tainacan-icon-rotate-90" />
</span>
</button>
<button
:disabled="index == metadataSection.metadata_object_list.filter((meta) => meta != undefined && meta.parent == 0).length - 1"
class="link-button"
@click="moveMetadatumDownViaButton(index, sectionIndex)"
:aria-label="$i18n.get('label_move_down')">
<span class="icon">
<i class="tainacan-icon tainacan-icon-next tainacan-icon-rotate-90" />
</span>
</button>
</span>
<span
:style="{ opacity: !(metadatum.id == undefined || openedMetadatumId != '' || isUpdatingMetadataOrder || metadataNameFilterString != '' || hasSomeMetadataTypeFilterApplied) ? '1.0' : '0.0' }"
v-tooltip="{
content: metadatum.id == undefined || openedMetadatumId != '' || isUpdatingMetadataOrder || isUpdatingMetadatum ? $i18n.get('info_not_allowed_change_order_metadata') : $i18n.get('instruction_drag_and_drop_metadatum_sort'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip'],
placement: 'auto-start'
}"
class="icon grip-icon">
<svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 0 24 24"
width="24px"
fill="currentColor">
<path
d="M0 0h24v24H0V0z"
fill="transparent"/>
<path d="M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
</svg>
</span>
<span
v-tooltip="{
content: $i18n.get('label_view_metadata_details'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip'],
placement: 'auto-start'
}"
@click="$set(collapses, metadatum.id, !isCollapseOpen(metadatum.id))"
class="gray-icon icon"
:style="{ cursor: 'pointer', opacity: openedMetadatumId != metadatum.id ? '1.0' : '0.0' }">
<i :class="'tainacan-icon tainacan-icon-1-25em tainacan-icon-' + (isCollapseOpen(metadatum.id) ? 'arrowdown' : 'arrowright')" />
</span>
<span class="metadatum-name">
{{ metadatum.name }}
</span>
<span
v-if="metadatum.id != undefined && metadatum.metadata_type_object"
class="label-details"
:class="{ 'has-text-weight-bold': metadatum.metadata_type_object.core }">
<span
v-if="metadatum.required === 'yes'"
v-tooltip="{
content: $i18n.get('label_required'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip'],
placement: 'auto-start'
}">
*&nbsp;
</span>
({{ metadatum.metadata_type_object.name }})
<span
v-if="metadatum.status === 'private'"
class="icon"
v-tooltip="{
content: $i18n.get('status_private'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip'],
placement: 'auto-start'
}">
<i class="tainacan-icon tainacan-icon-private"/>
</span>
<span
v-tooltip="{
content: (metadatum.collection_id == 'default') ? $i18n.get('label_repository_metadatum') : $i18n.get('label_collection_metadatum'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip'],
placement: 'auto-start'
}"
class="icon icon-level-identifier">
<i
v-if="metadatum.collection_id == 'default'"
:class="{
'has-text-blue5': metadatum.enabled,
'has-text-gray3': !metadatum.enabled
}"
class="tainacan-icon tainacan-icon-repository" />
<i
v-else
:class="{
'has-text-turquoise5': metadatum.enabled,
'has-text-gray3': !metadatum.enabled
}"
class="tainacan-icon tainacan-icon-collection" />
</span>
</span>
<span
class="loading-spinner"
v-if="metadatum.id == undefined || isUpdatingMetadatum"/>
<span
class="controls"
v-if="metadatum.id !== undefined">
<b-switch
:style="{ visibility: !metadataSection.enabled ? 'hidden' : 'visible' }"
:disabled="isUpdatingMetadataOrder || !metadataSection.enabled"
size="is-small"
:value="metadatum.enabled"
@input="onChangeEnable($event, index, sectionIndex)"/>
<a
v-if="metadatum.current_user_can_edit"
:style="{ visibility:
metadatum.collection_id != collectionId
? 'hidden' : 'visible'
}"
@click.prevent="toggleMetadatumEdition(metadatum)">
<span
v-tooltip="{
content: $i18n.get('edit'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip'],
placement: 'auto-start'
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-edit"/>
</span>
</a>
<a
v-if="metadatum.current_user_can_delete"
:style="{ visibility: metadatum.collection_id != collectionId || metadatum.metadata_type_object.core ? 'hidden' : 'visible' }"
@click.prevent="removeMetadatum(metadatum, sectionIndex)">
<span
v-tooltip="{
content: $i18n.get('delete'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip'],
placement: 'auto-start'
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-delete"/>
</span>
</a>
</span>
</div>
<transition name="form-collapse">
<metadatum-details
v-if="isCollapseOpen(metadatum.id) && openedMetadatumId !== metadatum.id"
:metadatum="metadatum" />
</transition>
</div>
<!-- Child metadata list, inside each compound metadata -->
<child-metadata-list
v-if="metadatum.metadata_type_object && metadatum.metadata_type_object.component == 'tainacan-compound'"
:parent="metadatum"
:metadata-name-filter-string="metadataNameFilterString"
:metadata-type-filter-options="metadataTypeFilterOptions"
:has-some-metadata-type-filter-applied="hasSomeMetadataTypeFilterApplied"
:is-parent-multiple="metadatum.multiple == 'yes'"
:is-repository-level="false"
:collapse-all="collapseAll"
:section-id="metadataSection.id" />
<!-- Metadata edition form, for each metadata -->
<b-modal
@close="onEditionCanceled()"
:active="openedMetadatumId == metadatum.id"
trap-focus
aria-modal
aria-role="dialog"
custom-class="tainacan-modal"
:close-button-aria-label="$i18n.get('close')">
<metadatum-edition-form
:collection-id="collectionId"
:original-metadatum="metadatum"
:is-repository-level="false"
@onEditionFinished="onEditionFinished()"
@onEditionCanceled="onEditionCanceled()"
:index="index" />
</b-modal>
</div>
</draggable><!-- End of .active-metadata-area -->
</template>
<!-- Metadata Section edition form, for each metadata section -->
<b-modal
@close="onSectionEditionCanceled()"
:active="openedMetadataSectionId == metadataSection.id"
trap-focus
aria-modal
aria-role="dialog"
custom-class="tainacan-modal"
:close-button-aria-label="$i18n.get('close')">
<metadata-section-edition-form
:collection-id="collectionId"
:original-metadata-section="metadataSection"
@onEditionFinished="onSectionEditionFinished()"
@onEditionCanceled="onSectionEditionCanceled()"
:index="sectionIndex" />
</b-modal>
</div>
</draggable> <!-- End of .active-metadata-sections-area -->
</div> <!-- End of .columns -->
</template>
<script>
import MetadatumEditionForm from '../../components/edition/metadatum-edition-form.vue';
import MetadataSectionEditionForm from '../../components/edition/metadata-section-edition-form.vue';
import MetadatumDetails from '../../components/other/metadatum-details.vue';
import ChildMetadataList from '../../components/metadata-types/compound/child-metadata-list.vue';
import CustomDialog from '../../components/other/custom-dialog.vue';
import { mapGetters, mapActions } from 'vuex';
export default {
name: 'CollectionMetadataList',
components: {
MetadatumEditionForm,
MetadataSectionEditionForm,
ChildMetadataList,
MetadatumDetails
},
props: {
metadataTypeFilterOptions: Array
},
data() {
return {
collectionId: '',
isLoadingMetadataSections: false,
isUpdatingMetadataOrder: false,
openedMetadatumId: '',
openedMetadataSectionId: '',
hightlightedMetadatum: '',
collapses: {},
collapseAll: false,
metadataNameFilterString: '',
isUpdatingMetadataSectionsOrder: false,
isUpdatingMetadatum: false,
metadataSearchCancel: undefined
}
},
computed: {
hasSomeMetadataTypeFilterApplied() {
return this.metadataTypeFilterOptions.length && this.metadataTypeFilterOptions.some((metadatumType) => metadatumType.enabled);
},
activeMetadataSectionsList: {
get() {
return this.getMetadataSections();
},
set(value) {
this.updateMetadataSections(value);
}
}
},
watch: {
'$route.query': {
handler(newQuery) {
if (newQuery.edit != undefined) {
let existingMetadataIndex = -1;
let existingMetadataSectionIndex = -1;
for (let i = 0; i < this.activeMetadataSectionsList.length; i++) {
existingMetadataIndex = this.activeMetadataSectionsList[i].metadata_object_list.findIndex((metadatum) => metadatum && (metadatum.id == newQuery.edit));
if (existingMetadataIndex >= 0) {
existingMetadataSectionIndex = i;
break;
}
}
if (existingMetadataIndex >= 0 && existingMetadataSectionIndex >= 0)
this.editMetadatum(newQuery.edit);
} else if (newQuery.sectionEdit != undefined) {
let existingMetadataSectionIndex = this.activeMetadataSectionsList.findIndex((metadataSection) => metadataSection && (metadataSection.id == newQuery.sectionEdit));
if (existingMetadataSectionIndex >= 0)
this.editMetadataSection(newQuery.sectionEdit);
}
},
immediate: true
},
collapseAll(isCollapsed) {
this.activeMetadataSectionsList.forEach((metadataSection) => {
if ( metadataSection.metadata_object_list && Array.isArray(metadataSection.metadata_object_list) )
metadataSection.metadata_object_list.forEach((metadatum) => this.$set(this.collapses, metadatum.id, isCollapsed));
});
}
},
mounted() {
this.cleanMetadataSections();
this.$eventBusMetadataList.$on('addMetadatumViaButton', this.addMetadatumViaButton);
this.$eventBusMetadataList.$on('addMetadataSectionViaButton', this.addMetadataSectionViaButton);
this.collectionId = this.$route.params.collectionId;
this.isLoadingMetadataSections = true;
this.fetchMetadataSections({ collectionId: this.collectionId, isContextEdit: true, includeDisabled: true })
.then(() => {
this.isLoadingMetadataSections = false;
})
.catch((error) => {
this.$console.error(error);
this.isLoadingMetadataSections = false;
});
},
beforeDestroy() {
// Cancels previous Request
if (this.metadataSearchCancel != undefined)
this.metadataSearchCancel.cancel('Metadata search Canceled.');
this.$eventBusMetadataList.$off('addMetadatumViaButton', this.addMetadatumViaButton);
this.$eventBusMetadataList.$off('addMetadataSectionViaButton', this.addMetadataSectionViaButton);
},
methods: {
...mapActions('metadata', [
'sendMetadatum',
'sendMetadataSection',
'deleteMetadatum',
'updateMetadatum',
'updateCollectionMetadataOrder',
'updateCollectionMetadataSectionsOrder',
'updateMetadataSections',
'fetchMetadataSections',
'deleteMetadataSection',
'cleanMetadataSections',
'moveMetadataSectionUp',
'moveMetadataSectionDown',
'moveMetadatumUp',
'moveMetadatumDown'
]),
...mapGetters('metadata',[
'getMetadataSections'
]),
handleSectionChange(event) {
if (event.added)
this.addNewMetadataSection(event.added.newIndex);
else if (event.removed)
this.removeMetadataSection(event.removed.element);
else if (event.moved)
this.updateMetadataSectionsOrder();
},
handleChange(event, sectionIndex) {
if (event.added) {
if (!event.added.element.id)
this.addNewMetadatum(event.added.element, event.added.newIndex, sectionIndex);
else {
this.updateMetadatum({
collectionId: this.collectionId,
metadatumId: event.added.element.id,
isRepositoryLevel: event.added.element.collection_id === 'default',
index: event.added.newIndex,
options: {},
includeOptionsAsHtml: true,
sectionId: this.activeMetadataSectionsList[sectionIndex].id
});
this.updateMetadataSectionsOrder(sectionIndex);
}
}
else if (event.moved)
this.updateMetadataOrder(sectionIndex);
},
updateMetadataOrder(sectionIndex) {
let metadataOrder = [];
for (let metadatum of this.activeMetadataSectionsList[sectionIndex].metadata_object_list)
if (metadatum != undefined)
metadataOrder.push({
'id': metadatum.id,
'enabled': metadatum.enabled
});
this.isUpdatingMetadataOrder = true;
this.updateCollectionMetadataOrder({ collectionId: this.collectionId, metadataOrder: metadataOrder, metadataSectionId: this.activeMetadataSectionsList[sectionIndex].id })
.then(() => this.isUpdatingMetadataOrder = false)
.catch(() => this.isUpdatingMetadataOrder = false);
},
updateMetadataSectionsOrder() {
let metadataSectionsOrder = [];
for (let metadataSection of this.activeMetadataSectionsList)
if (metadataSection != undefined) {
metadataSectionsOrder.push({
'id': metadataSection.id,
'enabled': metadataSection.enabled,
'metadata_order': metadataSection.metadata_object_list.map((aMetadatum) => { return { 'id': aMetadatum.id, 'enabled': aMetadatum.enabled } })
});
}
this.isUpdatingMetadataSectionsOrder = true;
this.updateCollectionMetadataSectionsOrder({ collectionId: this.collectionId, metadataSectionsOrder: metadataSectionsOrder })
.then(() => this.isUpdatingMetadataSectionsOrder = false)
.catch(() => this.isUpdatingMetadataSectionsOrder = false);
},
onChangeEnable($event, index, sectionIndex) {
let metadataOrder = [];
for (let metadatum of this.activeMetadataSectionsList[sectionIndex].metadata_object_list)
if (metadatum != undefined)
metadataOrder.push({'id': metadatum.id, 'enabled': metadatum.enabled});
metadataOrder[index].enabled = $event;
this.isUpdatingMetadataOrder = true;
this.updateCollectionMetadataOrder({ collectionId: this.collectionId, metadataOrder: metadataOrder, metadataSectionId: this.activeMetadataSectionsList[sectionIndex].id })
.then(() => this.isUpdatingMetadataOrder = false)
.catch(() => this.isUpdatingMetadataOrder = false);
},
onChangeEnableSection($event, index) {
let metadataSectionsOrder = [];
for (let metadataSection of this.activeMetadataSectionsList)
if (metadataSection != undefined)
metadataSectionsOrder.push({
'id': metadataSection.id,
'enabled': metadataSection.enabled,
'metadata_order': metadataSection.metadata_object_list.map((aMetadatum) => { return { 'id': aMetadatum.id, 'enabled': aMetadatum.enabled } })
});
metadataSectionsOrder[index].enabled = $event;
this.isUpdatingMetadataSectionsOrder = true;
this.updateCollectionMetadataSectionsOrder({ collectionId: this.collectionId, metadataSectionsOrder: metadataSectionsOrder })
.then(() => this.isUpdatingMetadataSectionsOrder = false)
.catch(() => this.isUpdatingMetadataSectionsOrder = false);
},
addMetadatumViaButton(metadatumType) {
this.addNewMetadatum(metadatumType, this.activeMetadataSectionsList[0].metadata_object_list.length, 0);
// Higlights the clicked metadatum
this.hightlightedMetadatum = metadatumType.name;
this.$emit('onUpdatehightlightedMetadatum', this.hightlightedMetadatum);
},
addMetadataSectionViaButton() {
let lastIndex = this.activeMetadataSectionsList.length;
this.addNewMetadataSection(lastIndex);
},
addNewMetadatum(newMetadatum, newIndex, sectionIndex) {
this.isUpdatingMetadatum = true;
this.sendMetadatum({
collectionId: this.collectionId,
name: newMetadatum.name,
metadatumType: newMetadatum.className,
status: 'auto-draft',
isRepositoryLevel: false,
newIndex: newIndex,
parent: '0',
sectionId: this.activeMetadataSectionsList[sectionIndex].id
})
.then((metadatum) => {
this.updateMetadataOrder(sectionIndex);
this.toggleMetadatumEdition(metadatum)
this.hightlightedMetadatum = '';
this.isUpdatingMetadatum = false;
this.$emit('onUpdatehightlightedMetadatum', this.hightlightedMetadatum);
})
.catch((error) => {
this.isUpdatingMetadatum = false;
this.$console.error(error);
});
},
addNewMetadataSection(newIndex) {
this.isUpdatingMetadatum = true;
this.sendMetadataSection({
collectionId: this.collectionId,
name: this.$i18n.get('label_new_metadata_section'),
status: 'auto-draft',
newIndex: newIndex
})
.then((metadataSection) => {
this.updateMetadataSectionsOrder();
this.toggleMetadataSectionEdition(metadataSection);
this.isUpdatingMetadatum = false;
})
.catch((error) => {
this.$console.error(error);
this.isUpdatingMetadatum = false;
});
},
removeMetadatum(removedMetadatum, sectionIndex) {
this.$buefy.modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_metadatum_delete'),
onConfirm: () => {
this.isUpdatingMetadataOrder = true;
this.deleteMetadatum({
collectionId: this.collectionId,
metadatumId: removedMetadatum.id,
isRepositoryLevel: false
})
.then(() => {
this.updateMetadataOrder(sectionIndex);
})
.catch(() => {
this.isUpdatingMetadataOrder = false;
this.$console.log("Error deleting metadatum.")
});
}
},
trapFocus: true,
customClass: 'tainacan-modal',
closeButtonAriaLabel: this.$i18n.get('close')
});
},
removeMetadataSection(removedMetadataSection) {
this.$buefy.modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_metadata_section_delete'),
onConfirm: () => {
this.isUpdatingMetadataSectionsOrder = true;
this.deleteMetadataSection({ collectionId: this.collectionId, metadataSectionId: removedMetadataSection.id })
.then(() => {
this.updateMetadataSectionsOrder();
})
.catch(() => {
this.isUpdatingMetadataSectionsOrder = false;
this.$console.log("Error deleting metadata section.")
});
}
},
trapFocus: true,
customClass: 'tainacan-modal',
closeButtonAriaLabel: this.$i18n.get('close')
});
},
toggleMetadatumEdition(metadatum) {
this.$router.push({ query: { edit: metadatum.id } });
},
toggleMetadataSectionEdition(metadataSection) {
this.$router.push({ query: { sectionEdit: metadataSection.id } });
},
editMetadatum(metadatumId) {
this.openedMetadatumId = metadatumId;
},
editMetadataSection(metadataSectionId) {
this.openedMetadataSectionId = metadataSectionId;
},
onEditionFinished() {
this.openedMetadatumId = '';
this.$router.push({ query: {}});
},
onEditionCanceled() {
this.openedMetadatumId = '';
this.$router.push({ query: {}});
},
onSectionEditionFinished() {
this.openedMetadataSectionId = '';
this.$router.push({ query: {}});
},
onSectionEditionCanceled() {
this.openedMetadataSectionId = '';
this.$router.push({ query: {}});
},
moveMetadatumUpViaButton(index, sectionIndex) {
this.moveMetadatumUp({ index, sectionIndex });
this.updateMetadataOrder(sectionIndex);
},
moveMetadatumDownViaButton(index, sectionIndex) {
this.moveMetadatumDown({ index, sectionIndex });
this.updateMetadataOrder(sectionIndex);
},
moveMetadataSectionUpViaButon(sectionIndex) {
this.moveMetadataSectionUp(sectionIndex);
this.updateMetadataSectionsOrder();
},
moveMetadataSectionDownViaButton(sectionIndex) {
this.moveMetadataSectionDown(sectionIndex);
this.updateMetadataSectionsOrder();
},
filterByMetadatumName(metadatum) {
if (metadatum.metadata_type_object &&
metadatum.metadata_type_object.component == 'tainacan-compound' &&
metadatum.metadata_type_options &&
metadatum.metadata_type_options.children_objects &&
metadatum.metadata_type_options.children_objects.length
) {
let childNamesArray = metadatum.metadata_type_options.children_objects.map((children) => children.name);
childNamesArray.push(metadatum.name);
return childNamesArray.some((childName) => childName.toString().toLowerCase().indexOf(this.metadataNameFilterString.toString().toLowerCase()) >= 0);
}
else
return metadatum.name.toString().toLowerCase().indexOf(this.metadataNameFilterString.toString().toLowerCase()) >= 0;
},
filterByMetadatumType(metadatum) {
if (!this.hasSomeMetadataTypeFilterApplied)
return true;
if (metadatum.metadata_type_object &&
metadatum.metadata_type_object.component == 'tainacan-compound' &&
metadatum.metadata_type_options &&
metadatum.metadata_type_options.children_objects &&
metadatum.metadata_type_options.children_objects.length
) {
let childTypesArray = metadatum.metadata_type_options.children_objects.map((children) => children.metadata_type);
childTypesArray.push(metadatum.metadata_type);
for (let metadatumType of this.metadataTypeFilterOptions) {
if (metadatumType.enabled && childTypesArray.some((childType) => childType == metadatumType.type))
return true;
}
} else {
for (let metadatumType of this.metadataTypeFilterOptions) {
if (metadatumType.enabled && metadatum.metadata_type == metadatumType.type)
return true;
}
}
return false;
},
isCollapseOpen(metadatumId) {
return this.collapses[metadatumId] == true;
}
}
}
</script>

View File

@ -2,84 +2,80 @@
<div
style="min-height: initial; position: relative"
class="tainacan-cards-container">
<template v-if="collections.length <= 0 && !isLoading && $userCaps.hasCapability('tnc_rep_edit_collections')">
<ul class="new-collection-menu">
<li>
<router-link
tag="a"
:to="$routerHelper.getNewCollectionPath()"
class="first-card">
<div class="list-metadata">
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-addcollection"/>
</span>
<div>{{ $i18n.get('label_create_collection') }}</div>
</div>
</router-link>
</li>
<li>
<router-link
tag="a"
:to="{ path: $routerHelper.getNewCollectionPath() }"
:aria-label="$i18n.get('label_collection_items')">
<span
v-tooltip="{
content: $i18n.get('label_collection_items'),
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon is-medium">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-items"/>
</span>
<span class="menu-text">{{ $i18n.get('items') }}</span>
</router-link>
</li>
<li>
<router-link
tag="a"
:to="{ path: $routerHelper.getNewCollectionPath() }"
:aria-label="$i18n.get('label_collection_metadata')">
<span
v-tooltip="{
content: $i18n.get('label_collection_metadata'),
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon is-medium">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-metadata"/>
</span>
<span class="menu-text">{{ $i18n.getFrom('metadata', 'name') }}</span>
</router-link>
</li>
<li>
<router-link
tag="a"
:to="{ path: $routerHelper.getNewCollectionPath() }"
:aria-label="$i18n.get('label_collection_filters')">
<span
v-tooltip="{
content: $i18n.get('label_collection_filters'),
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon is-medium">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-filters"/>
</span>
<span class="menu-text">{{ $i18n.getFrom('filters', 'name') }}</span>
</router-link>
</li>
</ul>
</template>
<template v-if="collections.length > 0 && !isLoading">
<masonry
:cols="{ default: 5, 1919: 4, 1407: 3, 1215: 2, 1023: 2, 767: 1 }"
:gutter="25"
style="width:100%;">
<ul
v-if="collections.length <= 0 && !isLoading && $userCaps.hasCapability('tnc_rep_edit_collections')"
class="new-collection-menu">
<li>
<router-link
tag="a"
:to="$routerHelper.getNewCollectionPath()"
class="first-card">
<div class="list-metadata">
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-addcollection"/>
</span>
<div>{{ $i18n.get('label_create_collection') }}</div>
</div>
</router-link>
</li>
<li>
<router-link
tag="a"
:to="{ path: $routerHelper.getNewCollectionPath() }"
:aria-label="$i18n.get('label_collection_items')">
<span
v-tooltip="{
content: $i18n.get('label_collection_items'),
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon is-medium">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-items"/>
</span>
<span class="menu-text">{{ $i18n.get('items') }}</span>
</router-link>
</li>
<li>
<router-link
tag="a"
:to="{ path: $routerHelper.getNewCollectionPath() }"
:aria-label="$i18n.get('label_collection_metadata')">
<span
v-tooltip="{
content: $i18n.get('label_collection_metadata'),
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon is-medium">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-metadata"/>
</span>
<span class="menu-text">{{ $i18n.getFrom('metadata', 'name') }}</span>
</router-link>
</li>
<li>
<router-link
tag="a"
:to="{ path: $routerHelper.getNewCollectionPath() }"
:aria-label="$i18n.get('label_collection_filters')">
<span
v-tooltip="{
content: $i18n.get('label_collection_filters'),
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon is-medium">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-filters"/>
</span>
<span class="menu-text">{{ $i18n.getFrom('filters', 'name') }}</span>
</router-link>
</li>
</ul>
<ul v-if="collections.length > 0 && !isLoading">
<li v-if="$userCaps.hasCapability('tnc_rep_edit_collections')">
<router-link
v-if="$userCaps.hasCapability('tnc_rep_edit_collections')"
tag="a"
:to="$routerHelper.getNewCollectionPath()"
class="tainacan-card new-card">
@ -143,149 +139,149 @@
</li>
</ul>
</router-link>
<div
v-if="collections.length > 0 && !isLoading"
:key="index"
v-for="(collection, index) of collections"
class="tainacan-card"
:class="{ 'always-visible-collections': $adminOptions.homeCollectionsPerPage }">
<ul class="menu-list">
<li v-if="!$adminOptions.hideHomeCollectionItemsButton">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionItemsPath(collection.id, '') }"
:aria-label="$i18n.get('label_collection_items')">
<span
v-tooltip.bottom="{
content: $i18n.get('items'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-items"/>
</span>
<!-- <span class="menu-text">{{ $i18n.get('items') }}</span> -->
</router-link>
</li>
<li v-if="collection.current_user_can_edit_items && $adminOptions.showHomeCollectionCreateItemButton">
<router-link
tag="a"
:to="{ path: $routerHelper.getNewItemPath(collection.id) }"
:aria-label="$i18n.get('add_one_item')">
<span
v-tooltip.bottom="{
content: $i18n.get('add_one_item'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-add"/>
</span>
<!-- <span class="menu-text">{{ $i18n.get('add_one_item') }}</span> -->
</router-link>
</li>
<li v-if="collection.current_user_can_edit && !$adminOptions.hideHomeCollectionSettingsButton">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionEditPath(collection.id) }"
:aria-label="$i18n.get('label_settings')">
<span
v-tooltip.bottom="{
content: $i18n.get('label_settings'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-settings"/>
</span>
<!-- <span class="menu-text">{{ $i18n.get('label_settings') }}</span> -->
</router-link>
</li>
<li v-if="collection.current_user_can_edit_metadata && !$adminOptions.hideHomeCollectionMetadataButton">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionMetadataPath(collection.id) }"
:aria-label="$i18n.get('label_collection_metadata')">
<span
v-tooltip.bottom="{
content: $i18n.getFrom('metadata', 'name'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-metadata"/>
</span>
<!-- <span class="menu-text">{{ $i18n.getFrom('metadata', 'name') }}</span> -->
</router-link>
</li>
<li v-if="collection.current_user_can_edit_filters && !$adminOptions.hideHomeCollectionFiltersButton">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionFiltersPath(collection.id) }"
:aria-label="$i18n.get('label_collection_filters')">
<span
v-tooltip.bottom="{
content: $i18n.getFrom('filters', 'name'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-filters"/>
</span>
<!-- <span class="menu-text">{{ $i18n.getFrom('filters', 'name') }}</span> -->
</router-link>
</li>
<li v-if="$userCaps.hasCapability('tnc_rep_read_logs') && !$adminOptions.hideHomeCollectionActivitiesButton">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionActivitiesPath(collection.id) }"
:aria-label="$i18n.get('label_collection_activities')">
<span
v-tooltip.bottom="{
content: $i18n.get('activities'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-activities"/>
</span>
<!-- <span class="menu-text">{{ $i18n.get('activities') }}</span> -->
</router-link>
</li>
<li v-if="!$adminOptions.hideHomeCollectionThemeCollectionButton">
<a
:href="collection.url"
target="_blank"
:aria-label="$i18n.get('label_view_collection_on_website')">
<span
v-tooltip.bottom="{
content: $i18n.get('label_view_collection_on_website'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-see"/>
</span>
</a>
</li>
</ul>
<router-link
tag="a"
:to="$routerHelper.getCollectionPath(collection.id)"
class="card-body">
<img
:alt="$i18n.get('label_thumbnail')"
v-if="collection.thumbnail != undefined"
:src="$thumbHelper.getSrc(collection['thumbnail'], 'tainacan-medium')">
<!-- Name -->
<div class="metadata-title">
<p>{{ collection.name != undefined ? collection.name : '' }}</p>
</div>
</router-link>
</div>
</masonry>
</template>
</li>
<li
v-if="collections.length > 0 && !isLoading"
:key="index"
v-for="(collection, index) of collections"
class="tainacan-card"
:class="{ 'always-visible-collections': $adminOptions.homeCollectionsPerPage }">
<ul class="menu-list">
<li v-if="!$adminOptions.hideHomeCollectionItemsButton">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionItemsPath(collection.id, '') }"
:aria-label="$i18n.get('label_collection_items')">
<span
v-tooltip.bottom="{
content: $i18n.get('items'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-items"/>
</span>
<!-- <span class="menu-text">{{ $i18n.get('items') }}</span> -->
</router-link>
</li>
<li v-if="collection.current_user_can_edit_items && $adminOptions.showHomeCollectionCreateItemButton">
<router-link
tag="a"
:to="{ path: $routerHelper.getNewItemPath(collection.id) }"
:aria-label="$i18n.get('add_one_item')">
<span
v-tooltip.bottom="{
content: $i18n.get('add_one_item'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-add"/>
</span>
<!-- <span class="menu-text">{{ $i18n.get('add_one_item') }}</span> -->
</router-link>
</li>
<li v-if="collection.current_user_can_edit && !$adminOptions.hideHomeCollectionSettingsButton">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionEditPath(collection.id) }"
:aria-label="$i18n.get('label_settings')">
<span
v-tooltip.bottom="{
content: $i18n.get('label_settings'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-settings"/>
</span>
<!-- <span class="menu-text">{{ $i18n.get('label_settings') }}</span> -->
</router-link>
</li>
<li v-if="collection.current_user_can_edit_metadata && !$adminOptions.hideHomeCollectionMetadataButton">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionMetadataPath(collection.id) }"
:aria-label="$i18n.get('label_collection_metadata')">
<span
v-tooltip.bottom="{
content: $i18n.getFrom('metadata', 'name'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-metadata"/>
</span>
<!-- <span class="menu-text">{{ $i18n.getFrom('metadata', 'name') }}</span> -->
</router-link>
</li>
<li v-if="collection.current_user_can_edit_filters && !$adminOptions.hideHomeCollectionFiltersButton">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionFiltersPath(collection.id) }"
:aria-label="$i18n.get('label_collection_filters')">
<span
v-tooltip.bottom="{
content: $i18n.getFrom('filters', 'name'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-filters"/>
</span>
<!-- <span class="menu-text">{{ $i18n.getFrom('filters', 'name') }}</span> -->
</router-link>
</li>
<li v-if="$userCaps.hasCapability('tnc_rep_read_logs') && !$adminOptions.hideHomeCollectionActivitiesButton">
<router-link
tag="a"
:to="{ path: $routerHelper.getCollectionActivitiesPath(collection.id) }"
:aria-label="$i18n.get('label_collection_activities')">
<span
v-tooltip.bottom="{
content: $i18n.get('activities'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-activities"/>
</span>
<!-- <span class="menu-text">{{ $i18n.get('activities') }}</span> -->
</router-link>
</li>
<li v-if="!$adminOptions.hideHomeCollectionThemeCollectionButton">
<a
:href="collection.url"
target="_blank"
:aria-label="$i18n.get('label_view_collection_on_website')">
<span
v-tooltip.bottom="{
content: $i18n.get('label_view_collection_on_website'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip']
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-see"/>
</span>
</a>
</li>
</ul>
<router-link
tag="a"
:to="$routerHelper.getCollectionPath(collection.id)"
class="card-body">
<img
:alt="$i18n.get('label_thumbnail')"
v-if="collection.thumbnail != undefined"
:src="$thumbHelper.getSrc(collection['thumbnail'], 'tainacan-medium')">
<!-- Name -->
<div class="metadata-title">
<p>{{ collection.name != undefined ? collection.name : '' }}</p>
</div>
</router-link>
</li>
</ul>
</div>
</template>

View File

@ -155,6 +155,7 @@
v-tooltip="{
content: $i18n.get('status_' + collection.status),
autoHide: true,
html: true,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}">
@ -193,6 +194,7 @@
},
content: collection.name,
autoHide: false,
html: true,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}">
@ -213,6 +215,7 @@
},
content: (collection.description != undefined && collection.description != '') ? collection.description : `<span class='has-text-gray is-italic'>` + $i18n.get('label_description_not_provided') + `</span>`,
autoHide: false,
html: true,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}"
@ -233,6 +236,7 @@
},
content: collection.modification_date,
autoHide: false,
html: true,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}"
@ -253,6 +257,7 @@
},
content: collection.creation_date,
autoHide: false,
html: true,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}"
@ -273,6 +278,7 @@
},
content: collection.author_name,
autoHide: false,
html: true,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}"
@ -294,6 +300,7 @@
},
content: getTotalItemsDetailed(collection.total_items),
autoHide: false,
html: true,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}"

View File

@ -309,148 +309,149 @@
</div>
<!-- MASONRY VIEW MODE -->
<masonry
role="list"
<ul
v-if="viewMode == 'masonry'"
:cols="{default: 7, 1919: 6, 1407: 5, 1215: 4, 1023: 3, 767: 2, 343: 1}"
:gutter="25"
:class="{ 'hide-items-selection': $adminOptions.hideItemsListSelection }"
:class="{
'hide-items-selection': $adminOptions.hideItemsListSelection,
'tainacan-masonry-container--legacy': shouldUseLegacyMasonyCols
}"
class="tainacan-masonry-container">
<div
role="listitem"
<li
:key="index"
:data-tainacan-item-id="item.id"
v-for="(item, index) of items"
:class="{
'selected-masonry-item': getSelectedItemChecked(item.id) == true,
'tainacan-masonry-grid-sizer': index == 0
}">
<div
:class="{
'selected-masonry-item': getSelectedItemChecked(item.id) == true
}"
class="tainacan-masonry-item">
class="tainacan-masonry-item"
@click.left="onClickItem($event, item)"
@click.right="onRightClickItem($event, item)">
<!-- Checkbox -->
<!-- TODO: Remove v-if="collectionId" from this element when the bulk edit in repository is done -->
<div
v-if="collectionId && !$adminOptions.hideItemsListSelection && ($adminOptions.itemsSingleSelectionMode || $adminOptions.itemsMultipleSelectionMode || (collection && collection.current_user_can_bulk_edit))"
:class="{ 'is-selecting': isSelectingItems }"
class="masonry-item-checkbox">
<label
tabindex="0"
:class="(!$adminOptions.itemsSingleSelectionMode ? 'b-checkbox checkbox' : 'b-radio radio') + ' is-small'">
<input
v-if="!$adminOptions.itemsSingleSelectionMode"
type="checkbox"
:checked="getSelectedItemChecked(item.id)"
@input="setSelectedItemChecked(item.id)">
<input
v-else
type="radio"
name="item-single-selection"
:value="item.id"
v-model="singleItemSelection">
<span class="check" />
<span class="control-label" />
<span class="sr-only">{{ $i18n.get('label_select_item') }}</span>
</label>
</div>
<!-- Checkbox -->
<!-- TODO: Remove v-if="collectionId" from this element when the bulk edit in repository is done -->
<div
v-if="collectionId && !$adminOptions.hideItemsListSelection && ($adminOptions.itemsSingleSelectionMode || $adminOptions.itemsMultipleSelectionMode || (collection && collection.current_user_can_bulk_edit))"
:class="{ 'is-selecting': isSelectingItems }"
class="masonry-item-checkbox">
<label
tabindex="0"
:class="(!$adminOptions.itemsSingleSelectionMode ? 'b-checkbox checkbox' : 'b-radio radio') + ' is-small'">
<input
v-if="!$adminOptions.itemsSingleSelectionMode"
type="checkbox"
:checked="getSelectedItemChecked(item.id)"
@input="setSelectedItemChecked(item.id)">
<input
v-else
type="radio"
name="item-single-selection"
:value="item.id"
v-model="singleItemSelection">
<span class="check" />
<span class="control-label" />
<span class="sr-only">{{ $i18n.get('label_select_item') }}</span>
</label>
<!-- Title -->
<div
:style="{
'padding-left': !collectionId || !($adminOptions.itemsSingleSelectionMode || $adminOptions.itemsMultipleSelectionMode || (collection && collection.current_user_can_bulk_edit)) || $adminOptions.itemsSearchSelectionMode ? '0 !important' : (isOnAllItemsTabs ? '0.5em' : '1em')
}"
class="metadata-title">
<p>
<span
v-if="isOnAllItemsTabs && $statusHelper.hasIcon(item.status)"
class="icon has-text-gray"
v-tooltip="{
content: $i18n.get('status_' + item.status),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : ''],
placement: 'auto-start'
}">
<i
class="tainacan-icon tainacan-icon-1em"
:class="$statusHelper.getIcon(item.status)"
/>
</span>
{{ item.title != undefined ? item.title : '' }}
</p>
</div>
<!-- Thumbnail -->
<blur-hash-image
v-if="item.thumbnail != undefined"
class="tainacan-masonry-item-thumbnail"
:width="$thumbHelper.getWidth(item['thumbnail'], shouldUseLegacyMasonyCols ? 'tainacan-medium-full' : 'tainacan-large-full', 280)"
:height="$thumbHelper.getHeight(item['thumbnail'], shouldUseLegacyMasonyCols ? 'tainacan-medium-full' : 'tainacan-large-full', 280)"
:hash="$thumbHelper.getBlurhashString(item['thumbnail'], shouldUseLegacyMasonyCols ? 'tainacan-medium-full' : 'tainacan-large-full')"
:src="$thumbHelper.getSrc(item['thumbnail'], shouldUseLegacyMasonyCols ? 'tainacan-medium-full' : 'tainacan-large-full', item.document_mimetype)"
:srcset="$thumbHelper.getSrcSet(item['thumbnail'], shouldUseLegacyMasonyCols ? 'tainacan-medium-full' : 'tainacan-large-full', item.document_mimetype)"
:alt="item.thumbnail_alt ? item.thumbnail_alt : $i18n.get('label_thumbnail')"
:transition-duration="500"
/>
<!-- Actions -->
<div
v-if="item.current_user_can_edit && !$adminOptions.hideItemsListActionAreas"
class="actions-area"
:label="$i18n.get('label_actions')">
<a
v-if="!isOnTrash"
id="button-edit"
:aria-label="$i18n.getFrom('items','edit_item')"
@click.prevent.stop="goToItemEditPage(item)">
<span
v-tooltip="{
content: $i18n.get('edit'),
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : '']
}"
class="icon">
<i class="has-text-secondary tainacan-icon tainacan-icon-1-25em tainacan-icon-edit"/>
</span>
</a>
<a
:aria-lavel="$i18n.get('label_button_untrash')"
@click.prevent.stop="untrashOneItem(item.id)"
v-if="isOnTrash">
<span
v-tooltip="{
content: $i18n.get('label_recover_from_trash'),
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : '']
}"
class="icon">
<i class="has-text-secondary tainacan-icon tainacan-icon-1-25em tainacan-icon-undo"/>
</span>
</a>
<a
v-if="item.current_user_can_delete"
id="button-delete"
:aria-label="$i18n.get('label_button_delete')"
@click.prevent.stop="deleteOneItem(item.id)">
<span
v-tooltip="{
content: isOnTrash ? $i18n.get('label_delete_permanently') : $i18n.get('delete'),
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : '']
}"
class="icon">
<i
:class="{ 'tainacan-icon-delete': !isOnTrash, 'tainacan-icon-deleteforever': isOnTrash }"
class="has-text-secondary tainacan-icon tainacan-icon-1-25em"/>
</span>
</a>
</div>
</div>
<!-- Title -->
<div
:style="{
'padding-left': !collectionId || !($adminOptions.itemsSingleSelectionMode || $adminOptions.itemsMultipleSelectionMode || (collection && collection.current_user_can_bulk_edit)) || $adminOptions.itemsSearchSelectionMode ? '0 !important' : (isOnAllItemsTabs ? '0.5em' : '1em')
}"
@click.left="onClickItem($event, item)"
@click.right="onRightClickItem($event, item)"
class="metadata-title">
<p>
<span
v-if="isOnAllItemsTabs && $statusHelper.hasIcon(item.status)"
class="icon has-text-gray"
v-tooltip="{
content: $i18n.get('status_' + item.status),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : ''],
placement: 'auto-start'
}">
<i
class="tainacan-icon tainacan-icon-1em"
:class="$statusHelper.getIcon(item.status)"
/>
</span>
{{ item.title != undefined ? item.title : '' }}
</p>
</div>
<!-- Thumbnail -->
<blur-hash-image
@click.left="onClickItem($event, item)"
@click.right="onRightClickItem($event, item)"
v-if="item.thumbnail != undefined"
class="tainacan-masonry-item-thumbnail"
:width="$thumbHelper.getWidth(item['thumbnail'], 'tainacan-medium-full', 120)"
:height="$thumbHelper.getHeight(item['thumbnail'], 'tainacan-medium-full', 120)"
:hash="$thumbHelper.getBlurhashString(item['thumbnail'], 'tainacan-medium-full')"
:src="$thumbHelper.getSrc(item['thumbnail'], 'tainacan-medium-full', item.document_mimetype)"
:srcset="$thumbHelper.getSrcSet(item['thumbnail'], 'tainacan-medium-full', item.document_mimetype)"
:alt="item.thumbnail_alt ? item.thumbnail_alt : $i18n.get('label_thumbnail')"
:transition-duration="500"
/>
<!-- Actions -->
<div
v-if="item.current_user_can_edit && !$adminOptions.hideItemsListActionAreas"
class="actions-area"
:label="$i18n.get('label_actions')">
<a
v-if="!isOnTrash"
id="button-edit"
:aria-label="$i18n.getFrom('items','edit_item')"
@click.prevent.stop="goToItemEditPage(item)">
<span
v-tooltip="{
content: $i18n.get('edit'),
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : '']
}"
class="icon">
<i class="has-text-secondary tainacan-icon tainacan-icon-1-25em tainacan-icon-edit"/>
</span>
</a>
<a
:aria-lavel="$i18n.get('label_button_untrash')"
@click.prevent.stop="untrashOneItem(item.id)"
v-if="isOnTrash">
<span
v-tooltip="{
content: $i18n.get('label_recover_from_trash'),
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : '']
}"
class="icon">
<i class="has-text-secondary tainacan-icon tainacan-icon-1-25em tainacan-icon-undo"/>
</span>
</a>
<a
v-if="item.current_user_can_delete"
id="button-delete"
:aria-label="$i18n.get('label_button_delete')"
@click.prevent.stop="deleteOneItem(item.id)">
<span
v-tooltip="{
content: isOnTrash ? $i18n.get('label_delete_permanently') : $i18n.get('delete'),
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : '']
}"
class="icon">
<i
:class="{ 'tainacan-icon-delete': !isOnTrash, 'tainacan-icon-deleteforever': isOnTrash }"
class="has-text-secondary tainacan-icon tainacan-icon-1-25em"/>
</span>
</a>
</div>
</div>
</masonry>
</li>
</ul>
<!-- CARDS VIEW MODE -->
<div
@ -656,215 +657,212 @@
</div>
<!-- RECORDS VIEW MODE -->
<masonry
role="list"
:cols="{default: 4, 1919: 3, 1407: 2, 1215: 2, 1023: 1, 767: 1, 343: 1}"
:gutter="30"
<ul
:class="{ 'hide-items-selection': $adminOptions.hideItemsListSelection }"
class="tainacan-records-container"
v-if="viewMode == 'records'">
<div
role="listitem"
<li
:key="index"
:data-tainacan-item-id="item.id"
v-for="(item, index) of items"
:class="{ 'selected-record': getSelectedItemChecked(item.id) == true }"
class="tainacan-record">
:class="{ 'tainacan-records-grid-sizer': index == 0 }">
<div
:class="{ 'selected-record': getSelectedItemChecked(item.id) == true }"
class="tainacan-record">
<!-- Checkbox -->
<!-- TODO: Remove v-if="collectionId" from this element when the bulk edit in repository is done -->
<div
v-if="collectionId && !$adminOptions.hideItemsListSelection && ($adminOptions.itemsSingleSelectionMode || $adminOptions.itemsMultipleSelectionMode || (collection && collection.current_user_can_bulk_edit))"
:class="{ 'is-selecting': isSelectingItems }"
class="record-checkbox">
<label
tabindex="0"
:class="(!$adminOptions.itemsSingleSelectionMode ? 'b-checkbox checkbox' : 'b-radio radio') + ' is-small'">
<input
v-if="!$adminOptions.itemsSingleSelectionMode"
type="checkbox"
:checked="getSelectedItemChecked(item.id)"
@input="setSelectedItemChecked(item.id)">
<input
v-else
type="radio"
name="item-single-selection"
:value="item.id"
v-model="singleItemSelection">
<span class="check" />
<span class="control-label" />
<span class="sr-only">{{ $i18n.get('label_select_item') }}</span>
</label>
</div>
<!-- Checkbox -->
<!-- TODO: Remove v-if="collectionId" from this element when the bulk edit in repository is done -->
<div
v-if="collectionId && !$adminOptions.hideItemsListSelection && ($adminOptions.itemsSingleSelectionMode || $adminOptions.itemsMultipleSelectionMode || (collection && collection.current_user_can_bulk_edit))"
:class="{ 'is-selecting': isSelectingItems }"
class="record-checkbox">
<label
tabindex="0"
:class="(!$adminOptions.itemsSingleSelectionMode ? 'b-checkbox checkbox' : 'b-radio radio') + ' is-small'">
<input
v-if="!$adminOptions.itemsSingleSelectionMode"
type="checkbox"
:checked="getSelectedItemChecked(item.id)"
@input="setSelectedItemChecked(item.id)">
<input
v-else
type="radio"
name="item-single-selection"
:value="item.id"
v-model="singleItemSelection">
<span class="check" />
<span class="control-label" />
<span class="sr-only">{{ $i18n.get('label_select_item') }}</span>
</label>
</div>
<!-- Title -->
<div
class="metadata-title"
:style="{
'padding-left': !collectionId || !($adminOptions.itemsSingleSelectionMode || $adminOptions.itemsMultipleSelectionMode || (collection && collection.current_user_can_bulk_edit)) || $adminOptions.itemsSearchSelectionMode ? '1.5em !important' : '2.75em'
}">
<span
v-if="isOnAllItemsTabs && $statusHelper.hasIcon(item.status)"
class="icon has-text-gray"
v-tooltip="{
content: $i18n.get('status_' + item.status),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : ''],
placement: 'auto-start'
<!-- Title -->
<div
class="metadata-title"
:style="{
'padding-left': !collectionId || !($adminOptions.itemsSingleSelectionMode || $adminOptions.itemsMultipleSelectionMode || (collection && collection.current_user_can_bulk_edit)) || $adminOptions.itemsSearchSelectionMode ? '1.5em !important' : '2.75em'
}">
<i
class="tainacan-icon tainacan-icon-1em"
:class="$statusHelper.getIcon(item.status)"
/>
</span>
<p
v-tooltip="{
delay: {
shown: 500,
hide: 300,
},
content: item.metadata != undefined ? renderMetadata(item.metadata, column) : '',
html: true,
autoHide: false,
placement: 'auto-start',
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : '']
}"
v-for="(column, columnIndex) in displayedMetadata"
:key="columnIndex"
v-if="collectionId != undefined && column.display && column.metadata_type_object != undefined && (column.metadata_type_object.related_mapped_prop == 'title')"
@click.left="onClickItem($event, item)"
@click.right="onRightClickItem($event, item)"
v-html="item.metadata != undefined ? renderMetadata(item.metadata, column) : ''" />
<p
v-tooltip="{
delay: {
shown: 500,
hide: 300,
},
content: item.title != undefined ? item.title : '',
html: true,
autoHide: false,
placement: 'auto-start',
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : '']
}"
v-for="(column, columnIndex) in displayedMetadata"
:key="columnIndex"
v-if="collectionId == undefined && column.display && column.metadata_type_object != undefined && (column.metadata_type_object.related_mapped_prop == 'title')"
@click.left="onClickItem($event, item)"
@click.right="onRightClickItem($event, item)"
v-html="item.title != undefined ? item.title : ''" />
</div>
<!-- Actions -->
<div
v-if="item.current_user_can_edit && !$adminOptions.hideItemsListActionAreas"
class="actions-area"
:label="$i18n.get('label_actions')">
<a
v-if="!isOnTrash"
id="button-edit"
:aria-label="$i18n.getFrom('items','edit_item')"
@click.prevent.stop="goToItemEditPage(item)">
<span
<span
v-if="isOnAllItemsTabs && $statusHelper.hasIcon(item.status)"
class="icon has-text-gray"
v-tooltip="{
content: $i18n.get('edit'),
content: $i18n.get('status_' + item.status),
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : ''],
placement: 'auto-start'
}">
<i
class="tainacan-icon tainacan-icon-1em"
:class="$statusHelper.getIcon(item.status)"
/>
</span>
<p
v-tooltip="{
delay: {
shown: 500,
hide: 300,
},
content: item.metadata != undefined ? renderMetadata(item.metadata, column) : '',
html: true,
autoHide: false,
placement: 'auto-start',
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : '']
}"
class="icon">
<i class="has-text-secondary tainacan-icon tainacan-icon-1-25em tainacan-icon-edit"/>
</span>
</a>
<a
:aria-lavel="$i18n.get('label_button_untrash')"
@click.prevent.stop="untrashOneItem(item.id)"
v-if="isOnTrash">
<span
v-for="(column, columnIndex) in displayedMetadata"
:key="columnIndex"
v-if="collectionId != undefined && column.display && column.metadata_type_object != undefined && (column.metadata_type_object.related_mapped_prop == 'title')"
@click.left="onClickItem($event, item)"
@click.right="onRightClickItem($event, item)"
v-html="item.metadata != undefined ? renderMetadata(item.metadata, column) : ''" />
<p
v-tooltip="{
content: $i18n.get('label_recover_from_trash'),
autoHide: true,
placement: 'auto',
delay: {
shown: 500,
hide: 300,
},
content: item.title != undefined ? item.title : '',
html: true,
autoHide: false,
placement: 'auto-start',
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : '']
}"
class="icon">
<i class="has-text-secondary tainacan-icon tainacan-icon-1-25em tainacan-icon-undo"/>
</span>
</a>
<a
v-if="item.current_user_can_delete"
id="button-delete"
:aria-label="$i18n.get('label_button_delete')"
@click.prevent.stop="deleteOneItem(item.id)">
<span
v-tooltip="{
content: isOnTrash ? $i18n.get('label_delete_permanently') : $i18n.get('delete'),
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : '']
}"
class="icon">
<i
:class="{ 'tainacan-icon-delete': !isOnTrash, 'tainacan-icon-deleteforever': isOnTrash }"
class="has-text-secondary tainacan-icon tainacan-icon-1-25em"/>
</span>
</a>
</div>
v-for="(column, columnIndex) in displayedMetadata"
:key="columnIndex"
v-if="collectionId == undefined && column.display && column.metadata_type_object != undefined && (column.metadata_type_object.related_mapped_prop == 'title')"
@click.left="onClickItem($event, item)"
@click.right="onRightClickItem($event, item)"
v-html="item.title != undefined ? item.title : ''" />
</div>
<!-- Actions -->
<div
v-if="item.current_user_can_edit && !$adminOptions.hideItemsListActionAreas"
class="actions-area"
:label="$i18n.get('label_actions')">
<a
v-if="!isOnTrash"
id="button-edit"
:aria-label="$i18n.getFrom('items','edit_item')"
@click.prevent.stop="goToItemEditPage(item)">
<span
v-tooltip="{
content: $i18n.get('edit'),
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : '']
}"
class="icon">
<i class="has-text-secondary tainacan-icon tainacan-icon-1-25em tainacan-icon-edit"/>
</span>
</a>
<a
:aria-lavel="$i18n.get('label_button_untrash')"
@click.prevent.stop="untrashOneItem(item.id)"
v-if="isOnTrash">
<span
v-tooltip="{
content: $i18n.get('label_recover_from_trash'),
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : '']
}"
class="icon">
<i class="has-text-secondary tainacan-icon tainacan-icon-1-25em tainacan-icon-undo"/>
</span>
</a>
<a
v-if="item.current_user_can_delete"
id="button-delete"
:aria-label="$i18n.get('label_button_delete')"
@click.prevent.stop="deleteOneItem(item.id)">
<span
v-tooltip="{
content: isOnTrash ? $i18n.get('label_delete_permanently') : $i18n.get('delete'),
autoHide: true,
placement: 'auto',
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : '']
}"
class="icon">
<i
:class="{ 'tainacan-icon-delete': !isOnTrash, 'tainacan-icon-deleteforever': isOnTrash }"
class="has-text-secondary tainacan-icon tainacan-icon-1-25em"/>
</span>
</a>
</div>
<!-- Remaining metadata -->
<div
class="media"
@click.left="onClickItem($event, item)"
@click.right="onRightClickItem($event, item)">
<div class="list-metadata media-body">
<div class="tainacan-record-thumbnail">
<blur-hash-image
@click.left="onClickItem($event, item)"
@click.right="onRightClickItem($event, item)"
v-if="item.thumbnail != undefined"
class="tainacan-record-item-thumbnail"
:width="$thumbHelper.getWidth(item['thumbnail'], 'tainacan-medium-full', 120)"
:height="$thumbHelper.getHeight(item['thumbnail'], 'tainacan-medium-full', 120)"
:hash="$thumbHelper.getBlurhashString(item['thumbnail'], 'tainacan-medium-full')"
:src="$thumbHelper.getSrc(item['thumbnail'], 'tainacan-medium-full', item.document_mimetype)"
:srcset="$thumbHelper.getSrcSet(item['thumbnail'], 'tainacan-medium-full', item.document_mimetype)"
:alt="item.thumbnail_alt ? item.thumbnail_alt : $i18n.get('label_thumbnail')"
:transition-duration="500"
/>
<!-- Remaining metadata -->
<div
class="media"
@click.left="onClickItem($event, item)"
@click.right="onRightClickItem($event, item)">
<div class="list-metadata media-body">
<div class="tainacan-record-thumbnail">
<blur-hash-image
@click.left="onClickItem($event, item)"
@click.right="onRightClickItem($event, item)"
v-if="item.thumbnail != undefined"
class="tainacan-record-item-thumbnail"
:width="$thumbHelper.getWidth(item['thumbnail'], 'tainacan-medium-full', 120)"
:height="$thumbHelper.getHeight(item['thumbnail'], 'tainacan-medium-full', 120)"
:hash="$thumbHelper.getBlurhashString(item['thumbnail'], 'tainacan-medium-full')"
:src="$thumbHelper.getSrc(item['thumbnail'], 'tainacan-medium-full', item.document_mimetype)"
:srcset="$thumbHelper.getSrcSet(item['thumbnail'], 'tainacan-medium-full', item.document_mimetype)"
:alt="item.thumbnail_alt ? item.thumbnail_alt : $i18n.get('label_thumbnail')"
:transition-duration="500"
/>
</div>
<span
v-for="(column, metadatumIndex) in displayedMetadata"
:key="metadatumIndex"
:class="{ 'metadata-type-textarea': column.metadata_type_object != undefined && column.metadata_type_object.component == 'tainacan-textarea' }"
v-if="collectionId == undefined && column.display && column.metadata_type_object != undefined && (column.metadata_type_object.related_mapped_prop == 'description')">
<h3 class="metadata-label">{{ $i18n.get('label_description') }}</h3>
<p
v-html="item.description != undefined ? item.description : ''"
class="metadata-value"/>
</span>
<span
v-for="(column, metadatumIndex) in displayedMetadata"
:key="metadatumIndex"
:class="{ 'metadata-type-textarea': column.metadata_type_object != undefined && column.metadata_type_object.component == 'tainacan-textarea' }"
v-if="renderMetadata(item.metadata, column) != '' && column.display && column.slug != 'thumbnail' && column.metadata_type_object != undefined && (column.metadata_type_object.related_mapped_prop != 'title')">
<h3 class="metadata-label">{{ column.name }}</h3>
<p
v-html="renderMetadata(item.metadata, column)"
class="metadata-value"/>
</span>
<span
v-for="(column, metadatumIndex) in displayedMetadata"
:key="metadatumIndex"
v-if="(column.metadatum == 'row_modification' || column.metadatum == 'row_creation' || column.metadatum == 'row_author') && item[column.slug] != undefined">
<h3 class="metadata-label">{{ column.name }}</h3>
<p
v-html="(column.metadatum == 'row_creation' || column.metadatum == 'row_modification') ? parseDateToNavigatorLanguage(item[column.slug]) : item[column.slug]"
class="metadata-value"/>
</span>
</div>
<span
v-for="(column, metadatumIndex) in displayedMetadata"
:key="metadatumIndex"
:class="{ 'metadata-type-textarea': column.metadata_type_object != undefined && column.metadata_type_object.component == 'tainacan-textarea' }"
v-if="collectionId == undefined && column.display && column.metadata_type_object != undefined && (column.metadata_type_object.related_mapped_prop == 'description')">
<h3 class="metadata-label">{{ $i18n.get('label_description') }}</h3>
<p
v-html="item.description != undefined ? item.description : ''"
class="metadata-value"/>
</span>
<span
v-for="(column, metadatumIndex) in displayedMetadata"
:key="metadatumIndex"
:class="{ 'metadata-type-textarea': column.metadata_type_object != undefined && column.metadata_type_object.component == 'tainacan-textarea' }"
v-if="renderMetadata(item.metadata, column) != '' && column.display && column.slug != 'thumbnail' && column.metadata_type_object != undefined && (column.metadata_type_object.related_mapped_prop != 'title')">
<h3 class="metadata-label">{{ column.name }}</h3>
<p
v-html="renderMetadata(item.metadata, column)"
class="metadata-value"/>
</span>
<span
v-for="(column, metadatumIndex) in displayedMetadata"
:key="metadatumIndex"
v-if="(column.metadatum == 'row_modification' || column.metadatum == 'row_creation' || column.metadatum == 'row_author') && item[column.slug] != undefined">
<h3 class="metadata-label">{{ column.name }}</h3>
<p
v-html="(column.metadatum == 'row_creation' || column.metadatum == 'row_modification') ? parseDateToNavigatorLanguage(item[column.slug]) : item[column.slug]"
class="metadata-value"/>
</span>
</div>
</div>
</div>
</masonry>
</li>
</ul>
<!-- TABLE VIEW MODE -->
<table
@ -893,6 +891,8 @@
class="column-default-width"
:class="{
'thumbnail-cell': column.metadatum == 'row_thumbnail',
'column-needed-width column-align-right' : column.metadata_type_object != undefined ? (column.metadata_type_object.primitive_type == 'float' ||
column.metadata_type_object.primitive_type == 'int' ) : false,
'column-small-width' : column.metadata_type_object != undefined ? (column.metadata_type_object.primitive_type == 'date' ||
column.metadata_type_object.primitive_type == 'float' ||
column.metadata_type_object.primitive_type == 'int') : false,
@ -1351,6 +1351,7 @@ import { mapActions, mapGetters } from 'vuex';
import CustomDialog from '../other/custom-dialog.vue';
import ItemCopyDialog from '../other/item-copy-dialog.vue';
import BulkEditionModal from '../modals/bulk-edition-modal.vue';
import Masonry from 'masonry-layout';
import { dateInter } from "../../js/mixins";
export default {
@ -1373,7 +1374,9 @@ export default {
cursorPosX: -1,
cursorPosY: -1,
contextMenuItem: null,
singleItemSelection: false
singleItemSelection: false,
masonry: false,
shouldUseLegacyMasonyCols: false
}
},
computed: {
@ -1438,12 +1441,35 @@ export default {
singleItemSelection() {
if (this.$adminOptions.itemsSingleSelectionMode)
this.$eventBusSearch.setSelectedItemsForIframe([this.singleItemSelection], true);
},
isLoading: {
handler() {
if (this.items && this.items.length > 0) {
this.$nextTick(() => {
if (this.masonry !== false)
this.masonry.destroy();
if (this.viewMode == 'masonry' || this.viewMode == 'records') {
this.masonry = new Masonry( '.tainacan-' + this.viewMode + '-container', {
itemSelector: 'li',
columnWidth: '.tainacan-' + this.viewMode + '-grid-sizer',
gutter: this.viewMode == 'masonry' ? 25 : 30,
percentPosition: true
});
}
});
}
},
immediate: true
}
},
mounted() {
if (this.highlightsItem)
setTimeout(() => this.$eventBusSearch.highlightsItem(null), 3000);
},
created() {
this.shouldUseLegacyMasonyCols = wp.hooks.hasFilter('tainacan_use_legacy_masonry_view_mode_cols') && wp.hooks.applyFilters('tainacan_use_legacy_masonry_view_mode_cols', false);
},
methods: {
...mapActions('collection', [
'deleteItem',
@ -1701,6 +1727,10 @@ export default {
this.clearContextMenu();
},
onClickItem($event, item) {
if ($event && $event.target && ($event.target.className == 'check' || $event.target.tagName == 'INPUT') )
return;
if ($event.ctrlKey) {
this.setSelectedItemChecked(item.id);
} else if ($event.shiftKey) {

View File

@ -1,8 +1,14 @@
<template>
<div>
<div class="metadata-mappers-area">
<b-loading
:can-cancel="false"
:is-full-page="false"
:active.sync="isLoadingMetadatumMappers"/>
<b-loading
:can-cancel="false"
:is-full-page="false"
:active.sync="isLoadingMetadata"/>
<b-field>
<p style="line-height: 2em;">{{ $i18n.get('info_metadata_mapper_helper') }}</p>
<b-select
@ -129,7 +135,7 @@
<div
v-if="mapper != '' && !isLoadingMetadatumMappers"
class="field is-grouped form-submit">
class="field is-grouped form-submit fixed-form-submit">
<div class="control">
<button
class="button is-outlined"
@ -139,6 +145,7 @@
<div class="control">
<button
@click.prevent="onUpdateMetadataMapperMetadataClick"
:class="{ 'is-loading': isMapperMetadataLoading }"
class="button is-success">{{ $i18n.get('save') }}</button>
</div>
</div>
@ -209,6 +216,7 @@ export default {
return {
collectionId: '',
isLoadingMetadatumMappers: true,
isLoadingMetadata: false,
mapper: '',
mapperMetadata: [],
isMapperMetadataLoading: false,
@ -231,20 +239,36 @@ export default {
}
},
mounted() {
this.isLoadingMetadatumMappers = true;
this.fetchMetadatumMappers()
.then(() => {
this.isLoadingMetadatumMappers = false;
if (this.metadatumMappers.length == 1)
this.onSelectMetadataMapper(this.metadatumMappers[0])
/* If we're in a collection list, the metadata won't exist as they are read inside sections */
if (!this.isRepositoryLevel) {
this.collectionId = this.$route.params.collectionId;
this.isLoadingMetadata = true;
this.cleanMetadata();
this.fetchMetadata({
collectionId: this.collectionId,
isRepositoryLevel: false,
isContextEdit: true,
includeDisabled: true,
includeOptionsAsHtml: false
})
.catch(() => {
this.isLoadingMetadatumMappers = false;
});
.then(() => {
this.loadMetadataMappers();
this.isLoadingMetadata = false;
})
.catch(() => {
this.isLoadingMetadata = false;
});
} else {
this.loadMetadataMappers();
}
},
methods: {
...mapActions('metadata', [
'fetchMetadata',
'cleanMetadata',
'fetchMetadatumMappers',
'updateMetadataMapperMetadata',
]),
@ -252,12 +276,25 @@ export default {
'getMetadatumMappers',
'getMetadata'
]),
loadMetadataMappers() {
this.isLoadingMetadatumMappers = true;
this.fetchMetadatumMappers()
.then(() => {
this.isLoadingMetadatumMappers = false;
if (this.metadatumMappers.length >= 1)
this.onSelectMetadataMapper(this.metadatumMappers[0])
})
.catch(() => {
this.isLoadingMetadatumMappers = false;
});
},
onSelectMetadataMapper(metadatumMapper) {
this.isMapperMetadataLoading = true;
this.mapper = metadatumMapper; //TODO try to use v-model again
this.mapperMetadata = [];
if (metadatumMapper != '') {
for (var k in metadatumMapper.metadata) {
var item = metadatumMapper.metadata[k];
@ -442,6 +479,10 @@ export default {
<style lang="scss" scoped>
.metadata-mappers-area {
padding: 0 1em;
}
.tainacan-form {
display: flex;
flex-direction: column;
@ -546,7 +587,7 @@ export default {
}
}
.form-submit {
.fixed-form-submit {
margin-top: 24px;
position: sticky !important;
bottom: 0;

View File

@ -0,0 +1,410 @@
<template>
<div
v-if="(isRepositoryLevel && $userCaps.hasCapability('tnc_rep_edit_metadata')) || !isRepositoryLevel"
class="column available-metadata-types-area" >
<b-loading :active.sync="isLoadingMetadataTypes"/>
<div class="field">
<h3 class="label">{{ $i18n.get('label_available_metadata_types') }}</h3>
<draggable
v-model="availableMetadatumList"
:sort="false"
:group="{ name:'metadata', pull: 'clone', put: false, revertClone: true }"
drag-class="sortable-drag">
<div
:id="metadatum.component"
@click.prevent="addMetadatumViaButton(metadatum)"
class="available-metadatum-item"
:class="{ 'hightlighted-metadatum' : hightlightedMetadatum == metadatum.name, 'inherited-metadatum': metadatum.inherited || isRepositoryLevel }"
v-for="(metadatum, index) in availableMetadatumList"
:key="index">
<span
v-tooltip="{
content: $i18n.get('instruction_click_or_drag_metadatum_create'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : ''],
placement: 'auto-start'
}"
class="icon grip-icon">
<!-- <i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-drag"/> -->
<svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 0 24 24"
width="24px"
fill="currentColor">
<path
d="M0 0h24v24H0V0z"
fill="transparent"/>
<path d="M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
</svg>
</span>
<span class="metadatum-name">
{{ metadatum.name }}
<span
v-tooltip="{
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : '', 'metadata-type-preview-tooltip'],
content: getPreviewTemplateContent(metadatum),
html: true,
delay: {
shown: 0,
hide: 100,
},
placement: 'top',
}"
class="icon preview-help-icon has-text-secondary">
<i class="tainacan-icon tainacan-icon-help"/>
</span>
</span>
<span
class="loading-spinner"
v-if="hightlightedMetadatum == metadatum.name"/>
</div>
</draggable>
<draggable
v-if="!isRepositoryLevel"
v-model="availableMetadataSectionsList"
:sort="false"
:group="{ name:'metadata-sections', pull: 'clone', put: false, revertClone: true }"
drag-class="sortable-drag">
<div
:id="metadataSection.id"
@click.prevent="addMetadataSectionViaButton()"
class="available-metadata-section-item"
v-for="(metadataSection, index) in availableMetadataSectionsList"
:key="index">
<span
v-tooltip="{
content: $i18n.get('instruction_click_or_drag_metadatum_create'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : ''],
placement: 'auto-start'
}"
class="icon grip-icon">
<!-- <i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-drag"/> -->
<svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 0 24 24"
width="24px"
fill="currentColor">
<path
d="M0 0h24v24H0V0z"
fill="transparent"/>
<path d="M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
</svg>
</span>
<span class="metadatum-name">
{{ metadataSection.label }}
<span
v-tooltip="{
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : '', 'metadata-type-preview-tooltip'],
content: $i18n.get('info_create_section'),
html: true,
delay: {
shown: 0,
hide: 100,
},
placement: 'top',
}"
class="icon preview-help-icon has-text-secondary">
<i class="tainacan-icon tainacan-icon-help"/>
</span>
</span>
</div>
</draggable>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
name: 'MetadataTypesList',
props: {
isRepositoryLevel: Boolean,
hightlightedMetadatum: String
},
data() {
return {
isLoadingMetadataTypes: true,
availableMetadataSectionsList: [{
label: this.$i18n.get('label_add_new_section'),
name: this.$i18n.get('label_new_metadata_section'),
id: 'metadataSectionCreator'
}],
}
},
computed: {
availableMetadatumList: {
get() {
return this.getMetadatumTypes();
},
set(value) {
return this.updateMetadatumTypes(value);
}
},
},
mounted() {
this.isLoadingMetadataTypes = true;
this.fetchMetadatumTypes()
.then(() => {
this.$emit('onFinishedLoadingMetadataTypes');
this.isLoadingMetadataTypes = false;
})
.catch(() => {
this.isLoadingMetadataTypes = false;
});
},
methods: {
...mapActions('metadata', [
'fetchMetadatumTypes'
]),
...mapGetters('metadata',[
'getMetadatumTypes'
]),
addMetadatumViaButton(metadatumType) {
this.$eventBusMetadataList.onAddMetadatumViaButton(metadatumType);
},
addMetadataSectionViaButton() {
this.$eventBusMetadataList.onAddMetadataSectionViaButton();
},
getPreviewTemplateContent(metadatum) {
return `<div class="metadata-type-preview tainacan-form">
<span class="metadata-type-label">` + this.$i18n.get('label_metadatum_type_preview') + `</span>
<div class="field">
<span class="collapse-handle">
<span class="icon">
<i class="has-text-secondary tainacan-icon tainacan-icon-1-25em tainacan-icon-arrowdown"></i>
</span>
<label class="label has-tooltip">`
+ metadatum.name +
`</label>
</span>
<div>` + metadatum.preview_template + `</div>
</div>
</div>`;
},
}
}
</script>
<style lang="scss">
.available-metadata-types-area {
padding: 10px 0px 10px 10px !important;
margin: 0;
max-width: 380px;
min-width: 20.8333333%;
max-height: calc(100vh - 7.75em);
top: -28px;
position: sticky;
font-size: 0.875em;
@media screen and (max-width: 769px) {
max-width: 100%;
padding: 10px;
h3 {
margin: 1em 0em 1em 0em !important;
}
.available-metadatum-item::before,
.available-metadatum-item::after,
.available-metadata-section-item::before,
.available-metadata-section-item::after {
display: none !important;
}
}
h3 {
margin: 0.875em 0em 1em 0em;
}
.available-metadatum-item,
.available-metadata-section-item {
padding: 0.6em;
margin: 4px 4px 4px 1.2em;
background-color: var(--tainacan-white);
cursor: pointer;
left: 0;
height: 2.8571em;
position: relative;
border: 1px solid var(--tainacan-gray2);
border-radius: 1px;
transition: left 0.2s ease;
.grip-icon {
color: var(--tainacan-gray3);
position: relative;
}
.icon {
position: relative;
bottom: 1px;
}
.preview-help-icon {
position: absolute;
top: 6px;
}
.metadatum-name {
text-overflow: ellipsis;
overflow-x: hidden;
white-space: nowrap;
font-weight: bold;
margin-left: 0.4em;
display: inline-block;
max-width: 180px;
width: 60%;
}
&::after,
&::before {
content: '';
display: block;
position: absolute;
right: 100%;
width: 0;
height: 0;
border-style: solid;
}
&::after {
top: -1px;
border-color: transparent white transparent transparent;
border-right-width: 16px;
border-top-width: 1.4286em;
border-bottom-width: 1.4286em;
left: -19px;
}
&::before {
top: -1px;
border-color: transparent var(--tainacan-gray2) transparent transparent;
border-right-width: 16px;
border-top-width: 1.4286em;
border-bottom-width: 1.4286em;
left: -20px;
}
}
.available-metadata-section-item {
margin-top: 2em;
color: var(--tainacan-secondary);
border-color: var(--tainacan-secondary);
&::before {
border-color: transparent var(--tainacan-secondary) transparent transparent;
}
}
.sortable-drag {
opacity: 1 !important;
}
.sortable-chosen {
.metadata-type-preview {
display: none;
}
}
@keyframes hightlighten {
0% {
color: #222;
background-color: var(--tainacan-white);
border-color: var(--tainacan-white);
}
25% {
color: var(--tainacan-white);
background-color: var(--tainacan-secondary);
border-color: var(--tainacan-secondary);
}
75% {
color: var(--tainacan-white);
background-color: var(--tainacan-secondary);
border-color: var(--tainacan-secondary);
}
100% {
color: #222;
background-color: var(--tainacan-white);
border-color: var(--tainacan-white);
}
}
@keyframes hightlighten-icon {
0% { color: var(--tainacan-gray3); }
25% { color: var(--tainacan-white); }
75% { color: var(--tainacan-white); }
100% { color: var(--tainacan-gray3); }
}
@keyframes hightlighten-arrow {
0% {
border-color: transparent white transparent transparent;
border-color: transparent white transparent transparent;
}
25% {
border-color: transparent var(--tainacan-secondary) transparent transparent;
border-color: transparent var(--tainacan-secondary) transparent transparent;
}
75% {
border-color: transparent var(--tainacan-secondary) transparent transparent;
border-color: transparent var(--tainacan-secondary)transparent transparent;
}
100% {
border-color: transparent white transparent transparent;
border-color: transparent white transparent transparent;
}
}
.hightlighted-metadatum {
background-color: var(--tainacan-white);
position: relative;
left: 0px;
animation-name: hightlighten;
animation-duration: 1.0s;
animation-iteration-count: 2;
.grip-icon{
animation-name: hightlighten-icon;
animation-duration: 1.0s;
animation-iteration-count: 2;
}
&::before,
&::after {
animation-name: hightlighten-arrow;
animation-duration: 1.0s;
animation-iteration-count: 2;
}
}
.available-metadatum-item:hover,
.available-metadata-section-item:hover {
background-color: var(--tainacan-turquoise1);
border-color: var(--tainacan-turquoise2);
position: relative;
left: -4px;
&:after {
border-color: transparent var(--tainacan-turquoise1) transparent transparent;
}
&:before {
border-color: transparent var(--tainacan-turquoise2) transparent transparent;
}
.grip-icon {
color: var(--tainacan-secondary);
}
}
}
.inherited-metadatum {
&.available-metadatum-item:hover,
&.available-metadata-section-item:hover {
background-color: var(--tainacan-blue1) !important;
border-color: var(--tainacan-blue2) !important;
.grip-icon {
color: var(--tainacan-blue5) !important;
}
&:after {
border-color: transparent var(--tainacan-blue1) transparent transparent !important;
}
&:before {
border-color: transparent var(--tainacan-blue2) transparent transparent !important;
}
}
}
</style>

View File

@ -250,7 +250,7 @@
.related-item-group {
&:not(:last-child) {
border-bottom: 1px dashed var(--tainacan-info-color);
border-bottom: 1px dashed var(--tainacan-gray3);
margin-bottom: 2rem;
}

View File

@ -0,0 +1,508 @@
<template>
<div class="column">
<b-loading :active.sync="isLoadingMetadata"/>
<div class="tainacan-form sub-header">
<!-- <h3>{{ $i18n.get('metadata') }}<span class="has-text-gray">{{ ( activeMetadatumList && activeMetadatumList.length ? (' (' + activeMetadatumList.length + ')') : '' ) }}</span></h3> -->
<template v-if="activeMetadatumList && !isLoadingMetadata">
<button
aria-controls="filters-items-list"
:aria-expanded="!collapseAll"
v-if="activeMetadatumList.length > 0"
class="link-style collapse-all"
@click="collapseAll = !collapseAll">
<span class="icon">
<i
:class="{ 'tainacan-icon-arrowdown' : collapseAll, 'tainacan-icon-arrowright' : !collapseAll }"
class="has-text-secondary tainacan-icon tainacan-icon-1-125em"/>
</span>
<span class="collapse-all__text">
{{ collapseAll ? $i18n.get('label_show_less_details') : $i18n.get('label_show_more_details') }}
</span>
</button>
<b-field class="header-item">
<b-dropdown
:mobile-modal="true"
:disabled="activeMetadatumList.length <= 0"
class="show metadata-options-dropdown"
aria-role="list"
trap-focus>
<button
:aria-label="$i18n.get('label_filter_by_metadata_type')"
class="button is-white"
slot="trigger">
<span>{{ $i18n.get('label_filter_by_metadata_type') }}</span>
<span class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-arrowdown"/>
</span>
</button>
<div class="metadata-options-container">
<b-dropdown-item
v-for="(metadataType, index) in metadataTypeFilterOptions"
:key="index"
class="control"
custom
aria-role="listitem">
<b-checkbox
v-model="metadataType.enabled"
:native-value="metadataType.enabled">
{{ metadataType.name }}
</b-checkbox>
</b-dropdown-item>
</div>
</b-dropdown>
</b-field>
<b-field class="header-item">
<b-input
:placeholder="$i18n.get('instruction_type_search_metadata_filter')"
v-model="metadataNameFilterString"
icon="magnify"
size="is-small"
icon-right="close-circle"
icon-right-clickable
@icon-right-click="metadataNameFilterString = ''" />
</b-field>
</template>
</div>
<section
v-if="activeMetadatumList.length <= 0 && !isLoadingMetadata"
class="field is-grouped-centered section">
<div class="content has-text-gray has-text-centered">
<p>
<span class="icon is-large">
<i class="tainacan-icon tainacan-icon-36px tainacan-icon-metadata"/>
</span>
</p>
<p>{{ $i18n.get('info_there_is_no_metadatum' ) }}</p>
<p>{{ $i18n.get('info_create_metadata' ) }}</p>
</div>
</section>
<!-- The Repository Metadata list -->
<div class="active-metadata-sections-area">
<draggable
v-model="activeMetadatumList"
class="active-metadata-area"
@change="handleChange($event)"
:group="{ name:'metadata', pull: false, put: true }"
:sort="false"
:handle="'.handle'"
ghost-class="sortable-ghost"
chosen-class="sortable-chosen"
filter=".not-sortable-item"
:prevent-on-filter="false"
:animation="250">
<div
v-for="(metadatum, index) in activeMetadatumList.filter((meta) => meta != undefined && meta.parent == 0)"
:key="metadatum.id"
v-show="(metadataNameFilterString == '' || filterByMetadatumName(metadatum)) && filterByMetadatumType(metadatum)">
<div
class="active-metadatum-item"
:class="{
'is-compact-item': !isCollapseOpen(metadatum.id),
'not-sortable-item': true,
'not-focusable-item': openedMetadatumId == metadatum.id,
'disabled-metadatum': metadatum.enabled == false,
'inherited-metadatum': true,
'child-metadatum': metadatum.parent > 0
}">
<div
:ref="'metadatum-handler-' + metadatum.id"
class="handle">
<span
:style="{ opacity: '0.0' }"
v-tooltip="{
content: $i18n.get('info_not_allowed_change_order_metadata'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}"
class="icon grip-icon">
<svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 0 24 24"
width="24px"
fill="currentColor">
<path
d="M0 0h24v24H0V0z"
fill="transparent"/>
<path d="M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
</svg>
</span>
<span
v-tooltip="{
content: $i18n.get('label_view_metadata_details'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}"
@click="$set(collapses, metadatum.id, !isCollapseOpen(metadatum.id))"
class="gray-icon icon"
:style="{ cursor: 'pointer', opacity: openedMetadatumId != metadatum.id ? '1.0' : '0.0' }">
<i :class="'tainacan-icon tainacan-icon-1-25em tainacan-icon-' + (isCollapseOpen(metadatum.id) ? 'arrowdown' : 'arrowright')" />
</span>
<span class="metadatum-name">
{{ metadatum.name }}
</span>
<span
v-if="metadatum.id != undefined && metadatum.metadata_type_object"
class="label-details"
:class="{ 'has-text-weight-bold': metadatum.metadata_type_object.core }">
<span
v-if="metadatum.required === 'yes'"
v-tooltip="{
content: $i18n.get('label_required'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}">
*&nbsp;
</span>
({{ metadatum.metadata_type_object.name }})
<span
v-if="metadatum.status === 'private'"
class="icon"
v-tooltip="{
content: $i18n.get('status_private'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}">
<i class="tainacan-icon tainacan-icon-private"/>
</span>
<span
v-tooltip="{
content: $i18n.get('label_repository_metadatum'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}"
class="icon icon-level-identifier">
<i
:class="{
'has-text-blue5': metadatum.enabled,
'has-text-gray3': !metadatum.enabled
}"
class="tainacan-icon tainacan-icon-repository" />
</span>
</span>
<span
class="loading-spinner"
v-if="metadatum.id == undefined"/>
<span
class="controls"
v-if="metadatum.id !== undefined">
<a
v-if="metadatum.current_user_can_edit"
:style="{ visibility:
metadatum.collection_id != collectionId
? 'hidden' : 'visible'
}"
@click.prevent="toggleMetadatumEdition(metadatum)">
<span
v-tooltip="{
content: $i18n.get('edit'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-edit"/>
</span>
</a>
<a
v-if="metadatum.current_user_can_delete"
:style="{ visibility: metadatum.collection_id != collectionId || metadatum.metadata_type_object.core ? 'hidden' : 'visible' }"
@click.prevent="removeMetadatum(metadatum)">
<span
v-tooltip="{
content: $i18n.get('delete'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip', 'tainacan-repository-tooltip'],
placement: 'auto-start'
}"
class="icon">
<i class="tainacan-icon tainacan-icon-1-25em tainacan-icon-delete"/>
</span>
</a>
</span>
</div>
<transition name="form-collapse">
<metadatum-details
v-if="isCollapseOpen(metadatum.id) && openedMetadatumId !== metadatum.id"
:metadatum="metadatum" />
</transition>
</div>
<!-- Child metadata list, inside each compound metadata -->
<child-metadata-list
v-if="metadatum.metadata_type_object && metadatum.metadata_type_object.component == 'tainacan-compound'"
:parent="metadatum"
:metadata-name-filter-string="metadataNameFilterString"
:metadata-type-filter-options="metadataTypeFilterOptions"
:has-some-metadata-type-filter-applied="hasSomeMetadataTypeFilterApplied"
:is-parent-multiple="metadatum.multiple == 'yes'"
:is-repository-level="true"
:collapse-all="collapseAll" />
<!-- Metadata edition form, for each metadata -->
<b-modal
@close="onEditionCanceled()"
:active="openedMetadatumId == metadatum.id"
trap-focus
aria-modal
aria-role="dialog"
custom-class="tainacan-modal"
:close-button-aria-label="$i18n.get('close')">
<metadatum-edition-form
:collection-id="collectionId"
:original-metadatum="metadatum"
:is-repository-level="true"
@onEditionFinished="onEditionFinished()"
@onEditionCanceled="onEditionCanceled()"
:index="index" />
</b-modal>
</div>
</draggable><!-- End of .active-metadata-area -->
</div>
</div> <!-- End of .columns -->
</template>
<script>
import MetadatumEditionForm from '../edition/metadatum-edition-form.vue';
import MetadatumDetails from '../other/metadatum-details.vue';
import ChildMetadataList from '../metadata-types/compound/child-metadata-list.vue';
import CustomDialog from '../other/custom-dialog.vue';
import { mapGetters, mapActions } from 'vuex';
export default {
name: 'RepositoryMetadataList',
components: {
MetadatumEditionForm,
ChildMetadataList,
MetadatumDetails
},
props: {
metadataTypeFilterOptions: Array
},
data() {
return {
collectionId: 'default',
isLoadingMetadata: false,
openedMetadatumId: '',
hightlightedMetadatum: '',
collapses: {},
collapseAll: false,
metadataNameFilterString: '',
metadataSearchCancel: undefined
}
},
computed: {
hasSomeMetadataTypeFilterApplied() {
return this.metadataTypeFilterOptions.length && this.metadataTypeFilterOptions.some((metadatumType) => metadatumType.enabled);
},
activeMetadatumList: {
get() {
return this.getMetadata();
},
set(value) {
this.updateMetadata(value);
}
},
},
watch: {
'$route.query': {
handler(newQuery) {
if (newQuery.edit != undefined) {
let existingMetadataIndex = this.activeMetadatumList.findIndex((metadatum) => metadatum && (metadatum.id == newQuery.edit));
if (existingMetadataIndex >= 0)
this.editMetadatum(this.activeMetadatumList[existingMetadataIndex])
}
},
immediate: true
},
collapseAll(isCollapsed) {
this.activeMetadatumList.forEach((metadatum) => this.$set(this.collapses, metadatum.id, isCollapsed));
}
},
mounted() {
this.cleanMetadata();
this.loadMetadata();
this.$eventBusMetadataList.$on('addMetadatumViaButton', this.addMetadatumViaButton);
},
beforeDestroy() {
// Cancels previous Request
if (this.metadataSearchCancel != undefined)
this.metadataSearchCancel.cancel('Metadata search Canceled.');
},
methods: {
...mapActions('metadata', [
'fetchMetadata',
'sendMetadatum',
'deleteMetadatum',
'updateMetadata',
'cleanMetadata'
]),
...mapGetters('metadata',[
'getMetadata'
]),
handleChange(event) {
if (event.added)
this.addNewMetadatum(event.added.element, event.added.newIndex);
else if (event.removed)
this.removeMetadatum(event.removed.element);
},
addMetadatumViaButton(metadatumType) {
let lastIndex = this.activeMetadatumList.length;
this.addNewMetadatum(metadatumType, lastIndex);
// Higlights the clicked metadatum
this.hightlightedMetadatum = metadatumType.name;
this.$emit('onUpdatehightlightedMetadatum', this.hightlightedMetadatum);
},
addNewMetadatum(newMetadatum, newIndex) {
this.sendMetadatum({
collectionId: this.collectionId,
name: newMetadatum.name,
metadatumType: newMetadatum.className,
status: 'auto-draft',
isRepositoryLevel: true,
newIndex: newIndex,
parent: '0'
})
.then((metadatum) => {
this.toggleMetadatumEdition(metadatum);
this.hightlightedMetadatum = '';
this.$emit('onUpdatehightlightedMetadatum', this.hightlightedMetadatum);
})
.catch((error) => {
this.$console.error(error);
});
},
removeMetadatum(removedMetadatum) {
this.$buefy.modal.open({
parent: this,
component: CustomDialog,
props: {
icon: 'alert',
title: this.$i18n.get('label_warning'),
message: this.$i18n.get('info_warning_metadatum_delete'),
onConfirm: () => {
this.deleteMetadatum({
collectionId: this.collectionId,
metadatumId: removedMetadatum.id,
isRepositoryLevel: true
})
.catch(() => {
this.$console.log("Error deleting metadatum.")
});
}
},
trapFocus: true,
customClass: 'tainacan-modal',
closeButtonAriaLabel: this.$i18n.get('close')
});
},
toggleMetadatumEdition(metadatum) {
this.$router.push({ query: { edit: metadatum.id } });
},
editMetadatum(metadatum) {
this.openedMetadatumId = metadatum.id;
},
onEditionFinished() {
this.openedMetadatumId = '';
this.$router.push({ query: {}});
},
onEditionCanceled() {
this.openedMetadatumId = '';
this.$router.push({ query: {}});
},
loadMetadata() {
this.isLoadingMetadata = true;
// Cancels previous Request
if (this.metadataSearchCancel != undefined)
this.metadataSearchCancel.cancel('Metadata search Canceled.');
this.fetchMetadata({
collectionId: this.collectionId,
isRepositoryLevel: true,
isContextEdit: true,
includeDisabled: true,
parent: '0',
includeOptionsAsHtml: true
}).then((resp) => {
resp.request
.then(() => {
this.isLoadingMetadata = false;
// Checks URL as router watcher would not wait for list to load
if (this.$route.query.edit != undefined) {
let existingMetadataIndex = this.activeMetadatumList.findIndex((metadatum) => metadatum.id == this.$route.query.edit);
if (existingMetadataIndex >= 0)
this.editMetadatum(this.activeMetadatumList[existingMetadataIndex]);
}
})
.catch(() => {
this.isLoadingMetadata = false;
});
// Search Request Token for cancelling
this.metadataSearchCancel = resp.source;
})
.catch(() => this.isLoadingMetadata = false);
},
filterByMetadatumName(metadatum) {
if (metadatum.metadata_type_object &&
metadatum.metadata_type_object.component == 'tainacan-compound' &&
metadatum.metadata_type_options &&
metadatum.metadata_type_options.children_objects &&
metadatum.metadata_type_options.children_objects.length
) {
let childNamesArray = metadatum.metadata_type_options.children_objects.map((children) => children.name);
childNamesArray.push(metadatum.name);
return childNamesArray.some((childName) => childName.toString().toLowerCase().indexOf(this.metadataNameFilterString.toString().toLowerCase()) >= 0);
}
else
return metadatum.name.toString().toLowerCase().indexOf(this.metadataNameFilterString.toString().toLowerCase()) >= 0;
},
filterByMetadatumType(metadatum) {
if (!this.hasSomeMetadataTypeFilterApplied)
return true;
if (metadatum.metadata_type_object &&
metadatum.metadata_type_object.component == 'tainacan-compound' &&
metadatum.metadata_type_options &&
metadatum.metadata_type_options.children_objects &&
metadatum.metadata_type_options.children_objects.length
) {
let childTypesArray = metadatum.metadata_type_options.children_objects.map((children) => children.metadata_type);
childTypesArray.push(metadatum.metadata_type);
for (let metadatumType of this.metadataTypeFilterOptions) {
if (metadatumType.enabled && childTypesArray.some((childType) => childType == metadatumType.type))
return true;
}
} else {
for (let metadatumType of this.metadataTypeFilterOptions) {
if (metadatumType.enabled && metadatum.metadata_type == metadatumType.type)
return true;
}
}
return false;
},
isCollapseOpen(metadatumId) {
return this.collapses[metadatumId] == true;
}
}
}
</script>

View File

@ -377,6 +377,12 @@
else if ((this.focusedChildMetadatum - 1) >= 0)
this.setMetadatumChildFocus({ groupIndex: this.focusedGroupMetadatum, childIndex: this.focusedChildMetadatum - 1, scrollIntoView: true })
}
// This keeps the navigation going on when no child input exists
if (this.childItemMetadataGroups.length === 0) {
eventBusItemMetadata.$emit('isOnFirstMetadatumOfCompoundNavigation', true);
eventBusItemMetadata.$emit('isOnLastMetadatumOfCompoundNavigation', true);
}
},
focusNextChildMetadatum() {

View File

@ -42,6 +42,48 @@
<div
:ref="'metadatum-handler-' + metadatum.id"
class="handle">
<span class="sorting-buttons">
<button
:disabled="index == 0"
class="link-button"
@click="moveMetadatumUpViaButton(index)"
:aria-label="$i18n.get('label_move_up')">
<span class="icon">
<i class="tainacan-icon tainacan-icon-previous tainacan-icon-rotate-90" />
</span>
</button>
<button
:disabled="index == childrenMetadata.length - 1"
class="link-button"
@click="moveMetadatumDownViaButton(index)"
:aria-label="$i18n.get('label_move_down')">
<span class="icon">
<i class="tainacan-icon tainacan-icon-next tainacan-icon-rotate-90" />
</span>
</button>
</span>
<span
:style="{ opacity: !(metadatum.id == undefined || openedMetadatumId != '' || isUpdatingMetadataOrder || metadatum.parent == 0 || metadatum.collection_id != collectionId || metadataNameFilterString != '' || hasSomeMetadataTypeFilterApplied) ? '1.0' : '0.0' }"
v-tooltip="{
content: metadatum.id == undefined || openedMetadatumId != '' || isUpdatingMetadataOrder ? $i18n.get('info_not_allowed_change_order_metadata') : $i18n.get('instruction_drag_and_drop_metadatum_sort'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : ''],
placement: 'auto-start'
}"
class="icon grip-icon">
<!-- <i class="tainacan-icon tainacan-icon-18px tainacan-icon-drag"/> -->
<svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 0 24 24"
width="24px"
fill="currentColor">
<path
d="M0 0h24v24H0V0z"
fill="transparent"/>
<path d="M11 18c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zm-2-8c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0-6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm6 4c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2z"/>
</svg>
</span>
<span
v-tooltip="{
content: $i18n.get('label_view_metadata_details'),
@ -54,26 +96,24 @@
:style="{ cursor: 'pointer', opacity: openedMetadatumId != metadatum.id ? '1.0' : '0.0' }">
<i :class="'tainacan-icon tainacan-icon-1-25em tainacan-icon-' + (isCollapseOpen(metadatum.id) ? 'arrowdown' : 'arrowright')" />
</span>
<span
:style="{ opacity: !(metadatum.id == undefined || openedMetadatumId != '' || isUpdatingMetadataOrder || metadatum.parent == 0 || metadatum.collection_id != collectionId || metadataNameFilterString != '' || hasSomeMetadataTypeFilterApplied) ? '1.0' : '0.0' }"
v-tooltip="{
content: metadatum.id == undefined || openedMetadatumId != '' || isUpdatingMetadataOrder ? $i18n.get('info_not_allowed_change_order_metadata') : $i18n.get('instruction_drag_and_drop_metadatum_sort'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : ''],
placement: 'auto-start'
}"
class="icon grip-icon">
<i class="tainacan-icon tainacan-icon-18px tainacan-icon-drag"/>
</span>
<span class="metadatum-name">
{{ metadatum.name }}
</span>
<span
v-if="metadatum.id != undefined && metadatum.metadata_type_object"
class="label-details">
class="label-details">
<span
v-if="metadatum.required === 'yes'"
v-tooltip="{
content: $i18n.get('label_required'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : ''],
placement: 'auto-start'
}">
*&nbsp;
</span>
({{ metadatum.metadata_type_object.name }})
<em v-if="metadatum.collection_id != collectionId">{{ $i18n.get('label_inherited') }}</em>
<!-- <em v-if="metadatum.collection_id != collectionId">{{ $i18n.get('label_inherited') }}</em> -->
<em
v-if="metadatum.metadata_type_object.core &&
metadatum.metadata_type_object.related_mapped_prop == 'title'">
@ -95,16 +135,6 @@
}">
<i class="tainacan-icon tainacan-icon-private"/>
</span>
<span
v-if="metadatum.required === 'yes'"
v-tooltip="{
content: $i18n.get('label_required'),
autoHide: true,
popperClass: ['tainacan-tooltip', 'tooltip', isRepositoryLevel ? 'tainacan-repository-tooltip' : ''],
placement: 'auto-start'
}">
*&nbsp;
</span>
<span
v-tooltip="{
content: (metadatum.collection_id == 'default') || isRepositoryLevel ? $i18n.get('label_repository_metadatum') : $i18n.get('label_collection_metadatum'),
@ -115,7 +145,7 @@
class="icon icon-level-identifier">
<i
:class="{
'tainacan-icon-collections': (metadatum.collection_id != 'default' && !isRepositoryLevel),
'tainacan-icon-collection': (metadatum.collection_id != 'default' && !isRepositoryLevel),
'tainacan-icon-repository': (metadatum.collection_id == 'default') || isRepositoryLevel,
'has-text-turquoise5': (metadatum.collection_id != 'default' && !isRepositoryLevel),
'has-text-blue5': (metadatum.collection_id == 'default' || isRepositoryLevel),
@ -224,7 +254,8 @@
collapseAll: {
type: Boolean,
default: new Boolean()
}
},
sectionId: String
},
data() {
return {
@ -284,13 +315,12 @@
'updateChildMetadataOrder'
]),
handleChange(event) {
if (event.added) {
if (event.added)
this.addNewMetadatum(event.added.element, event.added.newIndex);
} else if (event.removed) {
else if (event.removed)
this.removeMetadatum(event.removed.element);
} else if (event.moved) {
else if (event.moved)
this.updateMetadataOrder();
}
},
updateMetadataOrder() {
let metadataOrder = [];
@ -316,7 +346,8 @@
status: 'auto-draft',
isRepositoryLevel: this.isRepositoryLevel,
newIndex: newIndex,
parent: this.parent.id
parent: this.parent.id,
sectionId: this.sectionId ? this.sectionId : false
})
.then((metadatum) => {
this.updateMetadataOrder();
@ -377,6 +408,14 @@
this.openedMetadatumId = '';
this.$router.push({ query: {}});
},
moveMetadatumUpViaButton(index) {
this.childrenMetadata.splice(index - 1, 0, this.childrenMetadata.splice(index, 1)[0]);
this.updateMetadataOrder();
},
moveMetadatumDownViaButton(index) {
this.childrenMetadata.splice(index + 1, 0, this.childrenMetadata.splice(index, 1)[0]);
this.updateMetadataOrder();
},
isAvailableChildMetadata(to, from, item) {
return !['tainacan-compound', 'tainacan-taxonomy'].includes(item.id);
},

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