Plugin Directory

Changeset 3322139


Ignore:
Timestamp:
07/04/2025 07:24:49 AM (5 months ago)
Author:
PerS
Message:

Update to version 2.2.0 from GitHub

Location:
jobbnorge-block
Files:
32 added
49 edited
1 copied

Legend:

Unmodified
Added
Removed
  • jobbnorge-block/assets/blueprints/blueprint.json

    r3019435 r3322139  
    11{
    22    "$schema": "https://playground.wordpress.net/blueprint-schema.json",
    3     "landingPage": "\/wp-admin\/post.php?post=2&action=edit",
     3    "_comment1": "This is the landing page URL, Also see the runPHP step below",
     4    "landingPage": "/wp-admin/post.php?post=2&action=edit",
     5    "_comment2": "These are the preferred versions for PHP and WordPress",
    46    "preferredVersions": {
    57        "php": "8.1",
    68        "wp": "latest"
    79    },
     10    "_comment3": "These are the PHP extension bundles",
    811    "phpExtensionBundles": [
     12        "_comment3.1": "kitchen-sink = gd, mbstring, iconv, openssl, libxml, xml, dom, simplexml, xmlreader and xmlwriter",
    913        "kitchen-sink"
    1014    ],
     15    "_comment4": "These are the features",
    1116    "features": {
     17        "_comment4.1": "Network access is required. The plugin needs access to the Jobbnorge API",
    1218        "networking": true
    1319    },
     20    "_comment5": "These are the steps",
    1421    "steps": [
    1522        {
     23            "_comment5.1": "This is the login step",
    1624            "step": "login",
    1725            "username": "admin",
     
    1927        },
    2028        {
     29            "_comment5.2": "This is the plugin installation step",
    2130            "step": "installPlugin",
    2231            "pluginZipFile": {
    23                 "resource": "wordpress.org\/plugins",
     32                "resource": "wordpress.org/plugins",
    2433                "slug": "jobbnorge-block"
    2534            },
     
    2938        },
    3039        {
     40            "_comment5.3": "Use wp_update_post. post ID 2 exists (default sample page), I updated it with my content and made it my landing page",
    3141            "step": "runPHP",
    3242            "code": "<?php require '/wordpress/wp-load.php'; wp_update_post( array('ID' => 2, 'post_title'    => 'Jobbnorge','post_content'  => '<!-- wp:dss/jobbnorge {\"employerID\":\"1981,1992,1980,2770,1989,1994,1986,1984,1985,1987,1996,1988,1982,1983,1995,1993,1991\"} /-->' ) );"
  • jobbnorge-block/tags/2.2.0/CHANGELOG.md

    r3003492 r3322139  
    1 # Cangelog
     1# Changelog
     2
     3## 2.2.0
     4
     5* **NEW**: Add frontend pagination support with AJAX loading
     6* **NEW**: Add pagination controls (enable/disable, jobs per page setting)
     7* **ENHANCEMENT**: Upgrade to Jobbnorge API v3 for better performance
     8* **ENHANCEMENT**: Implement PHP-based pagination to work around API limitations with employer filtering
     9* **ENHANCEMENT**: Add responsive grid layout that adapts to screen size
     10* **ENHANCEMENT**: Improve cache key logic to include pagination and layout parameters
     11* **ENHANCEMENT**: Add loading states and error handling for pagination
     12* **ENHANCEMENT**: Separate frontend and admin CSS loading for better performance
     13* **FIX**: Fix CSS class naming conflicts that prevented grid view from working on frontend
     14* **FIX**: Resolve frontend style loading issues
     15* **DEVELOPER**: Add comprehensive webpack build configuration for multiple entry points
     16* **DEVELOPER**: Add pagination JavaScript with proper AJAX handling and nonce security
    217
    318## 2.1.5
  • jobbnorge-block/tags/2.2.0/README.md

    r3002720 r3322139  
    55> Also available at https://wordpress.org/plugins/jobbnorge-block/
    66
     7
    78This WordPress plugin adds a block to the Gutenberg editor that displays a list of jobs from Jobbnorge.
    89
    9 See: [Features](#features) | [Installation](#installation) | [Use](#use) | [Filters](#filters) | [Styling](#styling) | [License](#license) | [Changelog](CHANGELOG.md)
     10**See:** [Live Preview](https://playground.wordpress.net/?plugin=jobbnorge-block&blueprint-url=https://wordpress.org/plugins/wp-json/plugins/v1/plugin/jobbnorge-block/blueprint.json) | [Features](#features) | [Installation](#installation) | [Use](#use) | [Filters](#filters) | [Styling](#styling) | [License](#license) | [Changelog](CHANGELOG.md)
    1011
     12<p>&nbsp;</p>
    1113
    1214<img src=".wordpress-org/jobbnorge.gif">
  • jobbnorge-block/tags/2.2.0/build/block.json

    r3003492 r3322139  
    33  "apiVersion": 2,
    44  "name": "dss/jobbnorge",
    5   "version": "2.1.5",
     5  "version": "2.2.0",
    66  "title": "Jobbnorge",
    77  "category": "widgets",
     
    6363      "type": "number",
    6464      "default": 55
     65    },
     66    "enablePagination": {
     67      "type": "boolean",
     68      "default": true
     69    },
     70    "jobsPerPage": {
     71      "type": "number",
     72      "default": 10
    6573    }
    6674  },
    6775  "textdomain": "wp-jobbnorge-block",
    6876  "editorScript": "file:init.js",
    69   "editorStyle": "file:editor.scss",
    70   "style": "file:style.scss"
     77  "editorStyle": "file:editor.css",
     78  "style": "file:style-init.css"
    7179}
  • jobbnorge-block/tags/2.2.0/build/init.asset.php

    r3003492 r3322139  
    1 <?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-server-side-render'), 'version' => '4d2818abc3875b8f27f4');
     1<?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-server-side-render'), 'version' => '7c82361092d9d86c438b');
  • jobbnorge-block/tags/2.2.0/build/init.css

    r2853882 r3322139  
    1 .wp-block-dss-jobbnorge li a>div{display:inline}.wp-block-dss-jobbnorge__placeholder-form{align-items:stretch;display:flex}.wp-block-dss-jobbnorge__placeholder-form>*{margin-bottom:8px}@media(min-width:782px){.wp-block-dss-jobbnorge__placeholder-form>*{margin-bottom:0}}.wp-block-dss-jobbnorge__placeholder-input{align-items:stretch;display:flex;flex-grow:1}.wp-block-dss-jobbnorge__placeholder-input .components-base-control__field{align-items:stretch;display:flex;flex-grow:1;margin:0 8px 0 0}
     1.wp-block-dss-jobbnorge li a>div{display:inline}.wp-block-dss-jobbnorge__placeholder-form{align-items:stretch;display:flex}.wp-block-dss-jobbnorge__placeholder-form>*{margin-bottom:8px}@media(min-width:782px){.wp-block-dss-jobbnorge__placeholder-form>*{margin-bottom:0}}.wp-block-dss-jobbnorge__placeholder-input{align-items:stretch;display:flex;flex-grow:1}.wp-block-dss-jobbnorge__placeholder-input .components-base-control__field{align-items:stretch;display:flex;flex-grow:1;margin:0 8px 0 0}ul.wp-block-dss-jobbnorge{list-style:none;padding:0}ul.wp-block-dss-jobbnorge.wp-block-dss-jobbnorge{box-sizing:border-box}ul.wp-block-dss-jobbnorge li{margin:0 0 1em}ul.wp-block-dss-jobbnorge.is-grid{display:flex;flex-wrap:wrap;list-style:none;padding:0}ul.wp-block-dss-jobbnorge.is-grid li{margin:0 1em 1em 0;width:100%}@media(min-width:600px){ul.wp-block-dss-jobbnorge.columns-2 li{width:calc(50% - 1em)}ul.wp-block-dss-jobbnorge.columns-3 li{width:calc(33.33333% - 1em)}ul.wp-block-dss-jobbnorge.columns-4 li{width:calc(25% - 1em)}ul.wp-block-dss-jobbnorge.columns-5 li{width:calc(20% - 1em)}ul.wp-block-dss-jobbnorge.columns-6 li{width:calc(16.66667% - 1em)}}
  • jobbnorge-block/tags/2.2.0/build/init.js

    r3003492 r3322139  
    1 !function(){"use strict";var e,o={938:function(e,o,t){var l=window.React,n=window.wp.primitives,r=(0,l.createElement)(n.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,l.createElement)(n.Path,{d:"M15.5 9.5a1 1 0 100-2 1 1 0 000 2zm0 1.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zm-2.25 6v-2a2.75 2.75 0 00-2.75-2.75h-4A2.75 2.75 0 003.75 15v2h1.5v-2c0-.69.56-1.25 1.25-1.25h4c.69 0 1.25.56 1.25 1.25v2h1.5zm7-2v2h-1.5v-2c0-.69-.56-1.25-1.25-1.25H15v-1.5h2.5A2.75 2.75 0 0120.25 15zM9.5 8.5a1 1 0 11-2 0 1 1 0 012 0zm1.5 0a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z",fillRule:"evenodd"})),a=window.wp.blocks,i=JSON.parse('{"$schema":"https://schemas.wp.org/trunk/block.json","apiVersion":2,"name":"dss/jobbnorge","version":"2.1.5","title":"Jobbnorge","category":"widgets","icon":"people","description":"Retrieve and display job listings from Jobbnorge.no","keywords":["jobbnorge","jobbnorge.no"],"supports":{"html":false},"attributes":{"columns":{"type":"number","default":3},"blockLayout":{"type":"string","default":"list"},"employerID":{"type":"string","default":""},"noJobsMessage":{"type":"string","default":""},"orderBy":{"type":"string","default":"Deadline"},"itemsToShow":{"type":"number","default":5},"displayEmployer":{"type":"boolean","default":false},"displayExcerpt":{"type":"boolean","default":true},"displayDeadline":{"type":"boolean","default":false},"displayScope":{"type":"boolean","default":false},"displayDate":{"type":"boolean","default":true},"excerptLength":{"type":"number","default":55}},"textdomain":"wp-jobbnorge-block","editorScript":"file:init.js","editorStyle":"file:editor.scss","style":"file:style.scss"}'),c=window.wp.blockEditor,s=window.wp.components,b=window.wp.element,p=(0,l.createElement)(n.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,l.createElement)(n.Path,{d:"m19 7-3-3-8.5 8.5-1 4 4-1L19 7Zm-7 11.5H5V20h7v-1.5Z"})),d=(0,l.createElement)(n.SVG,{viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},(0,l.createElement)(n.Path,{d:"M4 4v1.5h16V4H4zm8 8.5h8V11h-8v1.5zM4 20h16v-1.5H4V20zm4-8c0-1.1-.9-2-2-2s-2 .9-2 2 .9 2 2 2 2-.9 2-2z"})),m=(0,l.createElement)(n.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,l.createElement)(n.Path,{d:"m3 5c0-1.10457.89543-2 2-2h13.5c1.1046 0 2 .89543 2 2v13.5c0 1.1046-.8954 2-2 2h-13.5c-1.10457 0-2-.8954-2-2zm2-.5h6v6.5h-6.5v-6c0-.27614.22386-.5.5-.5zm-.5 8v6c0 .2761.22386.5.5.5h6v-6.5zm8 0v6.5h6c.2761 0 .5-.2239.5-.5v-6zm0-8v6.5h6.5v-6c0-.27614-.2239-.5-.5-.5z",fillRule:"evenodd",clipRule:"evenodd"})),u=window.wp.i18n,g=window.wp.serverSideRender,w=t.n(g);const{name:v}=i;(e=>{const{metadata:o,settings:t,name:l}=e;(0,a.registerBlockType)({name:l,...o},t)})({name:v,metadata:i,settings:{icon:r,example:{attributes:{employerID:"123[, 456, 789]"}},edit:function({attributes:e,setAttributes:o}){const[t,n]=(0,b.useState)(!e.employerID),{blockLayout:a,columns:i,displayScope:g,displayDate:v,displayEmployer:y,displayExcerpt:h,employerID:f,itemsToShow:k,noJobsMessage:_,orderBy:E}=e;function j(t){return()=>{const l=e[t];o({[t]:!l})}}const C=(0,c.useBlockProps)();var x;if(t)return(0,l.createElement)("div",{...C},(0,l.createElement)(s.Placeholder,{icon:r,label:"Jobbnorge"},(0,l.createElement)("form",{onSubmit:function(e){e.preventDefault(),f&&(o({employerID:f}),n(!1))},className:"wp-block-dss-jobbnorge__placeholder-form"},window.wpJobbnorgeBlock&&window.wpJobbnorgeBlock.employers?(0,l.createElement)(s.SelectControl,{multiple:!0,value:f.split(","),onChange:e=>o({employerID:e.toString()}),options:(null!==(x=wpJobbnorgeBlock.employers)&&void 0!==x?x:[]).map((e=>{var o;return{label:e.label,value:e.value,disabled:null!==(o=e?.disabled)&&void 0!==o&&o}})),className:"wp-block-dss-jobbnorge__placeholder-input",help:(0,u.__)("Select employers to display. Ctrl-click (Windows) or Cmd-click (Mac) to select multiple employers. Shift-click to select a range of employers.","wp-jobbnorge-block"),__nextHasNoMarginBottom:!0}):(0,l.createElement)(s.TextControl,{placeholder:(0,u.__)("Employer ID [,id2, id3, ..]","wp-jobbnorge-block"),value:f,onChange:e=>o({employerID:e}),className:"wp-block-dss-jobbnorge__placeholder-input"}),(0,l.createElement)(s.Button,{variant:"primary",type:"submit"},(0,u.__)("Save","wp-jobbnorge-block")))));const S=[{icon:p,title:(0,u.__)("Edit Jobbnorge URL","wp-jobbnorge-block"),onClick:()=>n(!0)},{icon:d,title:(0,u.__)("List view","wp-jobbnorge-block"),onClick:()=>o({blockLayout:"list"}),isActive:"list"===a},{icon:m,title:(0,u.__)("Grid view","wp-jobbnorge-block"),onClick:()=>o({blockLayout:"grid"}),isActive:"grid"===a}];return(0,l.createElement)(l.Fragment,null,(0,l.createElement)(c.BlockControls,null,(0,l.createElement)(s.ToolbarGroup,{controls:S})),(0,l.createElement)(c.InspectorControls,null,(0,l.createElement)(s.PanelBody,{title:(0,u.__)("Settings","wp-jobbnorge-block")},(0,l.createElement)(s.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,u.__)("Number of items","wp-jobbnorge-block"),value:k,onChange:e=>o({itemsToShow:e}),min:1,max:100,required:!0}),f.includes(",")&&(0,l.createElement)(s.RadioControl,{label:(0,u.__)("Order by","wp-jobbnorge-block"),selected:E,options:[{label:(0,u.__)("Deadline","wp-jobbnorge-block"),value:"Deadline"},{label:(0,u.__)("Employer","wp-jobbnorge-block"),value:"Employer"}],onChange:e=>o({orderBy:e})}),(0,l.createElement)(s.TextareaControl,{label:(0,u.__)("No jobs found message","wp-jobbnorge-block"),help:(0,u.__)("Message to display if no jobs are found","wp-jobbnorge-block"),value:_||(0,u.__)("There are no jobs at this time.","wp-jobbnorge-block"),onChange:e=>o({noJobsMessage:e})})),(0,l.createElement)(s.PanelBody,{title:(0,u.__)("Item","wp-jobbnorge-block")},(0,l.createElement)(s.ToggleControl,{label:(0,u.__)("Display employer","wp-jobbnorge-block"),checked:y,onChange:j("displayEmployer")}),(0,l.createElement)(s.ToggleControl,{label:(0,u.__)("Display excerpt","wp-jobbnorge-block"),checked:h,onChange:j("displayExcerpt")}),(0,l.createElement)(s.ToggleControl,{label:(0,u.__)("Display deadline","wp-jobbnorge-block"),checked:v,onChange:j("displayDate")}),(0,l.createElement)(s.ToggleControl,{label:(0,u.__)("Display scope","wp-jobbnorge-block"),checked:g,onChange:j("displayScope")})),"grid"===a&&(0,l.createElement)(s.PanelBody,{title:(0,u.__)("Grid view","wp-jobbnorge-block")},(0,l.createElement)(s.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,u.__)("Columns","wp-jobbnorge-block"),value:i,onChange:e=>o({columns:e}),min:2,max:6,required:!0}))),(0,l.createElement)("div",{...C},(0,l.createElement)(s.Disabled,null,(0,l.createElement)(w(),{block:"dss/jobbnorge",attributes:e,httpMethod:"POST"}))))}}})}},t={};function l(e){var n=t[e];if(void 0!==n)return n.exports;var r=t[e]={exports:{}};return o[e](r,r.exports,l),r.exports}l.m=o,e=[],l.O=function(o,t,n,r){if(!t){var a=1/0;for(b=0;b<e.length;b++){t=e[b][0],n=e[b][1],r=e[b][2];for(var i=!0,c=0;c<t.length;c++)(!1&r||a>=r)&&Object.keys(l.O).every((function(e){return l.O[e](t[c])}))?t.splice(c--,1):(i=!1,r<a&&(a=r));if(i){e.splice(b--,1);var s=n();void 0!==s&&(o=s)}}return o}r=r||0;for(var b=e.length;b>0&&e[b-1][2]>r;b--)e[b]=e[b-1];e[b]=[t,n,r]},l.n=function(e){var o=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(o,{a:o}),o},l.d=function(e,o){for(var t in o)l.o(o,t)&&!l.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:o[t]})},l.o=function(e,o){return Object.prototype.hasOwnProperty.call(e,o)},function(){var e={410:0,308:0};l.O.j=function(o){return 0===e[o]};var o=function(o,t){var n,r,a=t[0],i=t[1],c=t[2],s=0;if(a.some((function(o){return 0!==e[o]}))){for(n in i)l.o(i,n)&&(l.m[n]=i[n]);if(c)var b=c(l)}for(o&&o(t);s<a.length;s++)r=a[s],l.o(e,r)&&e[r]&&e[r][0](),e[r]=0;return l.O(b)},t=self.webpackChunkjobbnorge_block=self.webpackChunkjobbnorge_block||[];t.forEach(o.bind(null,0)),t.push=o.bind(null,t.push.bind(t))}();var n=l.O(void 0,[308],(function(){return l(938)}));n=l.O(n)}();
     1!function(){"use strict";var e,o={938:function(e,o,t){var n=window.React,l=window.wp.primitives,r=(0,n.createElement)(l.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,n.createElement)(l.Path,{d:"M15.5 9.5a1 1 0 100-2 1 1 0 000 2zm0 1.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zm-2.25 6v-2a2.75 2.75 0 00-2.75-2.75h-4A2.75 2.75 0 003.75 15v2h1.5v-2c0-.69.56-1.25 1.25-1.25h4c.69 0 1.25.56 1.25 1.25v2h1.5zm7-2v2h-1.5v-2c0-.69-.56-1.25-1.25-1.25H15v-1.5h2.5A2.75 2.75 0 0120.25 15zM9.5 8.5a1 1 0 11-2 0 1 1 0 012 0zm1.5 0a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z",fillRule:"evenodd"})),a=window.wp.blocks,i=JSON.parse('{"$schema":"https://schemas.wp.org/trunk/block.json","apiVersion":2,"name":"dss/jobbnorge","version":"2.2.0","title":"Jobbnorge","category":"widgets","icon":"people","description":"Retrieve and display job listings from Jobbnorge.no","keywords":["jobbnorge","jobbnorge.no"],"supports":{"html":false},"attributes":{"columns":{"type":"number","default":3},"blockLayout":{"type":"string","default":"list"},"employerID":{"type":"string","default":""},"noJobsMessage":{"type":"string","default":""},"orderBy":{"type":"string","default":"Deadline"},"itemsToShow":{"type":"number","default":5},"displayEmployer":{"type":"boolean","default":false},"displayExcerpt":{"type":"boolean","default":true},"displayDeadline":{"type":"boolean","default":false},"displayScope":{"type":"boolean","default":false},"displayDate":{"type":"boolean","default":true},"excerptLength":{"type":"number","default":55},"enablePagination":{"type":"boolean","default":true},"jobsPerPage":{"type":"number","default":10}},"textdomain":"wp-jobbnorge-block","editorScript":"file:init.js","editorStyle":"file:editor.css","style":"file:style-init.css"}'),c=window.wp.blockEditor,b=window.wp.components,s=window.wp.element,p=(0,n.createElement)(l.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,n.createElement)(l.Path,{d:"m19 7-3-3-8.5 8.5-1 4 4-1L19 7Zm-7 11.5H5V20h7v-1.5Z"})),d=(0,n.createElement)(l.SVG,{viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},(0,n.createElement)(l.Path,{d:"M4 4v1.5h16V4H4zm8 8.5h8V11h-8v1.5zM4 20h16v-1.5H4V20zm4-8c0-1.1-.9-2-2-2s-2 .9-2 2 .9 2 2 2 2-.9 2-2z"})),m=(0,n.createElement)(l.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,n.createElement)(l.Path,{d:"m3 5c0-1.10457.89543-2 2-2h13.5c1.1046 0 2 .89543 2 2v13.5c0 1.1046-.8954 2-2 2h-13.5c-1.10457 0-2-.8954-2-2zm2-.5h6v6.5h-6.5v-6c0-.27614.22386-.5.5-.5zm-.5 8v6c0 .2761.22386.5.5.5h6v-6.5zm8 0v6.5h6c.2761 0 .5-.2239.5-.5v-6zm0-8v6.5h6.5v-6c0-.27614-.2239-.5-.5-.5z",fillRule:"evenodd",clipRule:"evenodd"})),u=window.wp.i18n,g=window.wp.serverSideRender,w=t.n(g);const{name:h}=i;(e=>{const{metadata:o,settings:t,name:n}=e;(0,a.registerBlockType)({name:n,...o},t)})({name:h,metadata:i,settings:{icon:r,example:{attributes:{employerID:"123[, 456, 789]"}},edit:function({attributes:e,setAttributes:o}){const[t,l]=(0,s.useState)(!e.employerID),{blockLayout:a,columns:i,displayScope:g,displayDate:h,displayEmployer:y,displayExcerpt:v,employerID:f,itemsToShow:_,noJobsMessage:k,orderBy:j,enablePagination:E,jobsPerPage:C}=e;function x(t){return()=>{const n=e[t];o({[t]:!n})}}const S=(0,c.useBlockProps)();var B;if(t)return(0,n.createElement)("div",{...S},(0,n.createElement)(b.Placeholder,{icon:r,label:"Jobbnorge"},(0,n.createElement)("form",{onSubmit:function(e){e.preventDefault(),f&&(o({employerID:f}),l(!1))},className:"wp-block-dss-jobbnorge__placeholder-form"},window.wpJobbnorgeBlock&&window.wpJobbnorgeBlock.employers?(0,n.createElement)(b.SelectControl,{multiple:!0,value:f.split(","),onChange:e=>o({employerID:e.toString()}),options:(null!==(B=wpJobbnorgeBlock.employers)&&void 0!==B?B:[]).map((e=>{var o;return{label:e.label,value:e.value,disabled:null!==(o=e?.disabled)&&void 0!==o&&o}})),className:"wp-block-dss-jobbnorge__placeholder-input",help:(0,u.__)("Select employers to display. Ctrl-click (Windows) or Cmd-click (Mac) to select multiple employers. Shift-click to select a range of employers.","wp-jobbnorge-block"),__nextHasNoMarginBottom:!0}):(0,n.createElement)(b.TextControl,{placeholder:(0,u.__)("Employer ID [,id2, id3, ..]","wp-jobbnorge-block"),value:f,onChange:e=>o({employerID:e}),className:"wp-block-dss-jobbnorge__placeholder-input"}),(0,n.createElement)(b.Button,{variant:"primary",type:"submit"},(0,u.__)("Save","wp-jobbnorge-block")))));const D=[{icon:p,title:(0,u.__)("Edit Jobbnorge URL","wp-jobbnorge-block"),onClick:()=>l(!0)},{icon:d,title:(0,u.__)("List view","wp-jobbnorge-block"),onClick:()=>o({blockLayout:"list"}),isActive:"list"===a},{icon:m,title:(0,u.__)("Grid view","wp-jobbnorge-block"),onClick:()=>o({blockLayout:"grid"}),isActive:"grid"===a}];return(0,n.createElement)(n.Fragment,null,(0,n.createElement)(c.BlockControls,null,(0,n.createElement)(b.ToolbarGroup,{controls:D})),(0,n.createElement)(c.InspectorControls,null,(0,n.createElement)(b.PanelBody,{title:(0,u.__)("Settings","wp-jobbnorge-block")},(0,n.createElement)(b.ToggleControl,{label:(0,u.__)("Enable pagination","wp-jobbnorge-block"),help:(0,u.__)("When enabled, all jobs will be displayed with pagination controls. When disabled, only the specified number of jobs will be shown.","wp-jobbnorge-block"),checked:E,onChange:e=>o({enablePagination:e})}),!E&&(0,n.createElement)(b.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,u.__)("Number of items","wp-jobbnorge-block"),value:_,onChange:e=>o({itemsToShow:e}),min:1,max:100,required:!0}),E&&(0,n.createElement)(b.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,u.__)("Jobs per page","wp-jobbnorge-block"),value:C,onChange:e=>o({jobsPerPage:e}),min:1,max:50,required:!0}),f.includes(",")&&(0,n.createElement)(b.RadioControl,{label:(0,u.__)("Order by","wp-jobbnorge-block"),selected:j,options:[{label:(0,u.__)("Deadline","wp-jobbnorge-block"),value:"Deadline"},{label:(0,u.__)("Employer","wp-jobbnorge-block"),value:"Employer"}],onChange:e=>o({orderBy:e})}),(0,n.createElement)(b.TextareaControl,{label:(0,u.__)("No jobs found message","wp-jobbnorge-block"),help:(0,u.__)("Message to display if no jobs are found","wp-jobbnorge-block"),value:k||(0,u.__)("There are no jobs at this time.","wp-jobbnorge-block"),onChange:e=>o({noJobsMessage:e})})),(0,n.createElement)(b.PanelBody,{title:(0,u.__)("Item","wp-jobbnorge-block")},(0,n.createElement)(b.ToggleControl,{label:(0,u.__)("Display employer","wp-jobbnorge-block"),checked:y,onChange:x("displayEmployer")}),(0,n.createElement)(b.ToggleControl,{label:(0,u.__)("Display excerpt","wp-jobbnorge-block"),checked:v,onChange:x("displayExcerpt")}),(0,n.createElement)(b.ToggleControl,{label:(0,u.__)("Display deadline","wp-jobbnorge-block"),checked:h,onChange:x("displayDate")}),(0,n.createElement)(b.ToggleControl,{label:(0,u.__)("Display scope","wp-jobbnorge-block"),checked:g,onChange:x("displayScope")})),"grid"===a&&(0,n.createElement)(b.PanelBody,{title:(0,u.__)("Grid view","wp-jobbnorge-block")},(0,n.createElement)(b.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,u.__)("Columns","wp-jobbnorge-block"),value:i,onChange:e=>o({columns:e}),min:2,max:6,required:!0}))),(0,n.createElement)("div",{...S},(0,n.createElement)(b.Disabled,null,(0,n.createElement)(w(),{block:"dss/jobbnorge",attributes:e,httpMethod:"POST"}))))}}})}},t={};function n(e){var l=t[e];if(void 0!==l)return l.exports;var r=t[e]={exports:{}};return o[e](r,r.exports,n),r.exports}n.m=o,e=[],n.O=function(o,t,l,r){if(!t){var a=1/0;for(s=0;s<e.length;s++){t=e[s][0],l=e[s][1],r=e[s][2];for(var i=!0,c=0;c<t.length;c++)(!1&r||a>=r)&&Object.keys(n.O).every((function(e){return n.O[e](t[c])}))?t.splice(c--,1):(i=!1,r<a&&(a=r));if(i){e.splice(s--,1);var b=l();void 0!==b&&(o=b)}}return o}r=r||0;for(var s=e.length;s>0&&e[s-1][2]>r;s--)e[s]=e[s-1];e[s]=[t,l,r]},n.n=function(e){var o=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(o,{a:o}),o},n.d=function(e,o){for(var t in o)n.o(o,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:o[t]})},n.o=function(e,o){return Object.prototype.hasOwnProperty.call(e,o)},function(){var e={410:0,308:0};n.O.j=function(o){return 0===e[o]};var o=function(o,t){var l,r,a=t[0],i=t[1],c=t[2],b=0;if(a.some((function(o){return 0!==e[o]}))){for(l in i)n.o(i,l)&&(n.m[l]=i[l]);if(c)var s=c(n)}for(o&&o(t);b<a.length;b++)r=a[b],n.o(e,r)&&e[r]&&e[r][0](),e[r]=0;return n.O(s)},t=self.webpackChunkjobbnorge_block=self.webpackChunkjobbnorge_block||[];t.forEach(o.bind(null,0)),t.push=o.bind(null,t.push.bind(t))}();var l=n.O(void 0,[308],(function(){return n(938)}));l=n.O(l)}();
  • jobbnorge-block/tags/2.2.0/build/style-init.css

    r2997962 r3322139  
    1 ul.wp-block-dss-jobbnorge{list-style:none;padding:0}ul.wp-block-dss-jobbnorge.wp-block-dss-jobbnorge{box-sizing:border-box}ul.wp-block-dss-jobbnorge.alignleft{margin-right:2em}ul.wp-block-dss-jobbnorge.alignright{margin-left:2em}ul.wp-block-dss-jobbnorge li{margin:0 0 1em}ul.wp-block-dss-jobbnorge.is-grid{display:flex;flex-wrap:wrap;list-style:none;padding:0}ul.wp-block-dss-jobbnorge.is-grid li{margin:0 1em 1em 0;width:100%}@media(min-width:600px){ul.wp-block-dss-jobbnorge.columns-2 li{width:calc(50% - 1em)}ul.wp-block-dss-jobbnorge.columns-3 li{width:calc(33.33333% - 1em)}ul.wp-block-dss-jobbnorge.columns-4 li{width:calc(25% - 1em)}ul.wp-block-dss-jobbnorge.columns-5 li{width:calc(20% - 1em)}ul.wp-block-dss-jobbnorge.columns-6 li{width:calc(16.66667% - 1em)}}.wp-block-dss-jobbnorge__item-title{font-size:1.125em;font-weight:600;margin:0 0 .25em}.wp-block-dss-jobbnorge__item-meta{margin:0 0 .25em;padding:0}.wp-block-dss-jobbnorge__item-deadline,.wp-block-dss-jobbnorge__item-employer,.wp-block-dss-jobbnorge__item-scope{display:block;font-size:.8125em;font-weight:600}
     1ul.wp-block-dss-jobbnorge{list-style:none;padding:0}ul.wp-block-dss-jobbnorge.wp-block-dss-jobbnorge{box-sizing:border-box}ul.wp-block-dss-jobbnorge.alignleft{margin-right:2em}ul.wp-block-dss-jobbnorge.alignright{margin-left:2em}ul.wp-block-dss-jobbnorge li{margin:0 0 1em}ul.wp-block-dss-jobbnorge.is-grid{display:flex;flex-wrap:wrap;list-style:none;padding:0}ul.wp-block-dss-jobbnorge.is-grid li{margin:0 1em 1em 0;width:100%}@media(min-width:600px){ul.wp-block-dss-jobbnorge.columns-2 li{width:calc(50% - 1em)}ul.wp-block-dss-jobbnorge.columns-3 li{width:calc(33.33333% - 1em)}ul.wp-block-dss-jobbnorge.columns-4 li{width:calc(25% - 1em)}ul.wp-block-dss-jobbnorge.columns-5 li{width:calc(20% - 1em)}ul.wp-block-dss-jobbnorge.columns-6 li{width:calc(16.66667% - 1em)}}.wp-block-dss-jobbnorge__item-title{font-size:1.125em;font-weight:600;margin:0 0 .25em}.wp-block-dss-jobbnorge__item-meta{margin:0 0 .25em;padding:0}.wp-block-dss-jobbnorge__item-deadline,.wp-block-dss-jobbnorge__item-employer,.wp-block-dss-jobbnorge__item-scope{display:block;font-size:.8125em;font-weight:600}.wp-block-dss-jobbnorge__pagination{border-top:1px solid #e0e0e0;display:flex;flex-direction:column;gap:1rem;margin-top:2rem;padding:1rem 0}@media(min-width:600px){.wp-block-dss-jobbnorge__pagination{align-items:center;flex-direction:row;justify-content:space-between}}.wp-block-dss-jobbnorge__pagination-info{color:#666;font-size:.875rem;margin:0}.wp-block-dss-jobbnorge__pagination-controls{align-items:center;display:flex;gap:.5rem}.wp-block-dss-jobbnorge__pagination-controls button{background:#fff;border:1px solid #ddd;border-radius:4px;cursor:pointer;font-size:.875rem;padding:.5rem 1rem;transition:all .2s ease}.wp-block-dss-jobbnorge__pagination-controls button:hover:not(:disabled){background:#f5f5f5;border-color:#999}.wp-block-dss-jobbnorge__pagination-controls button:disabled{cursor:not-allowed;opacity:.5}.wp-block-dss-jobbnorge__pagination-controls .wp-block-dss-jobbnorge__pagination-info{color:#333;font-size:.875rem;margin:0 .5rem}.wp-block-dss-jobbnorge__loading{opacity:.6;pointer-events:none}.wp-block-dss-jobbnorge__loading:after{animation:spin 1s linear infinite;border:2px solid #ccc;border-radius:50%;border-top-color:#333;content:"";height:20px;left:50%;margin:-10px 0 0 -10px;position:absolute;top:50%;width:20px}.wp-block-dss-jobbnorge__error{background:#ffebe8;border:1px solid #d63638;border-radius:4px;color:#d63638;margin:1rem 0;padding:.75rem}.wp-block-dss-jobbnorge__error p{margin:0}@keyframes spin{to{transform:rotate(1turn)}}
  • jobbnorge-block/tags/2.2.0/languages/wp-jobbnorge-block-en_US-5847f2cef22ef3cae0e9359a7a5dd2ec.json

    r3002720 r3322139  
    11{
    22    "translation-revision-date": "YEAR-MO-DA HO:MI+ZONE",
    3     "generator": "WP-CLI\/2.9.0",
     3    "generator": "WP-CLI\/2.12.0",
    44    "source": "build\/init.js",
    55    "domain": "messages",
     
    3535                ""
    3636            ],
     37            "Enable pagination": [
     38                ""
     39            ],
     40            "When enabled, all jobs will be displayed with pagination controls. When disabled, only the specified number of jobs will be shown.": [
     41                ""
     42            ],
    3743            "Number of items": [
     44                ""
     45            ],
     46            "Jobs per page": [
    3847                ""
    3948            ],
  • jobbnorge-block/tags/2.2.0/languages/wp-jobbnorge-block-en_US-bb1d7dea005e67527e728d4801f74b61.json

    r3002720 r3322139  
    11{
    22    "translation-revision-date": "YEAR-MO-DA HO:MI+ZONE",
    3     "generator": "WP-CLI\/2.9.0",
     3    "generator": "WP-CLI\/2.12.0",
    44    "source": "src\/edit.js",
    55    "domain": "messages",
     
    3535                ""
    3636            ],
     37            "Enable pagination": [
     38                ""
     39            ],
     40            "When enabled, all jobs will be displayed with pagination controls. When disabled, only the specified number of jobs will be shown.": [
     41                ""
     42            ],
    3743            "Number of items": [
     44                ""
     45            ],
     46            "Jobs per page": [
    3847                ""
    3948            ],
  • jobbnorge-block/tags/2.2.0/languages/wp-jobbnorge-block-en_US.po

    r3002720 r3322139  
    1 # Copyright (C) 2023 PerS
     1# Copyright (C) 2025 PerS
    22# This file is distributed under the GPL-2.0-or-later.
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: Jobbnorge Block 2.1.4\n"
     5"Project-Id-Version: Jobbnorge Block 2.2.0\n"
    66"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/jobbnorge-block\n"
    77"Last-Translator: Per Søderlind <[email protected]\n"
     
    1010"Content-Type: text/plain; charset=UTF-8\n"
    1111"Content-Transfer-Encoding: 8bit\n"
    12 "POT-Creation-Date: 2023-11-28T12:11:37+00:00\n"
     12"POT-Creation-Date: 2025-07-03T21:16:21+00:00\n"
    1313"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    14 "X-Generator: WP-CLI 2.9.0\n"
     14"X-Generator: WP-CLI 2.12.0\n"
    1515"X-Domain: wp-jobbnorge-block\n"
    1616
    1717#. Plugin Name of the plugin
     18#: wp-jobb-norge.php
    1819msgid "Jobbnorge Block"
    1920msgstr ""
    2021
    2122#. Plugin URI of the plugin
     23#: wp-jobb-norge.php
    2224msgid "https://wordpress.org/plugins/jobbnorge-block/"
    2325msgstr ""
    2426
    2527#. Description of the plugin
     28#: wp-jobb-norge.php
    2629msgid "Retrieve and display job listings from Jobbnorge.no"
    2730msgstr ""
    2831
    2932#. Author of the plugin
     33#: wp-jobb-norge.php
    3034msgid "PerS"
    3135msgstr ""
    3236
    33 #: wp-jobb-norge.php:159
     37#: wp-jobb-norge.php:181
    3438msgid "Invalid ID"
    3539msgstr ""
    3640
    37 #: wp-jobb-norge.php:181
     41#: wp-jobb-norge.php:212
    3842msgid "Error connecting to Jobbnorge.no"
    3943msgstr ""
    4044
    41 #: wp-jobb-norge.php:195
     45#: wp-jobb-norge.php:251
    4246msgid "No jobs found"
    4347msgstr ""
    4448
    45 #: wp-jobb-norge.php:205
     49#: wp-jobb-norge.php:261
    4650msgid "(no title)"
    4751msgstr ""
    4852
    49 #: wp-jobb-norge.php:226
    50 #: build/init.js:1
    51 #: src/edit.js:160
     53#: wp-jobb-norge.php:282
     54#: build/init.js:1
     55#: src/edit.js:181
    5256msgid "Employer"
    5357msgstr ""
    5458
    55 #: wp-jobb-norge.php:227
     59#: wp-jobb-norge.php:283
    5660msgid "Scope"
    5761msgstr ""
    5862
    59 #: wp-jobb-norge.php:282
     63#: wp-jobb-norge.php:345
    6064msgid "Read more"
    6165msgstr ""
    6266
    63 #: wp-jobb-norge.php:353
     67#: wp-jobb-norge.php:416
    6468msgid "Deadline:"
    6569msgstr ""
    6670
    67 #: build/init.js:1
    68 #: src/edit.js:95
     71#: wp-jobb-norge.php:556
     72msgid "Job listings pagination"
     73msgstr ""
     74
     75#: wp-jobb-norge.php:562
     76#, php-format
     77msgid "Showing %d-%d of %d jobs"
     78msgstr ""
     79
     80#: wp-jobb-norge.php:577
     81#: wp-jobb-norge.php:582
     82msgid "Previous"
     83msgstr ""
     84
     85#: wp-jobb-norge.php:590
     86#, php-format
     87msgid "Page %d of %d"
     88msgstr ""
     89
     90#: wp-jobb-norge.php:601
     91#: wp-jobb-norge.php:606
     92msgid "Next"
     93msgstr ""
     94
     95#: build/init.js:1
     96#: src/edit.js:97
    6997msgid "Select employers to display. Ctrl-click (Windows) or Cmd-click (Mac) to select multiple employers. Shift-click to select a range of employers."
    7098msgstr ""
    7199
    72100#: build/init.js:1
    73 #: src/edit.js:103
     101#: src/edit.js:105
    74102msgid "Employer ID [,id2, id3, ..]"
    75103msgstr ""
    76104
    77105#: build/init.js:1
    78 #: src/edit.js:110
     106#: src/edit.js:112
    79107msgid "Save"
    80108msgstr ""
    81109
    82110#: build/init.js:1
    83 #: src/edit.js:121
     111#: src/edit.js:123
    84112msgid "Edit Jobbnorge URL"
    85113msgstr ""
    86114
    87115#: build/init.js:1
    88 #: src/edit.js:126
     116#: src/edit.js:128
    89117msgid "List view"
    90118msgstr ""
    91119
    92120#: build/init.js:1
    93 #: src/edit.js:132
     121#: src/edit.js:134
     122#: src/edit.js:216
     123msgid "Grid view"
     124msgstr ""
     125
     126#: build/init.js:1
     127#: src/edit.js:146
     128msgid "Settings"
     129msgstr ""
     130
     131#: build/init.js:1
     132#: src/edit.js:148
     133msgid "Enable pagination"
     134msgstr ""
     135
     136#: build/init.js:1
     137#: src/edit.js:149
     138msgid "When enabled, all jobs will be displayed with pagination controls. When disabled, only the specified number of jobs will be shown."
     139msgstr ""
     140
     141#: build/init.js:1
     142#: src/edit.js:156
     143msgid "Number of items"
     144msgstr ""
     145
     146#: build/init.js:1
     147#: src/edit.js:167
     148msgid "Jobs per page"
     149msgstr ""
     150
     151#: build/init.js:1
     152#: src/edit.js:177
     153msgid "Order by"
     154msgstr ""
     155
     156#: build/init.js:1
     157#: src/edit.js:180
     158msgid "Deadline"
     159msgstr ""
     160
     161#: build/init.js:1
     162#: src/edit.js:187
     163msgid "No jobs found message"
     164msgstr ""
     165
     166#: build/init.js:1
     167#: src/edit.js:188
     168msgid "Message to display if no jobs are found"
     169msgstr ""
     170
     171#: build/init.js:1
     172#: src/edit.js:189
     173msgid "There are no jobs at this time."
     174msgstr ""
     175
     176#: build/init.js:1
     177#: src/edit.js:193
     178msgid "Item"
     179msgstr ""
     180
     181#: build/init.js:1
    94182#: src/edit.js:195
    95 msgid "Grid view"
    96 msgstr ""
    97 
    98 #: build/init.js:1
    99 #: src/edit.js:144
    100 msgid "Settings"
    101 msgstr ""
    102 
    103 #: build/init.js:1
    104 #: src/edit.js:147
    105 msgid "Number of items"
    106 msgstr ""
    107 
    108 #: build/init.js:1
    109 #: src/edit.js:156
    110 msgid "Order by"
    111 msgstr ""
    112 
    113 #: build/init.js:1
    114 #: src/edit.js:159
    115 msgid "Deadline"
    116 msgstr ""
    117 
    118 #: build/init.js:1
    119 #: src/edit.js:166
    120 msgid "No jobs found message"
    121 msgstr ""
    122 
    123 #: build/init.js:1
    124 #: src/edit.js:167
    125 msgid "Message to display if no jobs are found"
    126 msgstr ""
    127 
    128 #: build/init.js:1
    129 #: src/edit.js:168
    130 msgid "There are no jobs at this time."
    131 msgstr ""
    132 
    133 #: build/init.js:1
    134 #: src/edit.js:172
    135 msgid "Item"
    136 msgstr ""
    137 
    138 #: build/init.js:1
    139 #: src/edit.js:174
    140183msgid "Display employer"
    141184msgstr ""
    142185
    143186#: build/init.js:1
    144 #: src/edit.js:179
     187#: src/edit.js:200
    145188msgid "Display excerpt"
    146189msgstr ""
    147190
    148191#: build/init.js:1
    149 #: src/edit.js:184
     192#: src/edit.js:205
    150193msgid "Display deadline"
    151194msgstr ""
    152195
    153196#: build/init.js:1
    154 #: src/edit.js:189
     197#: src/edit.js:210
    155198msgid "Display scope"
    156199msgstr ""
    157200
    158201#: build/init.js:1
    159 #: src/edit.js:198
     202#: src/edit.js:219
    160203msgid "Columns"
    161204msgstr ""
  • jobbnorge-block/tags/2.2.0/languages/wp-jobbnorge-block-nb_NO-5847f2cef22ef3cae0e9359a7a5dd2ec.json

    r3002720 r3322139  
    11{
    2     "translation-revision-date": "2023-11-28 13:09+0100",
    3     "generator": "WP-CLI\/2.9.0",
     2    "translation-revision-date": "2025-07-03 23:16+0200",
     3    "generator": "WP-CLI\/2.12.0",
    44    "source": "build\/init.js",
    55    "domain": "messages",
     
    3535                "Innstillinger"
    3636            ],
     37            "Enable pagination": [
     38                "Aktiver paginering"
     39            ],
     40            "When enabled, all jobs will be displayed with pagination controls. When disabled, only the specified number of jobs will be shown.": [
     41                "N\u00e5r denne funksjonen er aktivert, vises alle jobber med pagineringskontroller. N\u00e5r den er deaktivert, vises bare det angitte antallet jobber."
     42            ],
    3743            "Number of items": [
    3844                "Antall oppf\u00f8ringer"
     45            ],
     46            "Jobs per page": [
     47                "Jobber per side"
    3948            ],
    4049            "Order by": [
  • jobbnorge-block/tags/2.2.0/languages/wp-jobbnorge-block-nb_NO-bb1d7dea005e67527e728d4801f74b61.json

    r3002720 r3322139  
    11{
    2     "translation-revision-date": "2023-11-28 13:09+0100",
    3     "generator": "WP-CLI\/2.9.0",
     2    "translation-revision-date": "2025-07-03 23:16+0200",
     3    "generator": "WP-CLI\/2.12.0",
    44    "source": "src\/edit.js",
    55    "domain": "messages",
     
    3535                "Innstillinger"
    3636            ],
     37            "Enable pagination": [
     38                "Aktiver paginering"
     39            ],
     40            "When enabled, all jobs will be displayed with pagination controls. When disabled, only the specified number of jobs will be shown.": [
     41                "N\u00e5r denne funksjonen er aktivert, vises alle jobber med pagineringskontroller. N\u00e5r den er deaktivert, vises bare det angitte antallet jobber."
     42            ],
    3743            "Number of items": [
    3844                "Antall oppf\u00f8ringer"
     45            ],
     46            "Jobs per page": [
     47                "Jobber per side"
    3948            ],
    4049            "Order by": [
  • jobbnorge-block/tags/2.2.0/languages/wp-jobbnorge-block-nb_NO.po

    r3002720 r3322139  
    55"Project-Id-Version: Jobbnorge Block 0.1.0\n"
    66"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/jobbnorge-block\n"
    7 "POT-Creation-Date: 2023-11-28T11:53:20+00:00\n"
    8 "PO-Revision-Date: 2023-11-28 13:09+0100\n"
     7"POT-Creation-Date: 2025-07-03T21:00:36+00:00\n"
     8"PO-Revision-Date: 2025-07-03 23:16+0200\n"
    99"Last-Translator: Per Søderlind <[email protected]>\n"
    1010"Language-Team: \n"
     
    1313"Content-Type: text/plain; charset=UTF-8\n"
    1414"Content-Transfer-Encoding: 8bit\n"
    15 "X-Generator: Poedit 3.4.1\n"
     15"X-Generator: Poedit 3.6\n"
    1616"X-Domain: wp-jobbnorge-block\n"
    1717
    1818#. Plugin Name of the plugin
     19#: wp-jobb-norge.php
    1920msgid "Jobbnorge Block"
    2021msgstr "Jobbnorge-blokken"
    2122
    2223#. Plugin URI of the plugin
     24#: wp-jobb-norge.php
    2325msgid "https://wordpress.org/plugins/jobbnorge-block/"
    2426msgstr "https://wordpress.org/plugins/jobbnorge-block/"
    2527
    2628#. Description of the plugin
     29#: wp-jobb-norge.php
    2730msgid "Retrieve and display job listings from Jobbnorge.no"
    2831msgstr "Hente og vise stillingsannonser fra Jobbnorge.no"
    2932
    3033#. Author of the plugin
     34#: wp-jobb-norge.php
    3135msgid "PerS"
    3236msgstr "PerS"
    3337
    34 #: wp-jobb-norge.php:159
     38#: wp-jobb-norge.php:181
    3539msgid "Invalid ID"
    3640msgstr "Ugyldig ID"
    3741
    38 #: wp-jobb-norge.php:181
     42#: wp-jobb-norge.php:212
    3943msgid "Error connecting to Jobbnorge.no"
    4044msgstr "Feil ved tilkobling til Jobbnorge.no"
    4145
    42 #: wp-jobb-norge.php:195
     46#: wp-jobb-norge.php:251
    4347msgid "No jobs found"
    4448msgstr "Ingen jobber funnet"
    4549
    46 #: wp-jobb-norge.php:205
     50#: wp-jobb-norge.php:261
    4751msgid "(no title)"
    4852msgstr "(ingen tittel)"
    4953
    50 #: wp-jobb-norge.php:226 build/init.js:1 src/edit.js:160
     54#: wp-jobb-norge.php:282 build/init.js:1 src/edit.js:181
    5155msgid "Employer"
    5256msgstr "Arbeidsgiver"
    5357
    54 #: wp-jobb-norge.php:227
     58#: wp-jobb-norge.php:283
    5559msgid "Scope"
    5660msgstr "Omfang"
    5761
    58 #: wp-jobb-norge.php:282
     62#: wp-jobb-norge.php:345
    5963msgid "Read more"
    6064msgstr "Les mer"
    6165
    62 #: wp-jobb-norge.php:353
     66#: wp-jobb-norge.php:416
    6367msgid "Deadline:"
    6468msgstr "Søknadsfrist:"
    6569
    66 #: build/init.js:1 src/edit.js:95
     70#: wp-jobb-norge.php:556
     71msgid "Job listings pagination"
     72msgstr "Paginering av stillingsannonser"
     73
     74#: wp-jobb-norge.php:562
     75#, php-format
     76msgid "Showing %d-%d of %d jobs"
     77msgstr "Viser %d-%d av %d jobber"
     78
     79#: wp-jobb-norge.php:577 wp-jobb-norge.php:582
     80msgid "Previous"
     81msgstr "Forrige"
     82
     83#: wp-jobb-norge.php:590
     84#, php-format
     85msgid "Page %d of %d"
     86msgstr "Side %d av %d"
     87
     88#: wp-jobb-norge.php:601 wp-jobb-norge.php:606
     89msgid "Next"
     90msgstr "Neste"
     91
     92#: build/init.js:1 src/edit.js:97
    6793msgid ""
    6894"Select employers to display. Ctrl-click (Windows) or Cmd-click (Mac) to "
     
    7399"arbeidsgivere."
    74100
    75 #: build/init.js:1 src/edit.js:103
     101#: build/init.js:1 src/edit.js:105
    76102msgid "Employer ID [,id2, id3, ..]"
    77103msgstr "Arbeidsgiver-ID [,id2, id3, ...]"
    78104
    79 #: build/init.js:1 src/edit.js:110
     105#: build/init.js:1 src/edit.js:112
    80106msgid "Save"
    81107msgstr "Lagre"
    82108
    83 #: build/init.js:1 src/edit.js:121
     109#: build/init.js:1 src/edit.js:123
    84110msgid "Edit Jobbnorge URL"
    85111msgstr "Rediger Jobbnorge URL"
    86112
    87 #: build/init.js:1 src/edit.js:126
     113#: build/init.js:1 src/edit.js:128
    88114msgid "List view"
    89115msgstr "Listevisning"
    90116
    91 #: build/init.js:1 src/edit.js:132 src/edit.js:195
     117#: build/init.js:1 src/edit.js:134 src/edit.js:216
    92118msgid "Grid view"
    93119msgstr "Rutemønster"
    94120
    95 #: build/init.js:1 src/edit.js:144
     121#: build/init.js:1 src/edit.js:146
    96122msgid "Settings"
    97123msgstr "Innstillinger"
    98124
    99 #: build/init.js:1 src/edit.js:147
     125#: build/init.js:1 src/edit.js:148
     126msgid "Enable pagination"
     127msgstr "Aktiver paginering"
     128
     129#: build/init.js:1 src/edit.js:149
     130msgid ""
     131"When enabled, all jobs will be displayed with pagination controls. When "
     132"disabled, only the specified number of jobs will be shown."
     133msgstr ""
     134"Når denne funksjonen er aktivert, vises alle jobber med "
     135"pagineringskontroller. Når den er deaktivert, vises bare det angitte "
     136"antallet jobber."
     137
     138#: build/init.js:1 src/edit.js:156
    100139msgid "Number of items"
    101140msgstr "Antall oppføringer"
    102141
    103 #: build/init.js:1 src/edit.js:156
     142#: build/init.js:1 src/edit.js:167
     143msgid "Jobs per page"
     144msgstr "Jobber per side"
     145
     146#: build/init.js:1 src/edit.js:177
    104147msgid "Order by"
    105148msgstr "Sorter etter"
    106149
    107 #: build/init.js:1 src/edit.js:159
     150#: build/init.js:1 src/edit.js:180
    108151msgid "Deadline"
    109152msgstr "Søknadsfrist"
    110153
    111 #: build/init.js:1 src/edit.js:166
     154#: build/init.js:1 src/edit.js:187
    112155msgid "No jobs found message"
    113156msgstr "Ingen jobber funnet melding"
    114157
    115 #: build/init.js:1 src/edit.js:167
     158#: build/init.js:1 src/edit.js:188
    116159msgid "Message to display if no jobs are found"
    117160msgstr "Melding som vises hvis ingen jobber er funnet"
    118161
    119 #: build/init.js:1 src/edit.js:168
     162#: build/init.js:1 src/edit.js:189
    120163msgid "There are no jobs at this time."
    121164msgstr "Det er for tiden ingen ledige stillinger."
    122165
    123 #: build/init.js:1 src/edit.js:172
     166#: build/init.js:1 src/edit.js:193
    124167msgid "Item"
    125168msgstr "Element"
    126169
    127 #: build/init.js:1 src/edit.js:174
     170#: build/init.js:1 src/edit.js:195
    128171msgid "Display employer"
    129172msgstr "Vis arbeidsgiver"
    130173
    131 #: build/init.js:1 src/edit.js:179
     174#: build/init.js:1 src/edit.js:200
    132175msgid "Display excerpt"
    133176msgstr "Vis utdrag"
    134177
    135 #: build/init.js:1 src/edit.js:184
     178#: build/init.js:1 src/edit.js:205
    136179msgid "Display deadline"
    137180msgstr "Vis søknadsfrist"
    138181
    139 #: build/init.js:1 src/edit.js:189
     182#: build/init.js:1 src/edit.js:210
    140183msgid "Display scope"
    141184msgstr "Vis omfang"
    142185
    143 #: build/init.js:1 src/edit.js:198
     186#: build/init.js:1 src/edit.js:219
    144187msgid "Columns"
    145188msgstr "Kolonner"
  • jobbnorge-block/tags/2.2.0/languages/wp-jobbnorge-block.pot

    r3002720 r3322139  
    1 # Copyright (C) 2023 PerS
     1# Copyright (C) 2025 PerS
    22# This file is distributed under the GPL-2.0-or-later.
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: Jobbnorge Block 2.1.4\n"
     5"Project-Id-Version: Jobbnorge Block 2.2.0\n"
    66"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/jobbnorge-block\n"
    77"Last-Translator: Per Søderlind <[email protected]\n"
     
    1010"Content-Type: text/plain; charset=UTF-8\n"
    1111"Content-Transfer-Encoding: 8bit\n"
    12 "POT-Creation-Date: 2023-11-28T12:11:37+00:00\n"
     12"POT-Creation-Date: 2025-07-03T21:16:21+00:00\n"
    1313"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    14 "X-Generator: WP-CLI 2.9.0\n"
     14"X-Generator: WP-CLI 2.12.0\n"
    1515"X-Domain: wp-jobbnorge-block\n"
    1616
    1717#. Plugin Name of the plugin
     18#: wp-jobb-norge.php
    1819msgid "Jobbnorge Block"
    1920msgstr ""
    2021
    2122#. Plugin URI of the plugin
     23#: wp-jobb-norge.php
    2224msgid "https://wordpress.org/plugins/jobbnorge-block/"
    2325msgstr ""
    2426
    2527#. Description of the plugin
     28#: wp-jobb-norge.php
    2629msgid "Retrieve and display job listings from Jobbnorge.no"
    2730msgstr ""
    2831
    2932#. Author of the plugin
     33#: wp-jobb-norge.php
    3034msgid "PerS"
    3135msgstr ""
    3236
    33 #: wp-jobb-norge.php:159
     37#: wp-jobb-norge.php:181
    3438msgid "Invalid ID"
    3539msgstr ""
    3640
    37 #: wp-jobb-norge.php:181
     41#: wp-jobb-norge.php:212
    3842msgid "Error connecting to Jobbnorge.no"
    3943msgstr ""
    4044
    41 #: wp-jobb-norge.php:195
     45#: wp-jobb-norge.php:251
    4246msgid "No jobs found"
    4347msgstr ""
    4448
    45 #: wp-jobb-norge.php:205
     49#: wp-jobb-norge.php:261
    4650msgid "(no title)"
    4751msgstr ""
    4852
    49 #: wp-jobb-norge.php:226
    50 #: build/init.js:1
    51 #: src/edit.js:160
     53#: wp-jobb-norge.php:282
     54#: build/init.js:1
     55#: src/edit.js:181
    5256msgid "Employer"
    5357msgstr ""
    5458
    55 #: wp-jobb-norge.php:227
     59#: wp-jobb-norge.php:283
    5660msgid "Scope"
    5761msgstr ""
    5862
    59 #: wp-jobb-norge.php:282
     63#: wp-jobb-norge.php:345
    6064msgid "Read more"
    6165msgstr ""
    6266
    63 #: wp-jobb-norge.php:353
     67#: wp-jobb-norge.php:416
    6468msgid "Deadline:"
    6569msgstr ""
    6670
    67 #: build/init.js:1
    68 #: src/edit.js:95
     71#: wp-jobb-norge.php:556
     72msgid "Job listings pagination"
     73msgstr ""
     74
     75#: wp-jobb-norge.php:562
     76#, php-format
     77msgid "Showing %d-%d of %d jobs"
     78msgstr ""
     79
     80#: wp-jobb-norge.php:577
     81#: wp-jobb-norge.php:582
     82msgid "Previous"
     83msgstr ""
     84
     85#: wp-jobb-norge.php:590
     86#, php-format
     87msgid "Page %d of %d"
     88msgstr ""
     89
     90#: wp-jobb-norge.php:601
     91#: wp-jobb-norge.php:606
     92msgid "Next"
     93msgstr ""
     94
     95#: build/init.js:1
     96#: src/edit.js:97
    6997msgid "Select employers to display. Ctrl-click (Windows) or Cmd-click (Mac) to select multiple employers. Shift-click to select a range of employers."
    7098msgstr ""
    7199
    72100#: build/init.js:1
    73 #: src/edit.js:103
     101#: src/edit.js:105
    74102msgid "Employer ID [,id2, id3, ..]"
    75103msgstr ""
    76104
    77105#: build/init.js:1
    78 #: src/edit.js:110
     106#: src/edit.js:112
    79107msgid "Save"
    80108msgstr ""
    81109
    82110#: build/init.js:1
    83 #: src/edit.js:121
     111#: src/edit.js:123
    84112msgid "Edit Jobbnorge URL"
    85113msgstr ""
    86114
    87115#: build/init.js:1
    88 #: src/edit.js:126
     116#: src/edit.js:128
    89117msgid "List view"
    90118msgstr ""
    91119
    92120#: build/init.js:1
    93 #: src/edit.js:132
     121#: src/edit.js:134
     122#: src/edit.js:216
     123msgid "Grid view"
     124msgstr ""
     125
     126#: build/init.js:1
     127#: src/edit.js:146
     128msgid "Settings"
     129msgstr ""
     130
     131#: build/init.js:1
     132#: src/edit.js:148
     133msgid "Enable pagination"
     134msgstr ""
     135
     136#: build/init.js:1
     137#: src/edit.js:149
     138msgid "When enabled, all jobs will be displayed with pagination controls. When disabled, only the specified number of jobs will be shown."
     139msgstr ""
     140
     141#: build/init.js:1
     142#: src/edit.js:156
     143msgid "Number of items"
     144msgstr ""
     145
     146#: build/init.js:1
     147#: src/edit.js:167
     148msgid "Jobs per page"
     149msgstr ""
     150
     151#: build/init.js:1
     152#: src/edit.js:177
     153msgid "Order by"
     154msgstr ""
     155
     156#: build/init.js:1
     157#: src/edit.js:180
     158msgid "Deadline"
     159msgstr ""
     160
     161#: build/init.js:1
     162#: src/edit.js:187
     163msgid "No jobs found message"
     164msgstr ""
     165
     166#: build/init.js:1
     167#: src/edit.js:188
     168msgid "Message to display if no jobs are found"
     169msgstr ""
     170
     171#: build/init.js:1
     172#: src/edit.js:189
     173msgid "There are no jobs at this time."
     174msgstr ""
     175
     176#: build/init.js:1
     177#: src/edit.js:193
     178msgid "Item"
     179msgstr ""
     180
     181#: build/init.js:1
    94182#: src/edit.js:195
    95 msgid "Grid view"
    96 msgstr ""
    97 
    98 #: build/init.js:1
    99 #: src/edit.js:144
    100 msgid "Settings"
    101 msgstr ""
    102 
    103 #: build/init.js:1
    104 #: src/edit.js:147
    105 msgid "Number of items"
    106 msgstr ""
    107 
    108 #: build/init.js:1
    109 #: src/edit.js:156
    110 msgid "Order by"
    111 msgstr ""
    112 
    113 #: build/init.js:1
    114 #: src/edit.js:159
    115 msgid "Deadline"
    116 msgstr ""
    117 
    118 #: build/init.js:1
    119 #: src/edit.js:166
    120 msgid "No jobs found message"
    121 msgstr ""
    122 
    123 #: build/init.js:1
    124 #: src/edit.js:167
    125 msgid "Message to display if no jobs are found"
    126 msgstr ""
    127 
    128 #: build/init.js:1
    129 #: src/edit.js:168
    130 msgid "There are no jobs at this time."
    131 msgstr ""
    132 
    133 #: build/init.js:1
    134 #: src/edit.js:172
    135 msgid "Item"
    136 msgstr ""
    137 
    138 #: build/init.js:1
    139 #: src/edit.js:174
    140183msgid "Display employer"
    141184msgstr ""
    142185
    143186#: build/init.js:1
    144 #: src/edit.js:179
     187#: src/edit.js:200
    145188msgid "Display excerpt"
    146189msgstr ""
    147190
    148191#: build/init.js:1
    149 #: src/edit.js:184
     192#: src/edit.js:205
    150193msgid "Display deadline"
    151194msgstr ""
    152195
    153196#: build/init.js:1
    154 #: src/edit.js:189
     197#: src/edit.js:210
    155198msgid "Display scope"
    156199msgstr ""
    157200
    158201#: build/init.js:1
    159 #: src/edit.js:198
     202#: src/edit.js:219
    160203msgid "Columns"
    161204msgstr ""
  • jobbnorge-block/tags/2.2.0/package-lock.json

    r3003492 r3322139  
    11{
    22    "name": "jobbnorge-block",
    3     "version": "2.1.5",
     3    "version": "2.2.0",
    44    "lockfileVersion": 3,
    55    "requires": true,
     
    77        "": {
    88            "name": "jobbnorge-block",
    9             "version": "2.1.5",
     9            "version": "2.2.0",
    1010            "license": "GPL-2.0-or-later",
    1111            "dependencies": {
  • jobbnorge-block/tags/2.2.0/package.json

    r3003492 r3322139  
    11{
    22    "name": "jobbnorge-block",
    3     "version": "2.1.5",
     3    "version": "2.2.0",
    44    "description": "Jobbnorge Block for WordPress Gutenberg",
    55    "author": "Per Søderlind <[email protected]>",
  • jobbnorge-block/tags/2.2.0/readme.txt

    r3003492 r3322139  
    55Requires at least: 5.9
    66Requires PHP:      7.4
    7 Stable tag:        2.1.5
     7Stable tag:        2.2.0
    88License:           GPL-2.0-or-later
    99License URI:       https://www.gnu.org/licenses/gpl-2.0.html
     
    104104== Changelog ==
    105105
     106= 2.2.0 =
     107
     108* NEW: Add frontend pagination support with AJAX loading
     109* NEW: Add pagination controls (enable/disable, jobs per page setting)
     110* ENHANCEMENT: Upgrade to Jobbnorge API v3 for better performance
     111* ENHANCEMENT: Implement PHP-based pagination to work around API limitations with employer filtering
     112* ENHANCEMENT: Add responsive grid layout that adapts to screen size
     113* ENHANCEMENT: Improve cache key logic to include pagination and layout parameters
     114* ENHANCEMENT: Add loading states and error handling for pagination
     115* ENHANCEMENT: Separate frontend and admin CSS loading for better performance
     116* FIX: Fix CSS class naming conflicts that prevented grid view from working on frontend
     117* FIX: Resolve frontend style loading issues
     118* DEVELOPER: Add comprehensive webpack build configuration for multiple entry points
     119* DEVELOPER: Add pagination JavaScript with proper AJAX handling and nonce security
     120
    106121= 2.1.5 =
    107122
  • jobbnorge-block/tags/2.2.0/src/block.json

    r3003492 r3322139  
    33    "apiVersion": 2,
    44    "name": "dss/jobbnorge",
    5     "version": "2.1.5",
     5    "version": "2.2.0",
    66    "title": "Jobbnorge",
    77    "category": "widgets",
     
    6363            "type": "number",
    6464            "default": 55
     65        },
     66        "enablePagination": {
     67            "type": "boolean",
     68            "default": true
     69        },
     70        "jobsPerPage": {
     71            "type": "number",
     72            "default": 10
    6573        }
    6674    },
    6775    "textdomain": "wp-jobbnorge-block",
    6876    "editorScript": "file:init.js",
    69     "editorStyle": "file:editor.scss",
    70     "style": "file:style.scss"
     77    "editorStyle": "file:editor.css",
     78    "style": "file:style-init.css"
    7179}
  • jobbnorge-block/tags/2.2.0/src/edit.js

    r3002720 r3322139  
    5252        noJobsMessage,
    5353        orderBy,
     54        enablePagination,
     55        jobsPerPage,
    5456    } = attributes;
    5557
     
    143145            <InspectorControls>
    144146                <PanelBody title={__('Settings', 'wp-jobbnorge-block')}>
    145                     <RangeControl
    146                         __nextHasNoMarginBottom
    147                         label={__('Number of items', 'wp-jobbnorge-block')}
    148                         value={itemsToShow}
    149                         onChange={(value) => setAttributes({ itemsToShow: value })}
    150                         min={DEFAULT_MIN_ITEMS}
    151                         max={DEFAULT_MAX_ITEMS}
    152                         required
    153                     />
     147                    <ToggleControl
     148                        label={__('Enable pagination', 'wp-jobbnorge-block')}
     149                        help={__('When enabled, all jobs will be displayed with pagination controls. When disabled, only the specified number of jobs will be shown.', 'wp-jobbnorge-block')}
     150                        checked={enablePagination}
     151                        onChange={(value) => setAttributes({ enablePagination: value })}
     152                    />
     153                    {!enablePagination && (
     154                        <RangeControl
     155                            __nextHasNoMarginBottom
     156                            label={__('Number of items', 'wp-jobbnorge-block')}
     157                            value={itemsToShow}
     158                            onChange={(value) => setAttributes({ itemsToShow: value })}
     159                            min={DEFAULT_MIN_ITEMS}
     160                            max={DEFAULT_MAX_ITEMS}
     161                            required
     162                        />
     163                    )}
     164                    {enablePagination && (
     165                        <RangeControl
     166                            __nextHasNoMarginBottom
     167                            label={__('Jobs per page', 'wp-jobbnorge-block')}
     168                            value={jobsPerPage}
     169                            onChange={(value) => setAttributes({ jobsPerPage: value })}
     170                            min={1}
     171                            max={50}
     172                            required
     173                        />
     174                    )}
    154175                    {employerID.includes(',') && (
    155176                        <RadioControl
  • jobbnorge-block/tags/2.2.0/src/editor.scss

    r2853882 r3322139  
    11$break-medium: 782px;
     2$break-small: 600px;
    23$grid-unit: 8px;
    34$grid-unit-10: 1 * $grid-unit; // 8px
     
    910}
    1011
    11 .wp-block-dss-jobbnorge li a > div {
     12@mixin break-small() {
     13    @media (min-width: #{ ($break-small) }) {
     14        @content;
     15    }
     16}
     17
     18.wp-block-dss-jobbnorge li a>div {
    1219    display: inline;
    1320}
     
    1724    align-items: stretch;
    1825
    19     > * {
     26    >* {
    2027        margin-bottom: $grid-unit-10;
    2128    }
    2229
    2330    @include break-medium() {
    24         > * {
     31        >* {
    2532            margin-bottom: 0;
    2633        }
    2734    }
    2835}
    29 
    30 
    3136
    3237.wp-block-dss-jobbnorge__placeholder-input {
     
    4348    }
    4449}
     50
     51// Grid styles for the editor
     52ul.wp-block-dss-jobbnorge {
     53    list-style: none;
     54    padding: 0;
     55
     56    &.wp-block-dss-jobbnorge {
     57        box-sizing: border-box;
     58    }
     59
     60    li {
     61        margin: 0 0 1em 0;
     62    }
     63
     64    &.is-grid {
     65        display: flex;
     66        flex-wrap: wrap;
     67        padding: 0;
     68        list-style: none;
     69
     70        li {
     71            margin: 0 1em 1em 0;
     72            width: 100%;
     73        }
     74    }
     75
     76    @include break-small {
     77        @for $i from 2 through 6 {
     78            &.columns-#{ $i } li {
     79                width: calc((100% / #{ $i }) - 1em);
     80            }
     81        }
     82    }
     83}
  • jobbnorge-block/tags/2.2.0/src/style.scss

    r2997962 r3322139  
    88
    99
    10 ul.wp-block-dss-jobbnorge { // The ul is needed for specificity to override the reset styles in the editor.
     10ul.wp-block-dss-jobbnorge {
     11    // The ul is needed for specificity to override the reset styles in the editor.
    1112    list-style: none;
    1213    padding: 0;
     
    2122        margin-right: 2em;
    2223    }
     24
    2325    &.alignright {
    2426        /*rtl:ignore*/
    2527        margin-left: 2em;
    2628    }
     29
    2730    li {
    2831        margin: 0 0 1em 0;
    2932    }
     33
    3034    &.is-grid {
    3135        display: flex;
     
    4347        @for $i from 2 through 6 {
    4448            &.columns-#{ $i } li {
    45                 width: calc(( 100% / #{ $i } ) - 1em);
     49                width: calc((100% / #{ $i }) - 1em);
    4650            }
    4751        }
     
    6367.wp-block-dss-jobbnorge__item-employer,
    6468.wp-block-dss-jobbnorge__item-deadline,
    65 .wp-block-dss-jobbnorge__item-scope{
     69.wp-block-dss-jobbnorge__item-scope {
    6670    display: block;
    6771    font-weight: 600;
     
    6973}
    7074
     75// Pagination styles
     76.wp-block-dss-jobbnorge {
     77    &__pagination {
     78        display: flex;
     79        flex-direction: column;
     80        gap: 1rem;
     81        margin-top: 2rem;
     82        padding: 1rem 0;
     83        border-top: 1px solid #e0e0e0;
     84
     85        @include break-small {
     86            flex-direction: row;
     87            justify-content: space-between;
     88            align-items: center;
     89        }
     90    }
     91
     92    &__pagination-info {
     93        font-size: 0.875rem;
     94        color: #666;
     95        margin: 0;
     96    }
     97
     98    &__pagination-controls {
     99        display: flex;
     100        gap: 0.5rem;
     101        align-items: center;
     102
     103        button {
     104            padding: 0.5rem 1rem;
     105            border: 1px solid #ddd;
     106            background: white;
     107            cursor: pointer;
     108            border-radius: 4px;
     109            font-size: 0.875rem;
     110            transition: all 0.2s ease;
     111
     112            &:hover:not(:disabled) {
     113                background: #f5f5f5;
     114                border-color: #999;
     115            }
     116
     117            &:disabled {
     118                opacity: 0.5;
     119                cursor: not-allowed;
     120            }
     121        }
     122
     123        .wp-block-dss-jobbnorge__pagination-info {
     124            font-size: 0.875rem;
     125            color: #333;
     126            margin: 0 0.5rem;
     127        }
     128    }
     129
     130    // Loading state
     131    &__loading {
     132        opacity: 0.6;
     133        pointer-events: none;
     134
     135        &::after {
     136            content: '';
     137            position: absolute;
     138            top: 50%;
     139            left: 50%;
     140            width: 20px;
     141            height: 20px;
     142            margin: -10px 0 0 -10px;
     143            border: 2px solid #ccc;
     144            border-top-color: #333;
     145            border-radius: 50%;
     146            animation: spin 1s linear infinite;
     147        }
     148    }
     149
     150    // Error message
     151    &__error {
     152        margin: 1rem 0;
     153        padding: 0.75rem;
     154        background: #ffebe8;
     155        border: 1px solid #d63638;
     156        border-radius: 4px;
     157        color: #d63638;
     158
     159        p {
     160            margin: 0;
     161        }
     162    }
     163}
     164
     165@keyframes spin {
     166    to {
     167        transform: rotate(360deg);
     168    }
     169}
  • jobbnorge-block/tags/2.2.0/wp-jobb-norge.php

    r3003492 r3322139  
    66 * Requires at least: 5.9
    77 * Requires PHP:      7.0
    8  * Version:           2.1.5
     8 * Version:           2.2.0
    99 * Author:            PerS
    1010 * License:           GPL-2.0-or-later
     
    3636    add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\dss_jobbnorge_enqueue_scripts' );
    3737
    38     // Add the 'dss_jobbnorge_enqueue_scripts' function to the 'wp_enqueue_scripts' action hook.
     38    // Add the 'dss_jobbnorge_enqueue_frontend_styles' function to the 'wp_enqueue_scripts' action hook.
    3939    // This function will be called when scripts and styles are enqueued for the front end of the site.
    40     add_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\dss_jobbnorge_enqueue_scripts' );
     40    add_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\dss_jobbnorge_enqueue_frontend_styles' );
    4141
    4242    // Load the plugin's text domain for internationalization.
     
    5050    register_block_type(
    5151        __DIR__ . '/build',
    52         [
     52        [ 
    5353            'render_callback' => __NAMESPACE__ . '\render_block_dss_jobbnorge',
    5454        ]
     
    8181        // If it does, require it and merge its dependencies with the existing ones.
    8282        $file   = require $deps_file;
    83         $jsdeps = array_merge( $jsdeps, $file['dependencies'] );
     83        $jsdeps = array_merge( $jsdeps, $file[ 'dependencies' ] );
    8484        // Also, set the version to the one from the file.
    85         $version = $file['version'];
     85        $version = $file[ 'version' ];
    8686    }
    8787
     
    9292        wp_enqueue_style( 'dss-jobbnorge-admin' );
    9393    }
    94 
    95     // Register and enqueue a CSS file for the public view.
    96     wp_register_style( 'dss-jobbnorge', plugin_dir_url( __FILE__ ) . 'build/style-init.css', [], $version );
    97     wp_enqueue_style( 'dss-jobbnorge' );
    9894
    9995    // Set translations for the script.
     
    118114            'dss-jobbnorge-editor-script',
    119115            'wpJobbnorgeBlock',
    120             [
     116            [ 
    121117                'employers' => $employers,
    122118            ]
     
    126122
    127123/**
     124 * Enqueue frontend styles for the block
     125 *
     126 * @return void
     127 */
     128function dss_jobbnorge_enqueue_frontend_styles(): void {
     129    // Define the path to the dependencies file.
     130    $deps_file = plugin_dir_path( __FILE__ ) . 'build/init.asset.php';
     131
     132    // Initialize version number.
     133    $version = wp_rand();
     134
     135    // Check if the dependencies file exists.
     136    if ( file_exists( $deps_file ) ) {
     137        // If it does, require it and get the version.
     138        $file    = require $deps_file;
     139        $version = $file[ 'version' ];
     140    }
     141
     142    // Register and enqueue a CSS file for the public view.
     143    wp_register_style( 'dss-jobbnorge', plugin_dir_url( __FILE__ ) . 'build/style-init.css', [], $version );
     144    wp_enqueue_style( 'dss-jobbnorge' );
     145}
     146
     147/**
    128148 * Renders the `jobbnorge` block on server.
    129149 *
     
    137157    $attributes = wp_parse_args(
    138158        $attributes,
    139         [
    140             'employerID'      => '',
    141             'displayEmployer' => false,
    142             'displayDate'     => true,
    143             'displayDeadline' => false,
    144             'displayScope'    => false,
    145             'displayExcerpt'  => true,
    146             'excerptLength'   => 55,
    147             'blockLayout'     => 'list',
    148             'orderBy'         => 'Deadline',
    149             'columns'         => 3,
    150             'itemsToShow'     => 5,
     159        [
     160            'employerID'       => '',
     161            'displayEmployer'  => false,
     162            'displayDate'      => true,
     163            'displayDeadline'  => false,
     164            'displayScope'     => false,
     165            'displayExcerpt'   => true,
     166            'excerptLength'    => 55,
     167            'blockLayout'      => 'list',
     168            'orderBy'          => 'Deadline',
     169            'columns'          => 3,
     170            'itemsToShow'      => 5,
     171            'enablePagination' => true,
     172            'jobsPerPage'      => 10,
    151173        ]
    152174    );
    153175
    154176    // Convert employer IDs to an array and trim whitespace.
    155     $arr_ids = array_map( 'trim', explode( ',', $attributes['employerID'] ) );
     177    $arr_ids = array_map( 'trim', explode( ',', $attributes[ 'employerID' ] ) );
    156178
    157179    // Check if all IDs are numeric. If not, return an error message.
     
    160182    }
    161183
    162     // Construct the API URL.
    163     $jobbnorge_api_url = 'https://publicapi.jobbnorge.no/v2/Jobs?abroad=false&orderBy=' . $attributes['orderBy'];
     184    // Get current page for pagination
     185    $current_page = isset( $_GET[ 'jobbnorge_page' ] ) ? max( 1, intval( $_GET[ 'jobbnorge_page' ] ) ) : 1;
     186
     187    // Determine items per page based on pagination setting
     188    $items_per_page = $attributes[ 'enablePagination' ] ? $attributes[ 'jobsPerPage' ] : $attributes[ 'itemsToShow' ];
     189
     190    // Construct the API URL for v3
     191    // NOTE: API v3 pagination doesn't work correctly with employer filtering
     192    // So we fetch all jobs for the employers and paginate in PHP
     193    $jobbnorge_api_url = 'https://publicapi.jobbnorge.no/v3/Jobs?abroad=false&orderBy=' . $attributes[ 'orderBy' ];
    164194
    165195    // Add each employer ID to the API URL.
     
    171201    $cache      = new \Jobbnorge_CacheHandler( $cache_path );
    172202
    173     $cache_key  = md5( $jobbnorge_api_url );
    174     $expiration = apply_filters( 'jobbnorge_cache_time', 30 * MINUTE_IN_SECONDS );
    175     $body       = $cache->get( $cache_key, $expiration );
    176 
    177     if ( false === $body ) {
     203    // Cache key based on employer IDs and settings, not pagination
     204    $cache_key     = md5( $jobbnorge_api_url );
     205    $expiration    = apply_filters( 'jobbnorge_cache_time', 30 * MINUTE_IN_SECONDS );
     206    $response_data = $cache->get( $cache_key, $expiration );
     207
     208    if ( false === $response_data ) {
    178209        $response = wp_remote_get( $jobbnorge_api_url );
    179210
     
    182213        }
    183214
    184         $body = wp_remote_retrieve_body( $response );
    185         $cache->set( $cache_key, $body );
    186     }
    187 
    188     // Decode the JSON response and limit the number of items.
    189     $items = json_decode( $body, true );
    190     // Just get desired number of items.
    191     $items = array_slice( $items, 0, $attributes['itemsToShow'] );
     215        $body          = wp_remote_retrieve_body( $response );
     216        $response_data = json_decode( $body, true );
     217        $cache->set( $cache_key, $response_data );
     218    }
     219
     220    // Debug: Log the API response structure
     221    if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     222        error_log( 'Jobbnorge API URL: ' . $jobbnorge_api_url );
     223        error_log( 'Jobbnorge API Response: ' . print_r( $response_data, true ) );
     224    }
     225    // Handle v3 API response structure
     226    $all_items  = isset( $response_data[ 'jobs' ] ) ? $response_data[ 'jobs' ] : $response_data;
     227    $total_jobs = count( $all_items );
     228
     229    // Debug: Log the items array
     230    if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     231        error_log( 'Items count: ' . count( $all_items ) );
     232        error_log( 'Total jobs: ' . $total_jobs );
     233        if ( ! empty( $all_items ) ) {
     234            error_log( 'First item: ' . print_r( $all_items[ 0 ], true ) );
     235        }
     236    }
     237
     238    // Implement pagination in PHP since API pagination doesn't work with employer filtering
     239    if ( $attributes[ 'enablePagination' ] && $total_jobs > 0 ) {
     240        // Calculate pagination
     241        $start_index = ( $current_page - 1 ) * $attributes[ 'jobsPerPage' ];
     242        $items       = array_slice( $all_items, $start_index, $attributes[ 'jobsPerPage' ] );
     243    } else {
     244        // For non-paginated, limit to itemsToShow
     245        $items      = array_slice( $all_items, 0, $attributes[ 'itemsToShow' ] );
     246        $total_jobs = count( $items ); // Update total for non-paginated display
     247    }
    192248
    193249    // If there are no items, return an error message.
     
    202258    foreach ( $items as $item ) {
    203259        // Sanitize and format the title.
    204         $title = esc_html( trim( wp_strip_all_tags( $item['title'] ) ) );
     260        $title = esc_html( trim( wp_strip_all_tags( $item[ 'title' ] ) ) );
    205261        $title = empty( $title ) ? __( '(no title)', 'wp-jobbnorge-block' ) : $title;
    206262
    207263        // Sanitize the link.
    208         $link = esc_url( $item['link'] );
     264        $link = esc_url( $item[ 'link' ] );
    209265        // If there's a link, wrap the title in an anchor tag.
    210266        $title = $link ? "<a href='{$link}'>{$title}</a>" : $title;
     
    216272        $deadline = '';
    217273        // If the displayDate attribute is true and the item has a deadline, format the deadline.
    218         if ( $attributes['displayDate'] && isset( $item['deadline'] ) ) {
    219             $deadline = format_deadline( $item['deadline'] );
     274        if ( $attributes[ 'displayDate' ] && isset( $item[ 'deadline' ] ) ) {
     275            $deadline = format_deadline( $item[ 'deadline' ] );
    220276        }
    221277
     
    238294    }
    239295
    240     // Initialize an array for the classnames.
    241     $classnames = [];
    242 
    243     // If the blockLayout attribute is 'grid', add the 'is-grid' and 'columns-' classes.
    244     if ( 'grid' === $attributes['blockLayout'] ) {
    245         add_classname( $classnames, $attributes, 'blockLayout', 'is-grid' );
    246         add_classname( $classnames, $attributes, 'columns', 'columns-' . $attributes['columns'] );
    247     }
    248 
    249     // Add the 'has-' classes based on the display attributes.
    250     add_classname( $classnames, $attributes, 'displayEmployer', 'has-employer' );
    251     add_classname( $classnames, $attributes, 'displayDate', 'has-dates' );
    252     add_classname( $classnames, $attributes, 'displayDeadline', 'has-deadline' );
    253     add_classname( $classnames, $attributes, 'displayScope', 'has-scope' );
    254     add_classname( $classnames, $attributes, 'displayExcerpt', 'has-excerpts' );
    255 
    256     // Get the block wrapper attributes and add the classnames to it.
    257     $wrapper_attributes = get_block_wrapper_attributes( [ 'class' => implode( ' ', $classnames ) ] );
     296    // Get the block wrapper attributes (without grid classes)
     297    $wrapper_classes = [];
     298    add_classname( $wrapper_classes, $attributes, 'displayEmployer', 'has-employer' );
     299    add_classname( $wrapper_classes, $attributes, 'displayDate', 'has-dates' );
     300    add_classname( $wrapper_classes, $attributes, 'displayDeadline', 'has-deadline' );
     301    add_classname( $wrapper_classes, $attributes, 'displayScope', 'has-scope' );
     302    add_classname( $wrapper_classes, $attributes, 'displayExcerpt', 'has-excerpts' );
     303
     304    $wrapper_attributes = get_block_wrapper_attributes( [
     305        'class'           => implode( ' ', $wrapper_classes ),
     306        'data-attributes' => esc_attr( json_encode( $attributes ) ),
     307    ] );
     308
     309    // Generate the ul classes (including grid classes)
     310    $ul_classes = [ 'wp-block-dss-jobbnorge' ];
     311    if ( 'grid' === $attributes[ 'blockLayout' ] ) {
     312        $ul_classes[] = 'is-grid';
     313        $ul_classes[] = 'columns-' . $attributes[ 'columns' ];
     314    }
     315
     316    // Generate pagination controls if enabled
     317    $pagination_html = '';
     318    if ( $attributes[ 'enablePagination' ] && count( $all_items ) > $attributes[ 'jobsPerPage' ] ) {
     319        $pagination_html = generate_pagination_controls( $current_page, count( $all_items ), $attributes[ 'jobsPerPage' ], $attributes );
     320    }
    258321
    259322    // Return the final HTML string, wrapping the list items in an unordered list.
    260     return sprintf( '<ul %s>%s</ul>', $wrapper_attributes, $list_items );
     323    return sprintf( '<div %s><ul class="%s">%s</ul>%s</div>', $wrapper_attributes, esc_attr( implode( ' ', $ul_classes ) ), $list_items, $pagination_html );
    261324}
    262325
     
    273336
    274337    // If the displayExcerpt attribute is true and the item has a summary, format the excerpt.
    275     if ( $attributes['displayExcerpt'] && isset( $item['summary'] ) ) {
     338    if ( $attributes[ 'displayExcerpt' ] && isset( $item[ 'summary' ] ) ) {
    276339        // Decode the HTML entities in the summary.
    277         $excerpt = html_entity_decode( $item['summary'], ENT_QUOTES, get_option( 'blog_charset' ) );
     340        $excerpt = html_entity_decode( $item[ 'summary' ], ENT_QUOTES, get_option( 'blog_charset' ) );
    278341        // Trim the excerpt to the excerptLength and escape it for safe use in HTML output.
    279         $excerpt = esc_attr( wp_trim_words( $excerpt, $attributes['excerptLength'], '' ) );
     342        $excerpt = esc_attr( wp_trim_words( $excerpt, $attributes[ 'excerptLength' ], '' ) );
    280343
    281344        // Format the read more link.
    282         $read_more = sprintf( ' ... <a href="%s">%s</a>', esc_url( $item['link'] ), __( 'Read more', 'wp-jobbnorge-block' ) );
     345        $read_more = sprintf( ' ... <a href="%s">%s</a>', esc_url( $item[ 'link' ] ), __( 'Read more', 'wp-jobbnorge-block' ) );
    283346
    284347        // Add the excerpt and read more link to the result string, wrapped in a div.
     
    338401        // Format the date according to the site's date format.
    339402        $str_date = date_i18n( get_option( 'date_format' ), $date );
    340     } catch ( \Exception $e ) {
     403    } catch (\Exception $e) {
    341404        // If there's an exception, fallback to the original date.
    342405        $str_date = $deadline_date;
     
    348411        return sprintf(
    349412            '<time datetime="%1$s" class="wp-block-dss-jobbnorge__item-deadline">%2$s %3$s</time> ',
    350             // If there's a parsed date, use it for the datetime attribute. Otherwise, leave it empty.
     413                // If there's a parsed date, use it for the datetime attribute. Otherwise, leave it empty.
    351414            ( $date ) ? esc_attr( wp_date( 'c', $date ) ) : '',
    352415            // Translate the 'Deadline:' string.
     
    406469function parse_date_fallback( $deadline_date ) {
    407470    // Define an array of month names in Norwegian.
    408     $str_months = [
     471    $str_months = [ 
    409472        'januar',
    410473        'februar',
     
    422485
    423486    // Define an array of month numbers.
    424     $num_months = [
     487    $num_months = [ 
    425488        '01',
    426489        '02',
     
    448511
    449512    // Return a Unix timestamp for the date.
    450     return mktime( 0, 0, 0, $dato_arr[2], $dato_arr[1], $dato_arr[3] );
     513    return mktime( 0, 0, 0, $dato_arr[ 2 ], $dato_arr[ 1 ], $dato_arr[ 3 ] );
    451514}
    452515
     
    466529    }
    467530}
     531
     532/**
     533 * Generates pagination controls for the job listings.
     534 *
     535 * @param int   $current_page The current page number.
     536 * @param int   $total_jobs   The total number of jobs.
     537 * @param int   $jobs_per_page The number of jobs per page.
     538 * @param array $attributes   The block attributes.
     539 * @return string The pagination HTML.
     540 */
     541function generate_pagination_controls( $current_page, $total_jobs, $jobs_per_page, $attributes ) {
     542    $total_pages = ceil( $total_jobs / $jobs_per_page );
     543
     544    if ( $total_pages <= 1 ) {
     545        return '';
     546    }
     547
     548    $prev_page = max( 1, $current_page - 1 );
     549    $next_page = min( $total_pages, $current_page + 1 );
     550
     551    // Calculate result range
     552    $start_item = ( ( $current_page - 1 ) * $jobs_per_page ) + 1;
     553    $end_item   = min( $current_page * $jobs_per_page, $total_jobs );
     554
     555    // Generate pagination HTML
     556    $pagination_html = '<nav class="wp-block-dss-jobbnorge__pagination" role="navigation" aria-label="' . esc_attr__( 'Job listings pagination', 'wp-jobbnorge-block' ) . '">';
     557
     558    // Results info
     559    $pagination_html .= sprintf(
     560        '<div class="wp-block-dss-jobbnorge__pagination-info">%s</div>',
     561        sprintf(
     562            esc_html__( 'Showing %d-%d of %d jobs', 'wp-jobbnorge-block' ),
     563            $start_item,
     564            $end_item,
     565            $total_jobs
     566        )
     567    );
     568
     569    // Pagination controls
     570    $pagination_html .= '<div class="wp-block-dss-jobbnorge__pagination-controls">';
     571
     572    // Previous button
     573    if ( $current_page > 1 ) {
     574        $pagination_html .= sprintf(
     575            '<button type="button" class="wp-block-dss-jobbnorge__pagination-prev" data-page="%d">%s</button>',
     576            $prev_page,
     577            esc_html__( 'Previous', 'wp-jobbnorge-block' )
     578        );
     579    } else {
     580        $pagination_html .= sprintf(
     581            '<button type="button" class="wp-block-dss-jobbnorge__pagination-prev" disabled>%s</button>',
     582            esc_html__( 'Previous', 'wp-jobbnorge-block' )
     583        );
     584    }
     585
     586    // Page info
     587    $pagination_html .= sprintf(
     588        '<span class="wp-block-dss-jobbnorge__pagination-info">%s</span>',
     589        sprintf(
     590            esc_html__( 'Page %d of %d', 'wp-jobbnorge-block' ),
     591            $current_page,
     592            $total_pages
     593        )
     594    );
     595
     596    // Next button
     597    if ( $current_page < $total_pages ) {
     598        $pagination_html .= sprintf(
     599            '<button type="button" class="wp-block-dss-jobbnorge__pagination-next" data-page="%d">%s</button>',
     600            $next_page,
     601            esc_html__( 'Next', 'wp-jobbnorge-block' )
     602        );
     603    } else {
     604        $pagination_html .= sprintf(
     605            '<button type="button" class="wp-block-dss-jobbnorge__pagination-next" disabled>%s</button>',
     606            esc_html__( 'Next', 'wp-jobbnorge-block' )
     607        );
     608    }
     609
     610    $pagination_html .= '</div>';
     611    $pagination_html .= '</nav>';
     612
     613    return $pagination_html;
     614}
     615
     616/**
     617 * Register AJAX endpoints for pagination.
     618 */
     619add_action( 'wp_ajax_jobbnorge_get_jobs', __NAMESPACE__ . '\handle_ajax_get_jobs' );
     620add_action( 'wp_ajax_nopriv_jobbnorge_get_jobs', __NAMESPACE__ . '\handle_ajax_get_jobs' );
     621
     622/**
     623 * Handle AJAX request for paginated job listings.
     624 */
     625function handle_ajax_get_jobs() {
     626    // Verify nonce
     627    if ( ! wp_verify_nonce( $_POST[ 'nonce' ], 'jobbnorge_pagination_nonce' ) ) {
     628        wp_die( 'Security check failed' );
     629    }
     630
     631    // Get and sanitize parameters
     632    $page       = isset( $_POST[ 'page' ] ) ? max( 1, intval( $_POST[ 'page' ] ) ) : 1;
     633    $attributes = isset( $_POST[ 'attributes' ] ) ? json_decode( stripslashes( $_POST[ 'attributes' ] ), true ) : [];
     634
     635    // Validate attributes
     636    if ( empty( $attributes ) || ! is_array( $attributes ) ) {
     637        wp_send_json_error( 'Invalid attributes' );
     638    }
     639
     640    // Set current page in GET superglobal for compatibility
     641    $_GET[ 'jobbnorge_page' ] = $page;
     642
     643    // Generate the job listings HTML
     644    $html = render_block_dss_jobbnorge( $attributes );
     645
     646    // Return JSON response
     647    wp_send_json_success( [ 'html' => $html ] );
     648}
     649
     650/**
     651 * Enqueue frontend JavaScript for pagination.
     652 */
     653function enqueue_pagination_script() {
     654    // Check if the block is being used on the current page
     655    if ( ! has_block( 'dss/jobbnorge' ) ) {
     656        return;
     657    }
     658
     659    // Define the path to the pagination dependencies file.
     660    $deps_file = plugin_dir_path( __FILE__ ) . 'build/pagination.asset.php';
     661
     662    // Initialize an array for JavaScript dependencies and a random version number.
     663    $jsdeps  = [];
     664    $version = wp_rand();
     665
     666    // Check if the dependencies file exists.
     667    if ( file_exists( $deps_file ) ) {
     668        // If it does, require it and merge its dependencies with the existing ones.
     669        $file   = require $deps_file;
     670        $jsdeps = array_merge( $jsdeps, $file[ 'dependencies' ] );
     671        // Also, set the version to the one from the file.
     672        $version = $file[ 'version' ];
     673    }
     674
     675    wp_enqueue_script(
     676        'jobbnorge-pagination',
     677        plugin_dir_url( __FILE__ ) . 'build/pagination.js',
     678        $jsdeps,
     679        $version,
     680        true
     681    );
     682
     683    // Localize script with AJAX URL and nonce
     684    wp_localize_script(
     685        'jobbnorge-pagination',
     686        'jobbnorgeAjax',
     687        [
     688            'ajaxUrl' => admin_url( 'admin-ajax.php' ),
     689            'nonce'   => wp_create_nonce( 'jobbnorge_pagination_nonce' ),
     690        ]
     691    );
     692}
     693
     694// Hook into wp_enqueue_scripts to add pagination script
     695add_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\enqueue_pagination_script' );
  • jobbnorge-block/trunk/CHANGELOG.md

    r3003492 r3322139  
    1 # Cangelog
     1# Changelog
     2
     3## 2.2.0
     4
     5* **NEW**: Add frontend pagination support with AJAX loading
     6* **NEW**: Add pagination controls (enable/disable, jobs per page setting)
     7* **ENHANCEMENT**: Upgrade to Jobbnorge API v3 for better performance
     8* **ENHANCEMENT**: Implement PHP-based pagination to work around API limitations with employer filtering
     9* **ENHANCEMENT**: Add responsive grid layout that adapts to screen size
     10* **ENHANCEMENT**: Improve cache key logic to include pagination and layout parameters
     11* **ENHANCEMENT**: Add loading states and error handling for pagination
     12* **ENHANCEMENT**: Separate frontend and admin CSS loading for better performance
     13* **FIX**: Fix CSS class naming conflicts that prevented grid view from working on frontend
     14* **FIX**: Resolve frontend style loading issues
     15* **DEVELOPER**: Add comprehensive webpack build configuration for multiple entry points
     16* **DEVELOPER**: Add pagination JavaScript with proper AJAX handling and nonce security
    217
    318## 2.1.5
  • jobbnorge-block/trunk/README.md

    r3002720 r3322139  
    55> Also available at https://wordpress.org/plugins/jobbnorge-block/
    66
     7
    78This WordPress plugin adds a block to the Gutenberg editor that displays a list of jobs from Jobbnorge.
    89
    9 See: [Features](#features) | [Installation](#installation) | [Use](#use) | [Filters](#filters) | [Styling](#styling) | [License](#license) | [Changelog](CHANGELOG.md)
     10**See:** [Live Preview](https://playground.wordpress.net/?plugin=jobbnorge-block&blueprint-url=https://wordpress.org/plugins/wp-json/plugins/v1/plugin/jobbnorge-block/blueprint.json) | [Features](#features) | [Installation](#installation) | [Use](#use) | [Filters](#filters) | [Styling](#styling) | [License](#license) | [Changelog](CHANGELOG.md)
    1011
     12<p>&nbsp;</p>
    1113
    1214<img src=".wordpress-org/jobbnorge.gif">
  • jobbnorge-block/trunk/build/block.json

    r3003492 r3322139  
    33  "apiVersion": 2,
    44  "name": "dss/jobbnorge",
    5   "version": "2.1.5",
     5  "version": "2.2.0",
    66  "title": "Jobbnorge",
    77  "category": "widgets",
     
    6363      "type": "number",
    6464      "default": 55
     65    },
     66    "enablePagination": {
     67      "type": "boolean",
     68      "default": true
     69    },
     70    "jobsPerPage": {
     71      "type": "number",
     72      "default": 10
    6573    }
    6674  },
    6775  "textdomain": "wp-jobbnorge-block",
    6876  "editorScript": "file:init.js",
    69   "editorStyle": "file:editor.scss",
    70   "style": "file:style.scss"
     77  "editorStyle": "file:editor.css",
     78  "style": "file:style-init.css"
    7179}
  • jobbnorge-block/trunk/build/init.asset.php

    r3003492 r3322139  
    1 <?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-server-side-render'), 'version' => '4d2818abc3875b8f27f4');
     1<?php return array('dependencies' => array('react', 'wp-block-editor', 'wp-blocks', 'wp-components', 'wp-element', 'wp-i18n', 'wp-primitives', 'wp-server-side-render'), 'version' => '7c82361092d9d86c438b');
  • jobbnorge-block/trunk/build/init.css

    r2853882 r3322139  
    1 .wp-block-dss-jobbnorge li a>div{display:inline}.wp-block-dss-jobbnorge__placeholder-form{align-items:stretch;display:flex}.wp-block-dss-jobbnorge__placeholder-form>*{margin-bottom:8px}@media(min-width:782px){.wp-block-dss-jobbnorge__placeholder-form>*{margin-bottom:0}}.wp-block-dss-jobbnorge__placeholder-input{align-items:stretch;display:flex;flex-grow:1}.wp-block-dss-jobbnorge__placeholder-input .components-base-control__field{align-items:stretch;display:flex;flex-grow:1;margin:0 8px 0 0}
     1.wp-block-dss-jobbnorge li a>div{display:inline}.wp-block-dss-jobbnorge__placeholder-form{align-items:stretch;display:flex}.wp-block-dss-jobbnorge__placeholder-form>*{margin-bottom:8px}@media(min-width:782px){.wp-block-dss-jobbnorge__placeholder-form>*{margin-bottom:0}}.wp-block-dss-jobbnorge__placeholder-input{align-items:stretch;display:flex;flex-grow:1}.wp-block-dss-jobbnorge__placeholder-input .components-base-control__field{align-items:stretch;display:flex;flex-grow:1;margin:0 8px 0 0}ul.wp-block-dss-jobbnorge{list-style:none;padding:0}ul.wp-block-dss-jobbnorge.wp-block-dss-jobbnorge{box-sizing:border-box}ul.wp-block-dss-jobbnorge li{margin:0 0 1em}ul.wp-block-dss-jobbnorge.is-grid{display:flex;flex-wrap:wrap;list-style:none;padding:0}ul.wp-block-dss-jobbnorge.is-grid li{margin:0 1em 1em 0;width:100%}@media(min-width:600px){ul.wp-block-dss-jobbnorge.columns-2 li{width:calc(50% - 1em)}ul.wp-block-dss-jobbnorge.columns-3 li{width:calc(33.33333% - 1em)}ul.wp-block-dss-jobbnorge.columns-4 li{width:calc(25% - 1em)}ul.wp-block-dss-jobbnorge.columns-5 li{width:calc(20% - 1em)}ul.wp-block-dss-jobbnorge.columns-6 li{width:calc(16.66667% - 1em)}}
  • jobbnorge-block/trunk/build/init.js

    r3003492 r3322139  
    1 !function(){"use strict";var e,o={938:function(e,o,t){var l=window.React,n=window.wp.primitives,r=(0,l.createElement)(n.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,l.createElement)(n.Path,{d:"M15.5 9.5a1 1 0 100-2 1 1 0 000 2zm0 1.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zm-2.25 6v-2a2.75 2.75 0 00-2.75-2.75h-4A2.75 2.75 0 003.75 15v2h1.5v-2c0-.69.56-1.25 1.25-1.25h4c.69 0 1.25.56 1.25 1.25v2h1.5zm7-2v2h-1.5v-2c0-.69-.56-1.25-1.25-1.25H15v-1.5h2.5A2.75 2.75 0 0120.25 15zM9.5 8.5a1 1 0 11-2 0 1 1 0 012 0zm1.5 0a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z",fillRule:"evenodd"})),a=window.wp.blocks,i=JSON.parse('{"$schema":"https://schemas.wp.org/trunk/block.json","apiVersion":2,"name":"dss/jobbnorge","version":"2.1.5","title":"Jobbnorge","category":"widgets","icon":"people","description":"Retrieve and display job listings from Jobbnorge.no","keywords":["jobbnorge","jobbnorge.no"],"supports":{"html":false},"attributes":{"columns":{"type":"number","default":3},"blockLayout":{"type":"string","default":"list"},"employerID":{"type":"string","default":""},"noJobsMessage":{"type":"string","default":""},"orderBy":{"type":"string","default":"Deadline"},"itemsToShow":{"type":"number","default":5},"displayEmployer":{"type":"boolean","default":false},"displayExcerpt":{"type":"boolean","default":true},"displayDeadline":{"type":"boolean","default":false},"displayScope":{"type":"boolean","default":false},"displayDate":{"type":"boolean","default":true},"excerptLength":{"type":"number","default":55}},"textdomain":"wp-jobbnorge-block","editorScript":"file:init.js","editorStyle":"file:editor.scss","style":"file:style.scss"}'),c=window.wp.blockEditor,s=window.wp.components,b=window.wp.element,p=(0,l.createElement)(n.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,l.createElement)(n.Path,{d:"m19 7-3-3-8.5 8.5-1 4 4-1L19 7Zm-7 11.5H5V20h7v-1.5Z"})),d=(0,l.createElement)(n.SVG,{viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},(0,l.createElement)(n.Path,{d:"M4 4v1.5h16V4H4zm8 8.5h8V11h-8v1.5zM4 20h16v-1.5H4V20zm4-8c0-1.1-.9-2-2-2s-2 .9-2 2 .9 2 2 2 2-.9 2-2z"})),m=(0,l.createElement)(n.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,l.createElement)(n.Path,{d:"m3 5c0-1.10457.89543-2 2-2h13.5c1.1046 0 2 .89543 2 2v13.5c0 1.1046-.8954 2-2 2h-13.5c-1.10457 0-2-.8954-2-2zm2-.5h6v6.5h-6.5v-6c0-.27614.22386-.5.5-.5zm-.5 8v6c0 .2761.22386.5.5.5h6v-6.5zm8 0v6.5h6c.2761 0 .5-.2239.5-.5v-6zm0-8v6.5h6.5v-6c0-.27614-.2239-.5-.5-.5z",fillRule:"evenodd",clipRule:"evenodd"})),u=window.wp.i18n,g=window.wp.serverSideRender,w=t.n(g);const{name:v}=i;(e=>{const{metadata:o,settings:t,name:l}=e;(0,a.registerBlockType)({name:l,...o},t)})({name:v,metadata:i,settings:{icon:r,example:{attributes:{employerID:"123[, 456, 789]"}},edit:function({attributes:e,setAttributes:o}){const[t,n]=(0,b.useState)(!e.employerID),{blockLayout:a,columns:i,displayScope:g,displayDate:v,displayEmployer:y,displayExcerpt:h,employerID:f,itemsToShow:k,noJobsMessage:_,orderBy:E}=e;function j(t){return()=>{const l=e[t];o({[t]:!l})}}const C=(0,c.useBlockProps)();var x;if(t)return(0,l.createElement)("div",{...C},(0,l.createElement)(s.Placeholder,{icon:r,label:"Jobbnorge"},(0,l.createElement)("form",{onSubmit:function(e){e.preventDefault(),f&&(o({employerID:f}),n(!1))},className:"wp-block-dss-jobbnorge__placeholder-form"},window.wpJobbnorgeBlock&&window.wpJobbnorgeBlock.employers?(0,l.createElement)(s.SelectControl,{multiple:!0,value:f.split(","),onChange:e=>o({employerID:e.toString()}),options:(null!==(x=wpJobbnorgeBlock.employers)&&void 0!==x?x:[]).map((e=>{var o;return{label:e.label,value:e.value,disabled:null!==(o=e?.disabled)&&void 0!==o&&o}})),className:"wp-block-dss-jobbnorge__placeholder-input",help:(0,u.__)("Select employers to display. Ctrl-click (Windows) or Cmd-click (Mac) to select multiple employers. Shift-click to select a range of employers.","wp-jobbnorge-block"),__nextHasNoMarginBottom:!0}):(0,l.createElement)(s.TextControl,{placeholder:(0,u.__)("Employer ID [,id2, id3, ..]","wp-jobbnorge-block"),value:f,onChange:e=>o({employerID:e}),className:"wp-block-dss-jobbnorge__placeholder-input"}),(0,l.createElement)(s.Button,{variant:"primary",type:"submit"},(0,u.__)("Save","wp-jobbnorge-block")))));const S=[{icon:p,title:(0,u.__)("Edit Jobbnorge URL","wp-jobbnorge-block"),onClick:()=>n(!0)},{icon:d,title:(0,u.__)("List view","wp-jobbnorge-block"),onClick:()=>o({blockLayout:"list"}),isActive:"list"===a},{icon:m,title:(0,u.__)("Grid view","wp-jobbnorge-block"),onClick:()=>o({blockLayout:"grid"}),isActive:"grid"===a}];return(0,l.createElement)(l.Fragment,null,(0,l.createElement)(c.BlockControls,null,(0,l.createElement)(s.ToolbarGroup,{controls:S})),(0,l.createElement)(c.InspectorControls,null,(0,l.createElement)(s.PanelBody,{title:(0,u.__)("Settings","wp-jobbnorge-block")},(0,l.createElement)(s.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,u.__)("Number of items","wp-jobbnorge-block"),value:k,onChange:e=>o({itemsToShow:e}),min:1,max:100,required:!0}),f.includes(",")&&(0,l.createElement)(s.RadioControl,{label:(0,u.__)("Order by","wp-jobbnorge-block"),selected:E,options:[{label:(0,u.__)("Deadline","wp-jobbnorge-block"),value:"Deadline"},{label:(0,u.__)("Employer","wp-jobbnorge-block"),value:"Employer"}],onChange:e=>o({orderBy:e})}),(0,l.createElement)(s.TextareaControl,{label:(0,u.__)("No jobs found message","wp-jobbnorge-block"),help:(0,u.__)("Message to display if no jobs are found","wp-jobbnorge-block"),value:_||(0,u.__)("There are no jobs at this time.","wp-jobbnorge-block"),onChange:e=>o({noJobsMessage:e})})),(0,l.createElement)(s.PanelBody,{title:(0,u.__)("Item","wp-jobbnorge-block")},(0,l.createElement)(s.ToggleControl,{label:(0,u.__)("Display employer","wp-jobbnorge-block"),checked:y,onChange:j("displayEmployer")}),(0,l.createElement)(s.ToggleControl,{label:(0,u.__)("Display excerpt","wp-jobbnorge-block"),checked:h,onChange:j("displayExcerpt")}),(0,l.createElement)(s.ToggleControl,{label:(0,u.__)("Display deadline","wp-jobbnorge-block"),checked:v,onChange:j("displayDate")}),(0,l.createElement)(s.ToggleControl,{label:(0,u.__)("Display scope","wp-jobbnorge-block"),checked:g,onChange:j("displayScope")})),"grid"===a&&(0,l.createElement)(s.PanelBody,{title:(0,u.__)("Grid view","wp-jobbnorge-block")},(0,l.createElement)(s.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,u.__)("Columns","wp-jobbnorge-block"),value:i,onChange:e=>o({columns:e}),min:2,max:6,required:!0}))),(0,l.createElement)("div",{...C},(0,l.createElement)(s.Disabled,null,(0,l.createElement)(w(),{block:"dss/jobbnorge",attributes:e,httpMethod:"POST"}))))}}})}},t={};function l(e){var n=t[e];if(void 0!==n)return n.exports;var r=t[e]={exports:{}};return o[e](r,r.exports,l),r.exports}l.m=o,e=[],l.O=function(o,t,n,r){if(!t){var a=1/0;for(b=0;b<e.length;b++){t=e[b][0],n=e[b][1],r=e[b][2];for(var i=!0,c=0;c<t.length;c++)(!1&r||a>=r)&&Object.keys(l.O).every((function(e){return l.O[e](t[c])}))?t.splice(c--,1):(i=!1,r<a&&(a=r));if(i){e.splice(b--,1);var s=n();void 0!==s&&(o=s)}}return o}r=r||0;for(var b=e.length;b>0&&e[b-1][2]>r;b--)e[b]=e[b-1];e[b]=[t,n,r]},l.n=function(e){var o=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(o,{a:o}),o},l.d=function(e,o){for(var t in o)l.o(o,t)&&!l.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:o[t]})},l.o=function(e,o){return Object.prototype.hasOwnProperty.call(e,o)},function(){var e={410:0,308:0};l.O.j=function(o){return 0===e[o]};var o=function(o,t){var n,r,a=t[0],i=t[1],c=t[2],s=0;if(a.some((function(o){return 0!==e[o]}))){for(n in i)l.o(i,n)&&(l.m[n]=i[n]);if(c)var b=c(l)}for(o&&o(t);s<a.length;s++)r=a[s],l.o(e,r)&&e[r]&&e[r][0](),e[r]=0;return l.O(b)},t=self.webpackChunkjobbnorge_block=self.webpackChunkjobbnorge_block||[];t.forEach(o.bind(null,0)),t.push=o.bind(null,t.push.bind(t))}();var n=l.O(void 0,[308],(function(){return l(938)}));n=l.O(n)}();
     1!function(){"use strict";var e,o={938:function(e,o,t){var n=window.React,l=window.wp.primitives,r=(0,n.createElement)(l.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,n.createElement)(l.Path,{d:"M15.5 9.5a1 1 0 100-2 1 1 0 000 2zm0 1.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zm-2.25 6v-2a2.75 2.75 0 00-2.75-2.75h-4A2.75 2.75 0 003.75 15v2h1.5v-2c0-.69.56-1.25 1.25-1.25h4c.69 0 1.25.56 1.25 1.25v2h1.5zm7-2v2h-1.5v-2c0-.69-.56-1.25-1.25-1.25H15v-1.5h2.5A2.75 2.75 0 0120.25 15zM9.5 8.5a1 1 0 11-2 0 1 1 0 012 0zm1.5 0a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z",fillRule:"evenodd"})),a=window.wp.blocks,i=JSON.parse('{"$schema":"https://schemas.wp.org/trunk/block.json","apiVersion":2,"name":"dss/jobbnorge","version":"2.2.0","title":"Jobbnorge","category":"widgets","icon":"people","description":"Retrieve and display job listings from Jobbnorge.no","keywords":["jobbnorge","jobbnorge.no"],"supports":{"html":false},"attributes":{"columns":{"type":"number","default":3},"blockLayout":{"type":"string","default":"list"},"employerID":{"type":"string","default":""},"noJobsMessage":{"type":"string","default":""},"orderBy":{"type":"string","default":"Deadline"},"itemsToShow":{"type":"number","default":5},"displayEmployer":{"type":"boolean","default":false},"displayExcerpt":{"type":"boolean","default":true},"displayDeadline":{"type":"boolean","default":false},"displayScope":{"type":"boolean","default":false},"displayDate":{"type":"boolean","default":true},"excerptLength":{"type":"number","default":55},"enablePagination":{"type":"boolean","default":true},"jobsPerPage":{"type":"number","default":10}},"textdomain":"wp-jobbnorge-block","editorScript":"file:init.js","editorStyle":"file:editor.css","style":"file:style-init.css"}'),c=window.wp.blockEditor,b=window.wp.components,s=window.wp.element,p=(0,n.createElement)(l.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,n.createElement)(l.Path,{d:"m19 7-3-3-8.5 8.5-1 4 4-1L19 7Zm-7 11.5H5V20h7v-1.5Z"})),d=(0,n.createElement)(l.SVG,{viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},(0,n.createElement)(l.Path,{d:"M4 4v1.5h16V4H4zm8 8.5h8V11h-8v1.5zM4 20h16v-1.5H4V20zm4-8c0-1.1-.9-2-2-2s-2 .9-2 2 .9 2 2 2 2-.9 2-2z"})),m=(0,n.createElement)(l.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24"},(0,n.createElement)(l.Path,{d:"m3 5c0-1.10457.89543-2 2-2h13.5c1.1046 0 2 .89543 2 2v13.5c0 1.1046-.8954 2-2 2h-13.5c-1.10457 0-2-.8954-2-2zm2-.5h6v6.5h-6.5v-6c0-.27614.22386-.5.5-.5zm-.5 8v6c0 .2761.22386.5.5.5h6v-6.5zm8 0v6.5h6c.2761 0 .5-.2239.5-.5v-6zm0-8v6.5h6.5v-6c0-.27614-.2239-.5-.5-.5z",fillRule:"evenodd",clipRule:"evenodd"})),u=window.wp.i18n,g=window.wp.serverSideRender,w=t.n(g);const{name:h}=i;(e=>{const{metadata:o,settings:t,name:n}=e;(0,a.registerBlockType)({name:n,...o},t)})({name:h,metadata:i,settings:{icon:r,example:{attributes:{employerID:"123[, 456, 789]"}},edit:function({attributes:e,setAttributes:o}){const[t,l]=(0,s.useState)(!e.employerID),{blockLayout:a,columns:i,displayScope:g,displayDate:h,displayEmployer:y,displayExcerpt:v,employerID:f,itemsToShow:_,noJobsMessage:k,orderBy:j,enablePagination:E,jobsPerPage:C}=e;function x(t){return()=>{const n=e[t];o({[t]:!n})}}const S=(0,c.useBlockProps)();var B;if(t)return(0,n.createElement)("div",{...S},(0,n.createElement)(b.Placeholder,{icon:r,label:"Jobbnorge"},(0,n.createElement)("form",{onSubmit:function(e){e.preventDefault(),f&&(o({employerID:f}),l(!1))},className:"wp-block-dss-jobbnorge__placeholder-form"},window.wpJobbnorgeBlock&&window.wpJobbnorgeBlock.employers?(0,n.createElement)(b.SelectControl,{multiple:!0,value:f.split(","),onChange:e=>o({employerID:e.toString()}),options:(null!==(B=wpJobbnorgeBlock.employers)&&void 0!==B?B:[]).map((e=>{var o;return{label:e.label,value:e.value,disabled:null!==(o=e?.disabled)&&void 0!==o&&o}})),className:"wp-block-dss-jobbnorge__placeholder-input",help:(0,u.__)("Select employers to display. Ctrl-click (Windows) or Cmd-click (Mac) to select multiple employers. Shift-click to select a range of employers.","wp-jobbnorge-block"),__nextHasNoMarginBottom:!0}):(0,n.createElement)(b.TextControl,{placeholder:(0,u.__)("Employer ID [,id2, id3, ..]","wp-jobbnorge-block"),value:f,onChange:e=>o({employerID:e}),className:"wp-block-dss-jobbnorge__placeholder-input"}),(0,n.createElement)(b.Button,{variant:"primary",type:"submit"},(0,u.__)("Save","wp-jobbnorge-block")))));const D=[{icon:p,title:(0,u.__)("Edit Jobbnorge URL","wp-jobbnorge-block"),onClick:()=>l(!0)},{icon:d,title:(0,u.__)("List view","wp-jobbnorge-block"),onClick:()=>o({blockLayout:"list"}),isActive:"list"===a},{icon:m,title:(0,u.__)("Grid view","wp-jobbnorge-block"),onClick:()=>o({blockLayout:"grid"}),isActive:"grid"===a}];return(0,n.createElement)(n.Fragment,null,(0,n.createElement)(c.BlockControls,null,(0,n.createElement)(b.ToolbarGroup,{controls:D})),(0,n.createElement)(c.InspectorControls,null,(0,n.createElement)(b.PanelBody,{title:(0,u.__)("Settings","wp-jobbnorge-block")},(0,n.createElement)(b.ToggleControl,{label:(0,u.__)("Enable pagination","wp-jobbnorge-block"),help:(0,u.__)("When enabled, all jobs will be displayed with pagination controls. When disabled, only the specified number of jobs will be shown.","wp-jobbnorge-block"),checked:E,onChange:e=>o({enablePagination:e})}),!E&&(0,n.createElement)(b.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,u.__)("Number of items","wp-jobbnorge-block"),value:_,onChange:e=>o({itemsToShow:e}),min:1,max:100,required:!0}),E&&(0,n.createElement)(b.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,u.__)("Jobs per page","wp-jobbnorge-block"),value:C,onChange:e=>o({jobsPerPage:e}),min:1,max:50,required:!0}),f.includes(",")&&(0,n.createElement)(b.RadioControl,{label:(0,u.__)("Order by","wp-jobbnorge-block"),selected:j,options:[{label:(0,u.__)("Deadline","wp-jobbnorge-block"),value:"Deadline"},{label:(0,u.__)("Employer","wp-jobbnorge-block"),value:"Employer"}],onChange:e=>o({orderBy:e})}),(0,n.createElement)(b.TextareaControl,{label:(0,u.__)("No jobs found message","wp-jobbnorge-block"),help:(0,u.__)("Message to display if no jobs are found","wp-jobbnorge-block"),value:k||(0,u.__)("There are no jobs at this time.","wp-jobbnorge-block"),onChange:e=>o({noJobsMessage:e})})),(0,n.createElement)(b.PanelBody,{title:(0,u.__)("Item","wp-jobbnorge-block")},(0,n.createElement)(b.ToggleControl,{label:(0,u.__)("Display employer","wp-jobbnorge-block"),checked:y,onChange:x("displayEmployer")}),(0,n.createElement)(b.ToggleControl,{label:(0,u.__)("Display excerpt","wp-jobbnorge-block"),checked:v,onChange:x("displayExcerpt")}),(0,n.createElement)(b.ToggleControl,{label:(0,u.__)("Display deadline","wp-jobbnorge-block"),checked:h,onChange:x("displayDate")}),(0,n.createElement)(b.ToggleControl,{label:(0,u.__)("Display scope","wp-jobbnorge-block"),checked:g,onChange:x("displayScope")})),"grid"===a&&(0,n.createElement)(b.PanelBody,{title:(0,u.__)("Grid view","wp-jobbnorge-block")},(0,n.createElement)(b.RangeControl,{__nextHasNoMarginBottom:!0,label:(0,u.__)("Columns","wp-jobbnorge-block"),value:i,onChange:e=>o({columns:e}),min:2,max:6,required:!0}))),(0,n.createElement)("div",{...S},(0,n.createElement)(b.Disabled,null,(0,n.createElement)(w(),{block:"dss/jobbnorge",attributes:e,httpMethod:"POST"}))))}}})}},t={};function n(e){var l=t[e];if(void 0!==l)return l.exports;var r=t[e]={exports:{}};return o[e](r,r.exports,n),r.exports}n.m=o,e=[],n.O=function(o,t,l,r){if(!t){var a=1/0;for(s=0;s<e.length;s++){t=e[s][0],l=e[s][1],r=e[s][2];for(var i=!0,c=0;c<t.length;c++)(!1&r||a>=r)&&Object.keys(n.O).every((function(e){return n.O[e](t[c])}))?t.splice(c--,1):(i=!1,r<a&&(a=r));if(i){e.splice(s--,1);var b=l();void 0!==b&&(o=b)}}return o}r=r||0;for(var s=e.length;s>0&&e[s-1][2]>r;s--)e[s]=e[s-1];e[s]=[t,l,r]},n.n=function(e){var o=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(o,{a:o}),o},n.d=function(e,o){for(var t in o)n.o(o,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:o[t]})},n.o=function(e,o){return Object.prototype.hasOwnProperty.call(e,o)},function(){var e={410:0,308:0};n.O.j=function(o){return 0===e[o]};var o=function(o,t){var l,r,a=t[0],i=t[1],c=t[2],b=0;if(a.some((function(o){return 0!==e[o]}))){for(l in i)n.o(i,l)&&(n.m[l]=i[l]);if(c)var s=c(n)}for(o&&o(t);b<a.length;b++)r=a[b],n.o(e,r)&&e[r]&&e[r][0](),e[r]=0;return n.O(s)},t=self.webpackChunkjobbnorge_block=self.webpackChunkjobbnorge_block||[];t.forEach(o.bind(null,0)),t.push=o.bind(null,t.push.bind(t))}();var l=n.O(void 0,[308],(function(){return n(938)}));l=n.O(l)}();
  • jobbnorge-block/trunk/build/style-init.css

    r2997962 r3322139  
    1 ul.wp-block-dss-jobbnorge{list-style:none;padding:0}ul.wp-block-dss-jobbnorge.wp-block-dss-jobbnorge{box-sizing:border-box}ul.wp-block-dss-jobbnorge.alignleft{margin-right:2em}ul.wp-block-dss-jobbnorge.alignright{margin-left:2em}ul.wp-block-dss-jobbnorge li{margin:0 0 1em}ul.wp-block-dss-jobbnorge.is-grid{display:flex;flex-wrap:wrap;list-style:none;padding:0}ul.wp-block-dss-jobbnorge.is-grid li{margin:0 1em 1em 0;width:100%}@media(min-width:600px){ul.wp-block-dss-jobbnorge.columns-2 li{width:calc(50% - 1em)}ul.wp-block-dss-jobbnorge.columns-3 li{width:calc(33.33333% - 1em)}ul.wp-block-dss-jobbnorge.columns-4 li{width:calc(25% - 1em)}ul.wp-block-dss-jobbnorge.columns-5 li{width:calc(20% - 1em)}ul.wp-block-dss-jobbnorge.columns-6 li{width:calc(16.66667% - 1em)}}.wp-block-dss-jobbnorge__item-title{font-size:1.125em;font-weight:600;margin:0 0 .25em}.wp-block-dss-jobbnorge__item-meta{margin:0 0 .25em;padding:0}.wp-block-dss-jobbnorge__item-deadline,.wp-block-dss-jobbnorge__item-employer,.wp-block-dss-jobbnorge__item-scope{display:block;font-size:.8125em;font-weight:600}
     1ul.wp-block-dss-jobbnorge{list-style:none;padding:0}ul.wp-block-dss-jobbnorge.wp-block-dss-jobbnorge{box-sizing:border-box}ul.wp-block-dss-jobbnorge.alignleft{margin-right:2em}ul.wp-block-dss-jobbnorge.alignright{margin-left:2em}ul.wp-block-dss-jobbnorge li{margin:0 0 1em}ul.wp-block-dss-jobbnorge.is-grid{display:flex;flex-wrap:wrap;list-style:none;padding:0}ul.wp-block-dss-jobbnorge.is-grid li{margin:0 1em 1em 0;width:100%}@media(min-width:600px){ul.wp-block-dss-jobbnorge.columns-2 li{width:calc(50% - 1em)}ul.wp-block-dss-jobbnorge.columns-3 li{width:calc(33.33333% - 1em)}ul.wp-block-dss-jobbnorge.columns-4 li{width:calc(25% - 1em)}ul.wp-block-dss-jobbnorge.columns-5 li{width:calc(20% - 1em)}ul.wp-block-dss-jobbnorge.columns-6 li{width:calc(16.66667% - 1em)}}.wp-block-dss-jobbnorge__item-title{font-size:1.125em;font-weight:600;margin:0 0 .25em}.wp-block-dss-jobbnorge__item-meta{margin:0 0 .25em;padding:0}.wp-block-dss-jobbnorge__item-deadline,.wp-block-dss-jobbnorge__item-employer,.wp-block-dss-jobbnorge__item-scope{display:block;font-size:.8125em;font-weight:600}.wp-block-dss-jobbnorge__pagination{border-top:1px solid #e0e0e0;display:flex;flex-direction:column;gap:1rem;margin-top:2rem;padding:1rem 0}@media(min-width:600px){.wp-block-dss-jobbnorge__pagination{align-items:center;flex-direction:row;justify-content:space-between}}.wp-block-dss-jobbnorge__pagination-info{color:#666;font-size:.875rem;margin:0}.wp-block-dss-jobbnorge__pagination-controls{align-items:center;display:flex;gap:.5rem}.wp-block-dss-jobbnorge__pagination-controls button{background:#fff;border:1px solid #ddd;border-radius:4px;cursor:pointer;font-size:.875rem;padding:.5rem 1rem;transition:all .2s ease}.wp-block-dss-jobbnorge__pagination-controls button:hover:not(:disabled){background:#f5f5f5;border-color:#999}.wp-block-dss-jobbnorge__pagination-controls button:disabled{cursor:not-allowed;opacity:.5}.wp-block-dss-jobbnorge__pagination-controls .wp-block-dss-jobbnorge__pagination-info{color:#333;font-size:.875rem;margin:0 .5rem}.wp-block-dss-jobbnorge__loading{opacity:.6;pointer-events:none}.wp-block-dss-jobbnorge__loading:after{animation:spin 1s linear infinite;border:2px solid #ccc;border-radius:50%;border-top-color:#333;content:"";height:20px;left:50%;margin:-10px 0 0 -10px;position:absolute;top:50%;width:20px}.wp-block-dss-jobbnorge__error{background:#ffebe8;border:1px solid #d63638;border-radius:4px;color:#d63638;margin:1rem 0;padding:.75rem}.wp-block-dss-jobbnorge__error p{margin:0}@keyframes spin{to{transform:rotate(1turn)}}
  • jobbnorge-block/trunk/languages/wp-jobbnorge-block-en_US-5847f2cef22ef3cae0e9359a7a5dd2ec.json

    r3002720 r3322139  
    11{
    22    "translation-revision-date": "YEAR-MO-DA HO:MI+ZONE",
    3     "generator": "WP-CLI\/2.9.0",
     3    "generator": "WP-CLI\/2.12.0",
    44    "source": "build\/init.js",
    55    "domain": "messages",
     
    3535                ""
    3636            ],
     37            "Enable pagination": [
     38                ""
     39            ],
     40            "When enabled, all jobs will be displayed with pagination controls. When disabled, only the specified number of jobs will be shown.": [
     41                ""
     42            ],
    3743            "Number of items": [
     44                ""
     45            ],
     46            "Jobs per page": [
    3847                ""
    3948            ],
  • jobbnorge-block/trunk/languages/wp-jobbnorge-block-en_US-bb1d7dea005e67527e728d4801f74b61.json

    r3002720 r3322139  
    11{
    22    "translation-revision-date": "YEAR-MO-DA HO:MI+ZONE",
    3     "generator": "WP-CLI\/2.9.0",
     3    "generator": "WP-CLI\/2.12.0",
    44    "source": "src\/edit.js",
    55    "domain": "messages",
     
    3535                ""
    3636            ],
     37            "Enable pagination": [
     38                ""
     39            ],
     40            "When enabled, all jobs will be displayed with pagination controls. When disabled, only the specified number of jobs will be shown.": [
     41                ""
     42            ],
    3743            "Number of items": [
     44                ""
     45            ],
     46            "Jobs per page": [
    3847                ""
    3948            ],
  • jobbnorge-block/trunk/languages/wp-jobbnorge-block-en_US.po

    r3002720 r3322139  
    1 # Copyright (C) 2023 PerS
     1# Copyright (C) 2025 PerS
    22# This file is distributed under the GPL-2.0-or-later.
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: Jobbnorge Block 2.1.4\n"
     5"Project-Id-Version: Jobbnorge Block 2.2.0\n"
    66"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/jobbnorge-block\n"
    77"Last-Translator: Per Søderlind <[email protected]\n"
     
    1010"Content-Type: text/plain; charset=UTF-8\n"
    1111"Content-Transfer-Encoding: 8bit\n"
    12 "POT-Creation-Date: 2023-11-28T12:11:37+00:00\n"
     12"POT-Creation-Date: 2025-07-03T21:16:21+00:00\n"
    1313"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    14 "X-Generator: WP-CLI 2.9.0\n"
     14"X-Generator: WP-CLI 2.12.0\n"
    1515"X-Domain: wp-jobbnorge-block\n"
    1616
    1717#. Plugin Name of the plugin
     18#: wp-jobb-norge.php
    1819msgid "Jobbnorge Block"
    1920msgstr ""
    2021
    2122#. Plugin URI of the plugin
     23#: wp-jobb-norge.php
    2224msgid "https://wordpress.org/plugins/jobbnorge-block/"
    2325msgstr ""
    2426
    2527#. Description of the plugin
     28#: wp-jobb-norge.php
    2629msgid "Retrieve and display job listings from Jobbnorge.no"
    2730msgstr ""
    2831
    2932#. Author of the plugin
     33#: wp-jobb-norge.php
    3034msgid "PerS"
    3135msgstr ""
    3236
    33 #: wp-jobb-norge.php:159
     37#: wp-jobb-norge.php:181
    3438msgid "Invalid ID"
    3539msgstr ""
    3640
    37 #: wp-jobb-norge.php:181
     41#: wp-jobb-norge.php:212
    3842msgid "Error connecting to Jobbnorge.no"
    3943msgstr ""
    4044
    41 #: wp-jobb-norge.php:195
     45#: wp-jobb-norge.php:251
    4246msgid "No jobs found"
    4347msgstr ""
    4448
    45 #: wp-jobb-norge.php:205
     49#: wp-jobb-norge.php:261
    4650msgid "(no title)"
    4751msgstr ""
    4852
    49 #: wp-jobb-norge.php:226
    50 #: build/init.js:1
    51 #: src/edit.js:160
     53#: wp-jobb-norge.php:282
     54#: build/init.js:1
     55#: src/edit.js:181
    5256msgid "Employer"
    5357msgstr ""
    5458
    55 #: wp-jobb-norge.php:227
     59#: wp-jobb-norge.php:283
    5660msgid "Scope"
    5761msgstr ""
    5862
    59 #: wp-jobb-norge.php:282
     63#: wp-jobb-norge.php:345
    6064msgid "Read more"
    6165msgstr ""
    6266
    63 #: wp-jobb-norge.php:353
     67#: wp-jobb-norge.php:416
    6468msgid "Deadline:"
    6569msgstr ""
    6670
    67 #: build/init.js:1
    68 #: src/edit.js:95
     71#: wp-jobb-norge.php:556
     72msgid "Job listings pagination"
     73msgstr ""
     74
     75#: wp-jobb-norge.php:562
     76#, php-format
     77msgid "Showing %d-%d of %d jobs"
     78msgstr ""
     79
     80#: wp-jobb-norge.php:577
     81#: wp-jobb-norge.php:582
     82msgid "Previous"
     83msgstr ""
     84
     85#: wp-jobb-norge.php:590
     86#, php-format
     87msgid "Page %d of %d"
     88msgstr ""
     89
     90#: wp-jobb-norge.php:601
     91#: wp-jobb-norge.php:606
     92msgid "Next"
     93msgstr ""
     94
     95#: build/init.js:1
     96#: src/edit.js:97
    6997msgid "Select employers to display. Ctrl-click (Windows) or Cmd-click (Mac) to select multiple employers. Shift-click to select a range of employers."
    7098msgstr ""
    7199
    72100#: build/init.js:1
    73 #: src/edit.js:103
     101#: src/edit.js:105
    74102msgid "Employer ID [,id2, id3, ..]"
    75103msgstr ""
    76104
    77105#: build/init.js:1
    78 #: src/edit.js:110
     106#: src/edit.js:112
    79107msgid "Save"
    80108msgstr ""
    81109
    82110#: build/init.js:1
    83 #: src/edit.js:121
     111#: src/edit.js:123
    84112msgid "Edit Jobbnorge URL"
    85113msgstr ""
    86114
    87115#: build/init.js:1
    88 #: src/edit.js:126
     116#: src/edit.js:128
    89117msgid "List view"
    90118msgstr ""
    91119
    92120#: build/init.js:1
    93 #: src/edit.js:132
     121#: src/edit.js:134
     122#: src/edit.js:216
     123msgid "Grid view"
     124msgstr ""
     125
     126#: build/init.js:1
     127#: src/edit.js:146
     128msgid "Settings"
     129msgstr ""
     130
     131#: build/init.js:1
     132#: src/edit.js:148
     133msgid "Enable pagination"
     134msgstr ""
     135
     136#: build/init.js:1
     137#: src/edit.js:149
     138msgid "When enabled, all jobs will be displayed with pagination controls. When disabled, only the specified number of jobs will be shown."
     139msgstr ""
     140
     141#: build/init.js:1
     142#: src/edit.js:156
     143msgid "Number of items"
     144msgstr ""
     145
     146#: build/init.js:1
     147#: src/edit.js:167
     148msgid "Jobs per page"
     149msgstr ""
     150
     151#: build/init.js:1
     152#: src/edit.js:177
     153msgid "Order by"
     154msgstr ""
     155
     156#: build/init.js:1
     157#: src/edit.js:180
     158msgid "Deadline"
     159msgstr ""
     160
     161#: build/init.js:1
     162#: src/edit.js:187
     163msgid "No jobs found message"
     164msgstr ""
     165
     166#: build/init.js:1
     167#: src/edit.js:188
     168msgid "Message to display if no jobs are found"
     169msgstr ""
     170
     171#: build/init.js:1
     172#: src/edit.js:189
     173msgid "There are no jobs at this time."
     174msgstr ""
     175
     176#: build/init.js:1
     177#: src/edit.js:193
     178msgid "Item"
     179msgstr ""
     180
     181#: build/init.js:1
    94182#: src/edit.js:195
    95 msgid "Grid view"
    96 msgstr ""
    97 
    98 #: build/init.js:1
    99 #: src/edit.js:144
    100 msgid "Settings"
    101 msgstr ""
    102 
    103 #: build/init.js:1
    104 #: src/edit.js:147
    105 msgid "Number of items"
    106 msgstr ""
    107 
    108 #: build/init.js:1
    109 #: src/edit.js:156
    110 msgid "Order by"
    111 msgstr ""
    112 
    113 #: build/init.js:1
    114 #: src/edit.js:159
    115 msgid "Deadline"
    116 msgstr ""
    117 
    118 #: build/init.js:1
    119 #: src/edit.js:166
    120 msgid "No jobs found message"
    121 msgstr ""
    122 
    123 #: build/init.js:1
    124 #: src/edit.js:167
    125 msgid "Message to display if no jobs are found"
    126 msgstr ""
    127 
    128 #: build/init.js:1
    129 #: src/edit.js:168
    130 msgid "There are no jobs at this time."
    131 msgstr ""
    132 
    133 #: build/init.js:1
    134 #: src/edit.js:172
    135 msgid "Item"
    136 msgstr ""
    137 
    138 #: build/init.js:1
    139 #: src/edit.js:174
    140183msgid "Display employer"
    141184msgstr ""
    142185
    143186#: build/init.js:1
    144 #: src/edit.js:179
     187#: src/edit.js:200
    145188msgid "Display excerpt"
    146189msgstr ""
    147190
    148191#: build/init.js:1
    149 #: src/edit.js:184
     192#: src/edit.js:205
    150193msgid "Display deadline"
    151194msgstr ""
    152195
    153196#: build/init.js:1
    154 #: src/edit.js:189
     197#: src/edit.js:210
    155198msgid "Display scope"
    156199msgstr ""
    157200
    158201#: build/init.js:1
    159 #: src/edit.js:198
     202#: src/edit.js:219
    160203msgid "Columns"
    161204msgstr ""
  • jobbnorge-block/trunk/languages/wp-jobbnorge-block-nb_NO-5847f2cef22ef3cae0e9359a7a5dd2ec.json

    r3002720 r3322139  
    11{
    2     "translation-revision-date": "2023-11-28 13:09+0100",
    3     "generator": "WP-CLI\/2.9.0",
     2    "translation-revision-date": "2025-07-03 23:16+0200",
     3    "generator": "WP-CLI\/2.12.0",
    44    "source": "build\/init.js",
    55    "domain": "messages",
     
    3535                "Innstillinger"
    3636            ],
     37            "Enable pagination": [
     38                "Aktiver paginering"
     39            ],
     40            "When enabled, all jobs will be displayed with pagination controls. When disabled, only the specified number of jobs will be shown.": [
     41                "N\u00e5r denne funksjonen er aktivert, vises alle jobber med pagineringskontroller. N\u00e5r den er deaktivert, vises bare det angitte antallet jobber."
     42            ],
    3743            "Number of items": [
    3844                "Antall oppf\u00f8ringer"
     45            ],
     46            "Jobs per page": [
     47                "Jobber per side"
    3948            ],
    4049            "Order by": [
  • jobbnorge-block/trunk/languages/wp-jobbnorge-block-nb_NO-bb1d7dea005e67527e728d4801f74b61.json

    r3002720 r3322139  
    11{
    2     "translation-revision-date": "2023-11-28 13:09+0100",
    3     "generator": "WP-CLI\/2.9.0",
     2    "translation-revision-date": "2025-07-03 23:16+0200",
     3    "generator": "WP-CLI\/2.12.0",
    44    "source": "src\/edit.js",
    55    "domain": "messages",
     
    3535                "Innstillinger"
    3636            ],
     37            "Enable pagination": [
     38                "Aktiver paginering"
     39            ],
     40            "When enabled, all jobs will be displayed with pagination controls. When disabled, only the specified number of jobs will be shown.": [
     41                "N\u00e5r denne funksjonen er aktivert, vises alle jobber med pagineringskontroller. N\u00e5r den er deaktivert, vises bare det angitte antallet jobber."
     42            ],
    3743            "Number of items": [
    3844                "Antall oppf\u00f8ringer"
     45            ],
     46            "Jobs per page": [
     47                "Jobber per side"
    3948            ],
    4049            "Order by": [
  • jobbnorge-block/trunk/languages/wp-jobbnorge-block-nb_NO.po

    r3002720 r3322139  
    55"Project-Id-Version: Jobbnorge Block 0.1.0\n"
    66"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/jobbnorge-block\n"
    7 "POT-Creation-Date: 2023-11-28T11:53:20+00:00\n"
    8 "PO-Revision-Date: 2023-11-28 13:09+0100\n"
     7"POT-Creation-Date: 2025-07-03T21:00:36+00:00\n"
     8"PO-Revision-Date: 2025-07-03 23:16+0200\n"
    99"Last-Translator: Per Søderlind <[email protected]>\n"
    1010"Language-Team: \n"
     
    1313"Content-Type: text/plain; charset=UTF-8\n"
    1414"Content-Transfer-Encoding: 8bit\n"
    15 "X-Generator: Poedit 3.4.1\n"
     15"X-Generator: Poedit 3.6\n"
    1616"X-Domain: wp-jobbnorge-block\n"
    1717
    1818#. Plugin Name of the plugin
     19#: wp-jobb-norge.php
    1920msgid "Jobbnorge Block"
    2021msgstr "Jobbnorge-blokken"
    2122
    2223#. Plugin URI of the plugin
     24#: wp-jobb-norge.php
    2325msgid "https://wordpress.org/plugins/jobbnorge-block/"
    2426msgstr "https://wordpress.org/plugins/jobbnorge-block/"
    2527
    2628#. Description of the plugin
     29#: wp-jobb-norge.php
    2730msgid "Retrieve and display job listings from Jobbnorge.no"
    2831msgstr "Hente og vise stillingsannonser fra Jobbnorge.no"
    2932
    3033#. Author of the plugin
     34#: wp-jobb-norge.php
    3135msgid "PerS"
    3236msgstr "PerS"
    3337
    34 #: wp-jobb-norge.php:159
     38#: wp-jobb-norge.php:181
    3539msgid "Invalid ID"
    3640msgstr "Ugyldig ID"
    3741
    38 #: wp-jobb-norge.php:181
     42#: wp-jobb-norge.php:212
    3943msgid "Error connecting to Jobbnorge.no"
    4044msgstr "Feil ved tilkobling til Jobbnorge.no"
    4145
    42 #: wp-jobb-norge.php:195
     46#: wp-jobb-norge.php:251
    4347msgid "No jobs found"
    4448msgstr "Ingen jobber funnet"
    4549
    46 #: wp-jobb-norge.php:205
     50#: wp-jobb-norge.php:261
    4751msgid "(no title)"
    4852msgstr "(ingen tittel)"
    4953
    50 #: wp-jobb-norge.php:226 build/init.js:1 src/edit.js:160
     54#: wp-jobb-norge.php:282 build/init.js:1 src/edit.js:181
    5155msgid "Employer"
    5256msgstr "Arbeidsgiver"
    5357
    54 #: wp-jobb-norge.php:227
     58#: wp-jobb-norge.php:283
    5559msgid "Scope"
    5660msgstr "Omfang"
    5761
    58 #: wp-jobb-norge.php:282
     62#: wp-jobb-norge.php:345
    5963msgid "Read more"
    6064msgstr "Les mer"
    6165
    62 #: wp-jobb-norge.php:353
     66#: wp-jobb-norge.php:416
    6367msgid "Deadline:"
    6468msgstr "Søknadsfrist:"
    6569
    66 #: build/init.js:1 src/edit.js:95
     70#: wp-jobb-norge.php:556
     71msgid "Job listings pagination"
     72msgstr "Paginering av stillingsannonser"
     73
     74#: wp-jobb-norge.php:562
     75#, php-format
     76msgid "Showing %d-%d of %d jobs"
     77msgstr "Viser %d-%d av %d jobber"
     78
     79#: wp-jobb-norge.php:577 wp-jobb-norge.php:582
     80msgid "Previous"
     81msgstr "Forrige"
     82
     83#: wp-jobb-norge.php:590
     84#, php-format
     85msgid "Page %d of %d"
     86msgstr "Side %d av %d"
     87
     88#: wp-jobb-norge.php:601 wp-jobb-norge.php:606
     89msgid "Next"
     90msgstr "Neste"
     91
     92#: build/init.js:1 src/edit.js:97
    6793msgid ""
    6894"Select employers to display. Ctrl-click (Windows) or Cmd-click (Mac) to "
     
    7399"arbeidsgivere."
    74100
    75 #: build/init.js:1 src/edit.js:103
     101#: build/init.js:1 src/edit.js:105
    76102msgid "Employer ID [,id2, id3, ..]"
    77103msgstr "Arbeidsgiver-ID [,id2, id3, ...]"
    78104
    79 #: build/init.js:1 src/edit.js:110
     105#: build/init.js:1 src/edit.js:112
    80106msgid "Save"
    81107msgstr "Lagre"
    82108
    83 #: build/init.js:1 src/edit.js:121
     109#: build/init.js:1 src/edit.js:123
    84110msgid "Edit Jobbnorge URL"
    85111msgstr "Rediger Jobbnorge URL"
    86112
    87 #: build/init.js:1 src/edit.js:126
     113#: build/init.js:1 src/edit.js:128
    88114msgid "List view"
    89115msgstr "Listevisning"
    90116
    91 #: build/init.js:1 src/edit.js:132 src/edit.js:195
     117#: build/init.js:1 src/edit.js:134 src/edit.js:216
    92118msgid "Grid view"
    93119msgstr "Rutemønster"
    94120
    95 #: build/init.js:1 src/edit.js:144
     121#: build/init.js:1 src/edit.js:146
    96122msgid "Settings"
    97123msgstr "Innstillinger"
    98124
    99 #: build/init.js:1 src/edit.js:147
     125#: build/init.js:1 src/edit.js:148
     126msgid "Enable pagination"
     127msgstr "Aktiver paginering"
     128
     129#: build/init.js:1 src/edit.js:149
     130msgid ""
     131"When enabled, all jobs will be displayed with pagination controls. When "
     132"disabled, only the specified number of jobs will be shown."
     133msgstr ""
     134"Når denne funksjonen er aktivert, vises alle jobber med "
     135"pagineringskontroller. Når den er deaktivert, vises bare det angitte "
     136"antallet jobber."
     137
     138#: build/init.js:1 src/edit.js:156
    100139msgid "Number of items"
    101140msgstr "Antall oppføringer"
    102141
    103 #: build/init.js:1 src/edit.js:156
     142#: build/init.js:1 src/edit.js:167
     143msgid "Jobs per page"
     144msgstr "Jobber per side"
     145
     146#: build/init.js:1 src/edit.js:177
    104147msgid "Order by"
    105148msgstr "Sorter etter"
    106149
    107 #: build/init.js:1 src/edit.js:159
     150#: build/init.js:1 src/edit.js:180
    108151msgid "Deadline"
    109152msgstr "Søknadsfrist"
    110153
    111 #: build/init.js:1 src/edit.js:166
     154#: build/init.js:1 src/edit.js:187
    112155msgid "No jobs found message"
    113156msgstr "Ingen jobber funnet melding"
    114157
    115 #: build/init.js:1 src/edit.js:167
     158#: build/init.js:1 src/edit.js:188
    116159msgid "Message to display if no jobs are found"
    117160msgstr "Melding som vises hvis ingen jobber er funnet"
    118161
    119 #: build/init.js:1 src/edit.js:168
     162#: build/init.js:1 src/edit.js:189
    120163msgid "There are no jobs at this time."
    121164msgstr "Det er for tiden ingen ledige stillinger."
    122165
    123 #: build/init.js:1 src/edit.js:172
     166#: build/init.js:1 src/edit.js:193
    124167msgid "Item"
    125168msgstr "Element"
    126169
    127 #: build/init.js:1 src/edit.js:174
     170#: build/init.js:1 src/edit.js:195
    128171msgid "Display employer"
    129172msgstr "Vis arbeidsgiver"
    130173
    131 #: build/init.js:1 src/edit.js:179
     174#: build/init.js:1 src/edit.js:200
    132175msgid "Display excerpt"
    133176msgstr "Vis utdrag"
    134177
    135 #: build/init.js:1 src/edit.js:184
     178#: build/init.js:1 src/edit.js:205
    136179msgid "Display deadline"
    137180msgstr "Vis søknadsfrist"
    138181
    139 #: build/init.js:1 src/edit.js:189
     182#: build/init.js:1 src/edit.js:210
    140183msgid "Display scope"
    141184msgstr "Vis omfang"
    142185
    143 #: build/init.js:1 src/edit.js:198
     186#: build/init.js:1 src/edit.js:219
    144187msgid "Columns"
    145188msgstr "Kolonner"
  • jobbnorge-block/trunk/languages/wp-jobbnorge-block.pot

    r3002720 r3322139  
    1 # Copyright (C) 2023 PerS
     1# Copyright (C) 2025 PerS
    22# This file is distributed under the GPL-2.0-or-later.
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: Jobbnorge Block 2.1.4\n"
     5"Project-Id-Version: Jobbnorge Block 2.2.0\n"
    66"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/jobbnorge-block\n"
    77"Last-Translator: Per Søderlind <[email protected]\n"
     
    1010"Content-Type: text/plain; charset=UTF-8\n"
    1111"Content-Transfer-Encoding: 8bit\n"
    12 "POT-Creation-Date: 2023-11-28T12:11:37+00:00\n"
     12"POT-Creation-Date: 2025-07-03T21:16:21+00:00\n"
    1313"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    14 "X-Generator: WP-CLI 2.9.0\n"
     14"X-Generator: WP-CLI 2.12.0\n"
    1515"X-Domain: wp-jobbnorge-block\n"
    1616
    1717#. Plugin Name of the plugin
     18#: wp-jobb-norge.php
    1819msgid "Jobbnorge Block"
    1920msgstr ""
    2021
    2122#. Plugin URI of the plugin
     23#: wp-jobb-norge.php
    2224msgid "https://wordpress.org/plugins/jobbnorge-block/"
    2325msgstr ""
    2426
    2527#. Description of the plugin
     28#: wp-jobb-norge.php
    2629msgid "Retrieve and display job listings from Jobbnorge.no"
    2730msgstr ""
    2831
    2932#. Author of the plugin
     33#: wp-jobb-norge.php
    3034msgid "PerS"
    3135msgstr ""
    3236
    33 #: wp-jobb-norge.php:159
     37#: wp-jobb-norge.php:181
    3438msgid "Invalid ID"
    3539msgstr ""
    3640
    37 #: wp-jobb-norge.php:181
     41#: wp-jobb-norge.php:212
    3842msgid "Error connecting to Jobbnorge.no"
    3943msgstr ""
    4044
    41 #: wp-jobb-norge.php:195
     45#: wp-jobb-norge.php:251
    4246msgid "No jobs found"
    4347msgstr ""
    4448
    45 #: wp-jobb-norge.php:205
     49#: wp-jobb-norge.php:261
    4650msgid "(no title)"
    4751msgstr ""
    4852
    49 #: wp-jobb-norge.php:226
    50 #: build/init.js:1
    51 #: src/edit.js:160
     53#: wp-jobb-norge.php:282
     54#: build/init.js:1
     55#: src/edit.js:181
    5256msgid "Employer"
    5357msgstr ""
    5458
    55 #: wp-jobb-norge.php:227
     59#: wp-jobb-norge.php:283
    5660msgid "Scope"
    5761msgstr ""
    5862
    59 #: wp-jobb-norge.php:282
     63#: wp-jobb-norge.php:345
    6064msgid "Read more"
    6165msgstr ""
    6266
    63 #: wp-jobb-norge.php:353
     67#: wp-jobb-norge.php:416
    6468msgid "Deadline:"
    6569msgstr ""
    6670
    67 #: build/init.js:1
    68 #: src/edit.js:95
     71#: wp-jobb-norge.php:556
     72msgid "Job listings pagination"
     73msgstr ""
     74
     75#: wp-jobb-norge.php:562
     76#, php-format
     77msgid "Showing %d-%d of %d jobs"
     78msgstr ""
     79
     80#: wp-jobb-norge.php:577
     81#: wp-jobb-norge.php:582
     82msgid "Previous"
     83msgstr ""
     84
     85#: wp-jobb-norge.php:590
     86#, php-format
     87msgid "Page %d of %d"
     88msgstr ""
     89
     90#: wp-jobb-norge.php:601
     91#: wp-jobb-norge.php:606
     92msgid "Next"
     93msgstr ""
     94
     95#: build/init.js:1
     96#: src/edit.js:97
    6997msgid "Select employers to display. Ctrl-click (Windows) or Cmd-click (Mac) to select multiple employers. Shift-click to select a range of employers."
    7098msgstr ""
    7199
    72100#: build/init.js:1
    73 #: src/edit.js:103
     101#: src/edit.js:105
    74102msgid "Employer ID [,id2, id3, ..]"
    75103msgstr ""
    76104
    77105#: build/init.js:1
    78 #: src/edit.js:110
     106#: src/edit.js:112
    79107msgid "Save"
    80108msgstr ""
    81109
    82110#: build/init.js:1
    83 #: src/edit.js:121
     111#: src/edit.js:123
    84112msgid "Edit Jobbnorge URL"
    85113msgstr ""
    86114
    87115#: build/init.js:1
    88 #: src/edit.js:126
     116#: src/edit.js:128
    89117msgid "List view"
    90118msgstr ""
    91119
    92120#: build/init.js:1
    93 #: src/edit.js:132
     121#: src/edit.js:134
     122#: src/edit.js:216
     123msgid "Grid view"
     124msgstr ""
     125
     126#: build/init.js:1
     127#: src/edit.js:146
     128msgid "Settings"
     129msgstr ""
     130
     131#: build/init.js:1
     132#: src/edit.js:148
     133msgid "Enable pagination"
     134msgstr ""
     135
     136#: build/init.js:1
     137#: src/edit.js:149
     138msgid "When enabled, all jobs will be displayed with pagination controls. When disabled, only the specified number of jobs will be shown."
     139msgstr ""
     140
     141#: build/init.js:1
     142#: src/edit.js:156
     143msgid "Number of items"
     144msgstr ""
     145
     146#: build/init.js:1
     147#: src/edit.js:167
     148msgid "Jobs per page"
     149msgstr ""
     150
     151#: build/init.js:1
     152#: src/edit.js:177
     153msgid "Order by"
     154msgstr ""
     155
     156#: build/init.js:1
     157#: src/edit.js:180
     158msgid "Deadline"
     159msgstr ""
     160
     161#: build/init.js:1
     162#: src/edit.js:187
     163msgid "No jobs found message"
     164msgstr ""
     165
     166#: build/init.js:1
     167#: src/edit.js:188
     168msgid "Message to display if no jobs are found"
     169msgstr ""
     170
     171#: build/init.js:1
     172#: src/edit.js:189
     173msgid "There are no jobs at this time."
     174msgstr ""
     175
     176#: build/init.js:1
     177#: src/edit.js:193
     178msgid "Item"
     179msgstr ""
     180
     181#: build/init.js:1
    94182#: src/edit.js:195
    95 msgid "Grid view"
    96 msgstr ""
    97 
    98 #: build/init.js:1
    99 #: src/edit.js:144
    100 msgid "Settings"
    101 msgstr ""
    102 
    103 #: build/init.js:1
    104 #: src/edit.js:147
    105 msgid "Number of items"
    106 msgstr ""
    107 
    108 #: build/init.js:1
    109 #: src/edit.js:156
    110 msgid "Order by"
    111 msgstr ""
    112 
    113 #: build/init.js:1
    114 #: src/edit.js:159
    115 msgid "Deadline"
    116 msgstr ""
    117 
    118 #: build/init.js:1
    119 #: src/edit.js:166
    120 msgid "No jobs found message"
    121 msgstr ""
    122 
    123 #: build/init.js:1
    124 #: src/edit.js:167
    125 msgid "Message to display if no jobs are found"
    126 msgstr ""
    127 
    128 #: build/init.js:1
    129 #: src/edit.js:168
    130 msgid "There are no jobs at this time."
    131 msgstr ""
    132 
    133 #: build/init.js:1
    134 #: src/edit.js:172
    135 msgid "Item"
    136 msgstr ""
    137 
    138 #: build/init.js:1
    139 #: src/edit.js:174
    140183msgid "Display employer"
    141184msgstr ""
    142185
    143186#: build/init.js:1
    144 #: src/edit.js:179
     187#: src/edit.js:200
    145188msgid "Display excerpt"
    146189msgstr ""
    147190
    148191#: build/init.js:1
    149 #: src/edit.js:184
     192#: src/edit.js:205
    150193msgid "Display deadline"
    151194msgstr ""
    152195
    153196#: build/init.js:1
    154 #: src/edit.js:189
     197#: src/edit.js:210
    155198msgid "Display scope"
    156199msgstr ""
    157200
    158201#: build/init.js:1
    159 #: src/edit.js:198
     202#: src/edit.js:219
    160203msgid "Columns"
    161204msgstr ""
  • jobbnorge-block/trunk/package-lock.json

    r3003492 r3322139  
    11{
    22    "name": "jobbnorge-block",
    3     "version": "2.1.5",
     3    "version": "2.2.0",
    44    "lockfileVersion": 3,
    55    "requires": true,
     
    77        "": {
    88            "name": "jobbnorge-block",
    9             "version": "2.1.5",
     9            "version": "2.2.0",
    1010            "license": "GPL-2.0-or-later",
    1111            "dependencies": {
  • jobbnorge-block/trunk/package.json

    r3003492 r3322139  
    11{
    22    "name": "jobbnorge-block",
    3     "version": "2.1.5",
     3    "version": "2.2.0",
    44    "description": "Jobbnorge Block for WordPress Gutenberg",
    55    "author": "Per Søderlind <[email protected]>",
  • jobbnorge-block/trunk/readme.txt

    r3003492 r3322139  
    55Requires at least: 5.9
    66Requires PHP:      7.4
    7 Stable tag:        2.1.5
     7Stable tag:        2.2.0
    88License:           GPL-2.0-or-later
    99License URI:       https://www.gnu.org/licenses/gpl-2.0.html
     
    104104== Changelog ==
    105105
     106= 2.2.0 =
     107
     108* NEW: Add frontend pagination support with AJAX loading
     109* NEW: Add pagination controls (enable/disable, jobs per page setting)
     110* ENHANCEMENT: Upgrade to Jobbnorge API v3 for better performance
     111* ENHANCEMENT: Implement PHP-based pagination to work around API limitations with employer filtering
     112* ENHANCEMENT: Add responsive grid layout that adapts to screen size
     113* ENHANCEMENT: Improve cache key logic to include pagination and layout parameters
     114* ENHANCEMENT: Add loading states and error handling for pagination
     115* ENHANCEMENT: Separate frontend and admin CSS loading for better performance
     116* FIX: Fix CSS class naming conflicts that prevented grid view from working on frontend
     117* FIX: Resolve frontend style loading issues
     118* DEVELOPER: Add comprehensive webpack build configuration for multiple entry points
     119* DEVELOPER: Add pagination JavaScript with proper AJAX handling and nonce security
     120
    106121= 2.1.5 =
    107122
  • jobbnorge-block/trunk/src/block.json

    r3003492 r3322139  
    33    "apiVersion": 2,
    44    "name": "dss/jobbnorge",
    5     "version": "2.1.5",
     5    "version": "2.2.0",
    66    "title": "Jobbnorge",
    77    "category": "widgets",
     
    6363            "type": "number",
    6464            "default": 55
     65        },
     66        "enablePagination": {
     67            "type": "boolean",
     68            "default": true
     69        },
     70        "jobsPerPage": {
     71            "type": "number",
     72            "default": 10
    6573        }
    6674    },
    6775    "textdomain": "wp-jobbnorge-block",
    6876    "editorScript": "file:init.js",
    69     "editorStyle": "file:editor.scss",
    70     "style": "file:style.scss"
     77    "editorStyle": "file:editor.css",
     78    "style": "file:style-init.css"
    7179}
  • jobbnorge-block/trunk/src/edit.js

    r3002720 r3322139  
    5252        noJobsMessage,
    5353        orderBy,
     54        enablePagination,
     55        jobsPerPage,
    5456    } = attributes;
    5557
     
    143145            <InspectorControls>
    144146                <PanelBody title={__('Settings', 'wp-jobbnorge-block')}>
    145                     <RangeControl
    146                         __nextHasNoMarginBottom
    147                         label={__('Number of items', 'wp-jobbnorge-block')}
    148                         value={itemsToShow}
    149                         onChange={(value) => setAttributes({ itemsToShow: value })}
    150                         min={DEFAULT_MIN_ITEMS}
    151                         max={DEFAULT_MAX_ITEMS}
    152                         required
    153                     />
     147                    <ToggleControl
     148                        label={__('Enable pagination', 'wp-jobbnorge-block')}
     149                        help={__('When enabled, all jobs will be displayed with pagination controls. When disabled, only the specified number of jobs will be shown.', 'wp-jobbnorge-block')}
     150                        checked={enablePagination}
     151                        onChange={(value) => setAttributes({ enablePagination: value })}
     152                    />
     153                    {!enablePagination && (
     154                        <RangeControl
     155                            __nextHasNoMarginBottom
     156                            label={__('Number of items', 'wp-jobbnorge-block')}
     157                            value={itemsToShow}
     158                            onChange={(value) => setAttributes({ itemsToShow: value })}
     159                            min={DEFAULT_MIN_ITEMS}
     160                            max={DEFAULT_MAX_ITEMS}
     161                            required
     162                        />
     163                    )}
     164                    {enablePagination && (
     165                        <RangeControl
     166                            __nextHasNoMarginBottom
     167                            label={__('Jobs per page', 'wp-jobbnorge-block')}
     168                            value={jobsPerPage}
     169                            onChange={(value) => setAttributes({ jobsPerPage: value })}
     170                            min={1}
     171                            max={50}
     172                            required
     173                        />
     174                    )}
    154175                    {employerID.includes(',') && (
    155176                        <RadioControl
  • jobbnorge-block/trunk/src/editor.scss

    r2853882 r3322139  
    11$break-medium: 782px;
     2$break-small: 600px;
    23$grid-unit: 8px;
    34$grid-unit-10: 1 * $grid-unit; // 8px
     
    910}
    1011
    11 .wp-block-dss-jobbnorge li a > div {
     12@mixin break-small() {
     13    @media (min-width: #{ ($break-small) }) {
     14        @content;
     15    }
     16}
     17
     18.wp-block-dss-jobbnorge li a>div {
    1219    display: inline;
    1320}
     
    1724    align-items: stretch;
    1825
    19     > * {
     26    >* {
    2027        margin-bottom: $grid-unit-10;
    2128    }
    2229
    2330    @include break-medium() {
    24         > * {
     31        >* {
    2532            margin-bottom: 0;
    2633        }
    2734    }
    2835}
    29 
    30 
    3136
    3237.wp-block-dss-jobbnorge__placeholder-input {
     
    4348    }
    4449}
     50
     51// Grid styles for the editor
     52ul.wp-block-dss-jobbnorge {
     53    list-style: none;
     54    padding: 0;
     55
     56    &.wp-block-dss-jobbnorge {
     57        box-sizing: border-box;
     58    }
     59
     60    li {
     61        margin: 0 0 1em 0;
     62    }
     63
     64    &.is-grid {
     65        display: flex;
     66        flex-wrap: wrap;
     67        padding: 0;
     68        list-style: none;
     69
     70        li {
     71            margin: 0 1em 1em 0;
     72            width: 100%;
     73        }
     74    }
     75
     76    @include break-small {
     77        @for $i from 2 through 6 {
     78            &.columns-#{ $i } li {
     79                width: calc((100% / #{ $i }) - 1em);
     80            }
     81        }
     82    }
     83}
  • jobbnorge-block/trunk/src/style.scss

    r2997962 r3322139  
    88
    99
    10 ul.wp-block-dss-jobbnorge { // The ul is needed for specificity to override the reset styles in the editor.
     10ul.wp-block-dss-jobbnorge {
     11    // The ul is needed for specificity to override the reset styles in the editor.
    1112    list-style: none;
    1213    padding: 0;
     
    2122        margin-right: 2em;
    2223    }
     24
    2325    &.alignright {
    2426        /*rtl:ignore*/
    2527        margin-left: 2em;
    2628    }
     29
    2730    li {
    2831        margin: 0 0 1em 0;
    2932    }
     33
    3034    &.is-grid {
    3135        display: flex;
     
    4347        @for $i from 2 through 6 {
    4448            &.columns-#{ $i } li {
    45                 width: calc(( 100% / #{ $i } ) - 1em);
     49                width: calc((100% / #{ $i }) - 1em);
    4650            }
    4751        }
     
    6367.wp-block-dss-jobbnorge__item-employer,
    6468.wp-block-dss-jobbnorge__item-deadline,
    65 .wp-block-dss-jobbnorge__item-scope{
     69.wp-block-dss-jobbnorge__item-scope {
    6670    display: block;
    6771    font-weight: 600;
     
    6973}
    7074
     75// Pagination styles
     76.wp-block-dss-jobbnorge {
     77    &__pagination {
     78        display: flex;
     79        flex-direction: column;
     80        gap: 1rem;
     81        margin-top: 2rem;
     82        padding: 1rem 0;
     83        border-top: 1px solid #e0e0e0;
     84
     85        @include break-small {
     86            flex-direction: row;
     87            justify-content: space-between;
     88            align-items: center;
     89        }
     90    }
     91
     92    &__pagination-info {
     93        font-size: 0.875rem;
     94        color: #666;
     95        margin: 0;
     96    }
     97
     98    &__pagination-controls {
     99        display: flex;
     100        gap: 0.5rem;
     101        align-items: center;
     102
     103        button {
     104            padding: 0.5rem 1rem;
     105            border: 1px solid #ddd;
     106            background: white;
     107            cursor: pointer;
     108            border-radius: 4px;
     109            font-size: 0.875rem;
     110            transition: all 0.2s ease;
     111
     112            &:hover:not(:disabled) {
     113                background: #f5f5f5;
     114                border-color: #999;
     115            }
     116
     117            &:disabled {
     118                opacity: 0.5;
     119                cursor: not-allowed;
     120            }
     121        }
     122
     123        .wp-block-dss-jobbnorge__pagination-info {
     124            font-size: 0.875rem;
     125            color: #333;
     126            margin: 0 0.5rem;
     127        }
     128    }
     129
     130    // Loading state
     131    &__loading {
     132        opacity: 0.6;
     133        pointer-events: none;
     134
     135        &::after {
     136            content: '';
     137            position: absolute;
     138            top: 50%;
     139            left: 50%;
     140            width: 20px;
     141            height: 20px;
     142            margin: -10px 0 0 -10px;
     143            border: 2px solid #ccc;
     144            border-top-color: #333;
     145            border-radius: 50%;
     146            animation: spin 1s linear infinite;
     147        }
     148    }
     149
     150    // Error message
     151    &__error {
     152        margin: 1rem 0;
     153        padding: 0.75rem;
     154        background: #ffebe8;
     155        border: 1px solid #d63638;
     156        border-radius: 4px;
     157        color: #d63638;
     158
     159        p {
     160            margin: 0;
     161        }
     162    }
     163}
     164
     165@keyframes spin {
     166    to {
     167        transform: rotate(360deg);
     168    }
     169}
  • jobbnorge-block/trunk/wp-jobb-norge.php

    r3003492 r3322139  
    66 * Requires at least: 5.9
    77 * Requires PHP:      7.0
    8  * Version:           2.1.5
     8 * Version:           2.2.0
    99 * Author:            PerS
    1010 * License:           GPL-2.0-or-later
     
    3636    add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\dss_jobbnorge_enqueue_scripts' );
    3737
    38     // Add the 'dss_jobbnorge_enqueue_scripts' function to the 'wp_enqueue_scripts' action hook.
     38    // Add the 'dss_jobbnorge_enqueue_frontend_styles' function to the 'wp_enqueue_scripts' action hook.
    3939    // This function will be called when scripts and styles are enqueued for the front end of the site.
    40     add_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\dss_jobbnorge_enqueue_scripts' );
     40    add_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\dss_jobbnorge_enqueue_frontend_styles' );
    4141
    4242    // Load the plugin's text domain for internationalization.
     
    5050    register_block_type(
    5151        __DIR__ . '/build',
    52         [
     52        [ 
    5353            'render_callback' => __NAMESPACE__ . '\render_block_dss_jobbnorge',
    5454        ]
     
    8181        // If it does, require it and merge its dependencies with the existing ones.
    8282        $file   = require $deps_file;
    83         $jsdeps = array_merge( $jsdeps, $file['dependencies'] );
     83        $jsdeps = array_merge( $jsdeps, $file[ 'dependencies' ] );
    8484        // Also, set the version to the one from the file.
    85         $version = $file['version'];
     85        $version = $file[ 'version' ];
    8686    }
    8787
     
    9292        wp_enqueue_style( 'dss-jobbnorge-admin' );
    9393    }
    94 
    95     // Register and enqueue a CSS file for the public view.
    96     wp_register_style( 'dss-jobbnorge', plugin_dir_url( __FILE__ ) . 'build/style-init.css', [], $version );
    97     wp_enqueue_style( 'dss-jobbnorge' );
    9894
    9995    // Set translations for the script.
     
    118114            'dss-jobbnorge-editor-script',
    119115            'wpJobbnorgeBlock',
    120             [
     116            [ 
    121117                'employers' => $employers,
    122118            ]
     
    126122
    127123/**
     124 * Enqueue frontend styles for the block
     125 *
     126 * @return void
     127 */
     128function dss_jobbnorge_enqueue_frontend_styles(): void {
     129    // Define the path to the dependencies file.
     130    $deps_file = plugin_dir_path( __FILE__ ) . 'build/init.asset.php';
     131
     132    // Initialize version number.
     133    $version = wp_rand();
     134
     135    // Check if the dependencies file exists.
     136    if ( file_exists( $deps_file ) ) {
     137        // If it does, require it and get the version.
     138        $file    = require $deps_file;
     139        $version = $file[ 'version' ];
     140    }
     141
     142    // Register and enqueue a CSS file for the public view.
     143    wp_register_style( 'dss-jobbnorge', plugin_dir_url( __FILE__ ) . 'build/style-init.css', [], $version );
     144    wp_enqueue_style( 'dss-jobbnorge' );
     145}
     146
     147/**
    128148 * Renders the `jobbnorge` block on server.
    129149 *
     
    137157    $attributes = wp_parse_args(
    138158        $attributes,
    139         [
    140             'employerID'      => '',
    141             'displayEmployer' => false,
    142             'displayDate'     => true,
    143             'displayDeadline' => false,
    144             'displayScope'    => false,
    145             'displayExcerpt'  => true,
    146             'excerptLength'   => 55,
    147             'blockLayout'     => 'list',
    148             'orderBy'         => 'Deadline',
    149             'columns'         => 3,
    150             'itemsToShow'     => 5,
     159        [
     160            'employerID'       => '',
     161            'displayEmployer'  => false,
     162            'displayDate'      => true,
     163            'displayDeadline'  => false,
     164            'displayScope'     => false,
     165            'displayExcerpt'   => true,
     166            'excerptLength'    => 55,
     167            'blockLayout'      => 'list',
     168            'orderBy'          => 'Deadline',
     169            'columns'          => 3,
     170            'itemsToShow'      => 5,
     171            'enablePagination' => true,
     172            'jobsPerPage'      => 10,
    151173        ]
    152174    );
    153175
    154176    // Convert employer IDs to an array and trim whitespace.
    155     $arr_ids = array_map( 'trim', explode( ',', $attributes['employerID'] ) );
     177    $arr_ids = array_map( 'trim', explode( ',', $attributes[ 'employerID' ] ) );
    156178
    157179    // Check if all IDs are numeric. If not, return an error message.
     
    160182    }
    161183
    162     // Construct the API URL.
    163     $jobbnorge_api_url = 'https://publicapi.jobbnorge.no/v2/Jobs?abroad=false&orderBy=' . $attributes['orderBy'];
     184    // Get current page for pagination
     185    $current_page = isset( $_GET[ 'jobbnorge_page' ] ) ? max( 1, intval( $_GET[ 'jobbnorge_page' ] ) ) : 1;
     186
     187    // Determine items per page based on pagination setting
     188    $items_per_page = $attributes[ 'enablePagination' ] ? $attributes[ 'jobsPerPage' ] : $attributes[ 'itemsToShow' ];
     189
     190    // Construct the API URL for v3
     191    // NOTE: API v3 pagination doesn't work correctly with employer filtering
     192    // So we fetch all jobs for the employers and paginate in PHP
     193    $jobbnorge_api_url = 'https://publicapi.jobbnorge.no/v3/Jobs?abroad=false&orderBy=' . $attributes[ 'orderBy' ];
    164194
    165195    // Add each employer ID to the API URL.
     
    171201    $cache      = new \Jobbnorge_CacheHandler( $cache_path );
    172202
    173     $cache_key  = md5( $jobbnorge_api_url );
    174     $expiration = apply_filters( 'jobbnorge_cache_time', 30 * MINUTE_IN_SECONDS );
    175     $body       = $cache->get( $cache_key, $expiration );
    176 
    177     if ( false === $body ) {
     203    // Cache key based on employer IDs and settings, not pagination
     204    $cache_key     = md5( $jobbnorge_api_url );
     205    $expiration    = apply_filters( 'jobbnorge_cache_time', 30 * MINUTE_IN_SECONDS );
     206    $response_data = $cache->get( $cache_key, $expiration );
     207
     208    if ( false === $response_data ) {
    178209        $response = wp_remote_get( $jobbnorge_api_url );
    179210
     
    182213        }
    183214
    184         $body = wp_remote_retrieve_body( $response );
    185         $cache->set( $cache_key, $body );
    186     }
    187 
    188     // Decode the JSON response and limit the number of items.
    189     $items = json_decode( $body, true );
    190     // Just get desired number of items.
    191     $items = array_slice( $items, 0, $attributes['itemsToShow'] );
     215        $body          = wp_remote_retrieve_body( $response );
     216        $response_data = json_decode( $body, true );
     217        $cache->set( $cache_key, $response_data );
     218    }
     219
     220    // Debug: Log the API response structure
     221    if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     222        error_log( 'Jobbnorge API URL: ' . $jobbnorge_api_url );
     223        error_log( 'Jobbnorge API Response: ' . print_r( $response_data, true ) );
     224    }
     225    // Handle v3 API response structure
     226    $all_items  = isset( $response_data[ 'jobs' ] ) ? $response_data[ 'jobs' ] : $response_data;
     227    $total_jobs = count( $all_items );
     228
     229    // Debug: Log the items array
     230    if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
     231        error_log( 'Items count: ' . count( $all_items ) );
     232        error_log( 'Total jobs: ' . $total_jobs );
     233        if ( ! empty( $all_items ) ) {
     234            error_log( 'First item: ' . print_r( $all_items[ 0 ], true ) );
     235        }
     236    }
     237
     238    // Implement pagination in PHP since API pagination doesn't work with employer filtering
     239    if ( $attributes[ 'enablePagination' ] && $total_jobs > 0 ) {
     240        // Calculate pagination
     241        $start_index = ( $current_page - 1 ) * $attributes[ 'jobsPerPage' ];
     242        $items       = array_slice( $all_items, $start_index, $attributes[ 'jobsPerPage' ] );
     243    } else {
     244        // For non-paginated, limit to itemsToShow
     245        $items      = array_slice( $all_items, 0, $attributes[ 'itemsToShow' ] );
     246        $total_jobs = count( $items ); // Update total for non-paginated display
     247    }
    192248
    193249    // If there are no items, return an error message.
     
    202258    foreach ( $items as $item ) {
    203259        // Sanitize and format the title.
    204         $title = esc_html( trim( wp_strip_all_tags( $item['title'] ) ) );
     260        $title = esc_html( trim( wp_strip_all_tags( $item[ 'title' ] ) ) );
    205261        $title = empty( $title ) ? __( '(no title)', 'wp-jobbnorge-block' ) : $title;
    206262
    207263        // Sanitize the link.
    208         $link = esc_url( $item['link'] );
     264        $link = esc_url( $item[ 'link' ] );
    209265        // If there's a link, wrap the title in an anchor tag.
    210266        $title = $link ? "<a href='{$link}'>{$title}</a>" : $title;
     
    216272        $deadline = '';
    217273        // If the displayDate attribute is true and the item has a deadline, format the deadline.
    218         if ( $attributes['displayDate'] && isset( $item['deadline'] ) ) {
    219             $deadline = format_deadline( $item['deadline'] );
     274        if ( $attributes[ 'displayDate' ] && isset( $item[ 'deadline' ] ) ) {
     275            $deadline = format_deadline( $item[ 'deadline' ] );
    220276        }
    221277
     
    238294    }
    239295
    240     // Initialize an array for the classnames.
    241     $classnames = [];
    242 
    243     // If the blockLayout attribute is 'grid', add the 'is-grid' and 'columns-' classes.
    244     if ( 'grid' === $attributes['blockLayout'] ) {
    245         add_classname( $classnames, $attributes, 'blockLayout', 'is-grid' );
    246         add_classname( $classnames, $attributes, 'columns', 'columns-' . $attributes['columns'] );
    247     }
    248 
    249     // Add the 'has-' classes based on the display attributes.
    250     add_classname( $classnames, $attributes, 'displayEmployer', 'has-employer' );
    251     add_classname( $classnames, $attributes, 'displayDate', 'has-dates' );
    252     add_classname( $classnames, $attributes, 'displayDeadline', 'has-deadline' );
    253     add_classname( $classnames, $attributes, 'displayScope', 'has-scope' );
    254     add_classname( $classnames, $attributes, 'displayExcerpt', 'has-excerpts' );
    255 
    256     // Get the block wrapper attributes and add the classnames to it.
    257     $wrapper_attributes = get_block_wrapper_attributes( [ 'class' => implode( ' ', $classnames ) ] );
     296    // Get the block wrapper attributes (without grid classes)
     297    $wrapper_classes = [];
     298    add_classname( $wrapper_classes, $attributes, 'displayEmployer', 'has-employer' );
     299    add_classname( $wrapper_classes, $attributes, 'displayDate', 'has-dates' );
     300    add_classname( $wrapper_classes, $attributes, 'displayDeadline', 'has-deadline' );
     301    add_classname( $wrapper_classes, $attributes, 'displayScope', 'has-scope' );
     302    add_classname( $wrapper_classes, $attributes, 'displayExcerpt', 'has-excerpts' );
     303
     304    $wrapper_attributes = get_block_wrapper_attributes( [
     305        'class'           => implode( ' ', $wrapper_classes ),
     306        'data-attributes' => esc_attr( json_encode( $attributes ) ),
     307    ] );
     308
     309    // Generate the ul classes (including grid classes)
     310    $ul_classes = [ 'wp-block-dss-jobbnorge' ];
     311    if ( 'grid' === $attributes[ 'blockLayout' ] ) {
     312        $ul_classes[] = 'is-grid';
     313        $ul_classes[] = 'columns-' . $attributes[ 'columns' ];
     314    }
     315
     316    // Generate pagination controls if enabled
     317    $pagination_html = '';
     318    if ( $attributes[ 'enablePagination' ] && count( $all_items ) > $attributes[ 'jobsPerPage' ] ) {
     319        $pagination_html = generate_pagination_controls( $current_page, count( $all_items ), $attributes[ 'jobsPerPage' ], $attributes );
     320    }
    258321
    259322    // Return the final HTML string, wrapping the list items in an unordered list.
    260     return sprintf( '<ul %s>%s</ul>', $wrapper_attributes, $list_items );
     323    return sprintf( '<div %s><ul class="%s">%s</ul>%s</div>', $wrapper_attributes, esc_attr( implode( ' ', $ul_classes ) ), $list_items, $pagination_html );
    261324}
    262325
     
    273336
    274337    // If the displayExcerpt attribute is true and the item has a summary, format the excerpt.
    275     if ( $attributes['displayExcerpt'] && isset( $item['summary'] ) ) {
     338    if ( $attributes[ 'displayExcerpt' ] && isset( $item[ 'summary' ] ) ) {
    276339        // Decode the HTML entities in the summary.
    277         $excerpt = html_entity_decode( $item['summary'], ENT_QUOTES, get_option( 'blog_charset' ) );
     340        $excerpt = html_entity_decode( $item[ 'summary' ], ENT_QUOTES, get_option( 'blog_charset' ) );
    278341        // Trim the excerpt to the excerptLength and escape it for safe use in HTML output.
    279         $excerpt = esc_attr( wp_trim_words( $excerpt, $attributes['excerptLength'], '' ) );
     342        $excerpt = esc_attr( wp_trim_words( $excerpt, $attributes[ 'excerptLength' ], '' ) );
    280343
    281344        // Format the read more link.
    282         $read_more = sprintf( ' ... <a href="%s">%s</a>', esc_url( $item['link'] ), __( 'Read more', 'wp-jobbnorge-block' ) );
     345        $read_more = sprintf( ' ... <a href="%s">%s</a>', esc_url( $item[ 'link' ] ), __( 'Read more', 'wp-jobbnorge-block' ) );
    283346
    284347        // Add the excerpt and read more link to the result string, wrapped in a div.
     
    338401        // Format the date according to the site's date format.
    339402        $str_date = date_i18n( get_option( 'date_format' ), $date );
    340     } catch ( \Exception $e ) {
     403    } catch (\Exception $e) {
    341404        // If there's an exception, fallback to the original date.
    342405        $str_date = $deadline_date;
     
    348411        return sprintf(
    349412            '<time datetime="%1$s" class="wp-block-dss-jobbnorge__item-deadline">%2$s %3$s</time> ',
    350             // If there's a parsed date, use it for the datetime attribute. Otherwise, leave it empty.
     413                // If there's a parsed date, use it for the datetime attribute. Otherwise, leave it empty.
    351414            ( $date ) ? esc_attr( wp_date( 'c', $date ) ) : '',
    352415            // Translate the 'Deadline:' string.
     
    406469function parse_date_fallback( $deadline_date ) {
    407470    // Define an array of month names in Norwegian.
    408     $str_months = [
     471    $str_months = [ 
    409472        'januar',
    410473        'februar',
     
    422485
    423486    // Define an array of month numbers.
    424     $num_months = [
     487    $num_months = [ 
    425488        '01',
    426489        '02',
     
    448511
    449512    // Return a Unix timestamp for the date.
    450     return mktime( 0, 0, 0, $dato_arr[2], $dato_arr[1], $dato_arr[3] );
     513    return mktime( 0, 0, 0, $dato_arr[ 2 ], $dato_arr[ 1 ], $dato_arr[ 3 ] );
    451514}
    452515
     
    466529    }
    467530}
     531
     532/**
     533 * Generates pagination controls for the job listings.
     534 *
     535 * @param int   $current_page The current page number.
     536 * @param int   $total_jobs   The total number of jobs.
     537 * @param int   $jobs_per_page The number of jobs per page.
     538 * @param array $attributes   The block attributes.
     539 * @return string The pagination HTML.
     540 */
     541function generate_pagination_controls( $current_page, $total_jobs, $jobs_per_page, $attributes ) {
     542    $total_pages = ceil( $total_jobs / $jobs_per_page );
     543
     544    if ( $total_pages <= 1 ) {
     545        return '';
     546    }
     547
     548    $prev_page = max( 1, $current_page - 1 );
     549    $next_page = min( $total_pages, $current_page + 1 );
     550
     551    // Calculate result range
     552    $start_item = ( ( $current_page - 1 ) * $jobs_per_page ) + 1;
     553    $end_item   = min( $current_page * $jobs_per_page, $total_jobs );
     554
     555    // Generate pagination HTML
     556    $pagination_html = '<nav class="wp-block-dss-jobbnorge__pagination" role="navigation" aria-label="' . esc_attr__( 'Job listings pagination', 'wp-jobbnorge-block' ) . '">';
     557
     558    // Results info
     559    $pagination_html .= sprintf(
     560        '<div class="wp-block-dss-jobbnorge__pagination-info">%s</div>',
     561        sprintf(
     562            esc_html__( 'Showing %d-%d of %d jobs', 'wp-jobbnorge-block' ),
     563            $start_item,
     564            $end_item,
     565            $total_jobs
     566        )
     567    );
     568
     569    // Pagination controls
     570    $pagination_html .= '<div class="wp-block-dss-jobbnorge__pagination-controls">';
     571
     572    // Previous button
     573    if ( $current_page > 1 ) {
     574        $pagination_html .= sprintf(
     575            '<button type="button" class="wp-block-dss-jobbnorge__pagination-prev" data-page="%d">%s</button>',
     576            $prev_page,
     577            esc_html__( 'Previous', 'wp-jobbnorge-block' )
     578        );
     579    } else {
     580        $pagination_html .= sprintf(
     581            '<button type="button" class="wp-block-dss-jobbnorge__pagination-prev" disabled>%s</button>',
     582            esc_html__( 'Previous', 'wp-jobbnorge-block' )
     583        );
     584    }
     585
     586    // Page info
     587    $pagination_html .= sprintf(
     588        '<span class="wp-block-dss-jobbnorge__pagination-info">%s</span>',
     589        sprintf(
     590            esc_html__( 'Page %d of %d', 'wp-jobbnorge-block' ),
     591            $current_page,
     592            $total_pages
     593        )
     594    );
     595
     596    // Next button
     597    if ( $current_page < $total_pages ) {
     598        $pagination_html .= sprintf(
     599            '<button type="button" class="wp-block-dss-jobbnorge__pagination-next" data-page="%d">%s</button>',
     600            $next_page,
     601            esc_html__( 'Next', 'wp-jobbnorge-block' )
     602        );
     603    } else {
     604        $pagination_html .= sprintf(
     605            '<button type="button" class="wp-block-dss-jobbnorge__pagination-next" disabled>%s</button>',
     606            esc_html__( 'Next', 'wp-jobbnorge-block' )
     607        );
     608    }
     609
     610    $pagination_html .= '</div>';
     611    $pagination_html .= '</nav>';
     612
     613    return $pagination_html;
     614}
     615
     616/**
     617 * Register AJAX endpoints for pagination.
     618 */
     619add_action( 'wp_ajax_jobbnorge_get_jobs', __NAMESPACE__ . '\handle_ajax_get_jobs' );
     620add_action( 'wp_ajax_nopriv_jobbnorge_get_jobs', __NAMESPACE__ . '\handle_ajax_get_jobs' );
     621
     622/**
     623 * Handle AJAX request for paginated job listings.
     624 */
     625function handle_ajax_get_jobs() {
     626    // Verify nonce
     627    if ( ! wp_verify_nonce( $_POST[ 'nonce' ], 'jobbnorge_pagination_nonce' ) ) {
     628        wp_die( 'Security check failed' );
     629    }
     630
     631    // Get and sanitize parameters
     632    $page       = isset( $_POST[ 'page' ] ) ? max( 1, intval( $_POST[ 'page' ] ) ) : 1;
     633    $attributes = isset( $_POST[ 'attributes' ] ) ? json_decode( stripslashes( $_POST[ 'attributes' ] ), true ) : [];
     634
     635    // Validate attributes
     636    if ( empty( $attributes ) || ! is_array( $attributes ) ) {
     637        wp_send_json_error( 'Invalid attributes' );
     638    }
     639
     640    // Set current page in GET superglobal for compatibility
     641    $_GET[ 'jobbnorge_page' ] = $page;
     642
     643    // Generate the job listings HTML
     644    $html = render_block_dss_jobbnorge( $attributes );
     645
     646    // Return JSON response
     647    wp_send_json_success( [ 'html' => $html ] );
     648}
     649
     650/**
     651 * Enqueue frontend JavaScript for pagination.
     652 */
     653function enqueue_pagination_script() {
     654    // Check if the block is being used on the current page
     655    if ( ! has_block( 'dss/jobbnorge' ) ) {
     656        return;
     657    }
     658
     659    // Define the path to the pagination dependencies file.
     660    $deps_file = plugin_dir_path( __FILE__ ) . 'build/pagination.asset.php';
     661
     662    // Initialize an array for JavaScript dependencies and a random version number.
     663    $jsdeps  = [];
     664    $version = wp_rand();
     665
     666    // Check if the dependencies file exists.
     667    if ( file_exists( $deps_file ) ) {
     668        // If it does, require it and merge its dependencies with the existing ones.
     669        $file   = require $deps_file;
     670        $jsdeps = array_merge( $jsdeps, $file[ 'dependencies' ] );
     671        // Also, set the version to the one from the file.
     672        $version = $file[ 'version' ];
     673    }
     674
     675    wp_enqueue_script(
     676        'jobbnorge-pagination',
     677        plugin_dir_url( __FILE__ ) . 'build/pagination.js',
     678        $jsdeps,
     679        $version,
     680        true
     681    );
     682
     683    // Localize script with AJAX URL and nonce
     684    wp_localize_script(
     685        'jobbnorge-pagination',
     686        'jobbnorgeAjax',
     687        [
     688            'ajaxUrl' => admin_url( 'admin-ajax.php' ),
     689            'nonce'   => wp_create_nonce( 'jobbnorge_pagination_nonce' ),
     690        ]
     691    );
     692}
     693
     694// Hook into wp_enqueue_scripts to add pagination script
     695add_action( 'wp_enqueue_scripts', __NAMESPACE__ . '\enqueue_pagination_script' );
Note: See TracChangeset for help on using the changeset viewer.