├── .babelrc ├── .jshintrc ├── .gitignore ├── dist ├── img │ ├── logo-48x48.png │ ├── ajax-loader.gif │ └── ajax-loader-lg.gif └── js │ ├── instant-images-styles.min.js │ └── instant-images-styles.js ├── src ├── img │ ├── ajax-loader.gif │ ├── logo-48x48.png │ ├── ajax-loader-lg.gif │ └── icon-256x256.png ├── js │ ├── components │ │ ├── API.js │ │ ├── ResultsToolTip.js │ │ ├── Helpers.js │ │ ├── PhotoList.js │ │ └── Photo.js │ ├── block │ │ ├── components │ │ │ ├── setFeaturedImage.js │ │ │ ├── insertImage.js │ │ │ ├── unsplash │ │ │ │ ├── menu.js │ │ │ │ └── index.js │ │ │ └── icon.js │ │ └── index.js │ └── index.js └── scss │ ├── partials │ ├── _editor.scss │ ├── _no-results.scss │ ├── _gutenberg.scss │ ├── _onboarding.scss │ ├── _settings.scss │ ├── _nav.scss │ ├── _admin.scss │ └── _photos.scss │ └── style.scss ├── postcss.config.js ├── admin ├── assets │ ├── img │ │ └── bolt.svg │ └── js │ │ └── admin.js ├── includes │ ├── cta │ │ └── permissions.php │ ├── unsplash-settings.php │ └── settings.php ├── views │ └── unsplash.php └── admin.php ├── .editorconfig ├── vendor └── connekt-plugin-installer │ ├── .editorconfig │ ├── README.md │ ├── assets │ ├── installer.js │ └── installer.css │ ├── class-connekt-plugin-installer.php │ └── LICENSE ├── webpack ├── dev.config.js └── prod.config.js ├── api ├── test.php ├── upload.php └── resize.php ├── webpack.config.js ├── package.json ├── instant-images.php ├── lang └── instant-images.pot ├── README.txt └── LICENSE.txt /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"] 3 | } -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "esversion": 6 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.map 2 | /node_nodules 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /dist/img/logo-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derweili/instant-images/master/dist/img/logo-48x48.png -------------------------------------------------------------------------------- /src/img/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derweili/instant-images/master/src/img/ajax-loader.gif -------------------------------------------------------------------------------- /src/img/logo-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derweili/instant-images/master/src/img/logo-48x48.png -------------------------------------------------------------------------------- /dist/img/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derweili/instant-images/master/dist/img/ajax-loader.gif -------------------------------------------------------------------------------- /src/img/ajax-loader-lg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derweili/instant-images/master/src/img/ajax-loader-lg.gif -------------------------------------------------------------------------------- /src/img/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derweili/instant-images/master/src/img/icon-256x256.png -------------------------------------------------------------------------------- /dist/img/ajax-loader-lg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/derweili/instant-images/master/dist/img/ajax-loader-lg.gif -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'autoprefixer': {browsers: ['last 10 versions']}, 4 | } 5 | } -------------------------------------------------------------------------------- /admin/assets/img/bolt.svg: -------------------------------------------------------------------------------- 1 | Asset 1 -------------------------------------------------------------------------------- /src/js/components/API.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | photo_api: 'https://api.unsplash.com/photos', 3 | search_api: 'https://api.unsplash.com/search/photos', 4 | app_id: '/?client_id='+ instant_img_localize.unsplash_app_id, 5 | posts_per_page: '&per_page=20' 6 | } -------------------------------------------------------------------------------- /src/js/block/components/setFeaturedImage.js: -------------------------------------------------------------------------------- 1 | const { dispatch } = wp.data; 2 | 3 | const SetFeaturedImage = (imageId) => { 4 | if(imageId === null){ 5 | return false; 6 | } 7 | dispatch("core/editor").editPost({ featured_media: imageId }); 8 | } 9 | export default SetFeaturedImage; -------------------------------------------------------------------------------- /src/scss/partials/_editor.scss: -------------------------------------------------------------------------------- 1 | // Media context popup window 2 | .instant-img-container[data-media-popup="true"]{ 3 | background: #fff; 4 | .header-wrap{ 5 | display: none; 6 | } 7 | .instant-images-wrapper{ 8 | padding: 0 16px; 9 | } 10 | #photos{ 11 | .photo{} 12 | } 13 | } -------------------------------------------------------------------------------- /src/scss/style.scss: -------------------------------------------------------------------------------- 1 | @import url('//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css'); 2 | @import 'partials/admin'; 3 | @import 'partials/nav'; 4 | @import 'partials/photos'; 5 | @import 'partials/settings'; 6 | @import 'partials/editor'; 7 | @import 'partials/gutenberg'; 8 | @import 'partials/no-results'; -------------------------------------------------------------------------------- /src/scss/partials/_no-results.scss: -------------------------------------------------------------------------------- 1 | .no-results{ 2 | display: none; 3 | padding: 150px 100px; 4 | text-align: center; 5 | &.show{ 6 | display: block; 7 | } 8 | h3{ 9 | font-size: 24px; 10 | line-height: 29px; 11 | margin: 0 0 10px; 12 | } 13 | p{ 14 | font-size: 16px; 15 | margin: 0; 16 | } 17 | @media screen and (max-width: $small){ 18 | padding: 50px; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/js/block/components/insertImage.js: -------------------------------------------------------------------------------- 1 | const { createBlock } = wp.blocks; 2 | 3 | const InsertImage = (url = '', caption = '', alt = '') => { 4 | if(url === ''){ 5 | return false; 6 | } 7 | const block = createBlock("core/image", { 8 | url: url, 9 | caption: caption, 10 | alt: alt 11 | }); 12 | wp.data.dispatch('core/editor').insertBlocks(block) 13 | } 14 | export default InsertImage; -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | # PHP PSR-2 Coding Standards 6 | # http://www.php-fig.org/psr/psr-2/ 7 | 8 | root = true 9 | 10 | [*] 11 | charset = utf-8 12 | end_of_line = lf 13 | insert_final_newline = true 14 | trim_trailing_whitespace = true 15 | indent_style = tab 16 | indent_size = 3 17 | -------------------------------------------------------------------------------- /vendor/connekt-plugin-installer/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | # PHP PSR-2 Coding Standards 6 | # http://www.php-fig.org/psr/psr-2/ 7 | 8 | root = true 9 | 10 | [*] 11 | charset = utf-8 12 | end_of_line = lf 13 | insert_final_newline = true 14 | trim_trailing_whitespace = true 15 | indent_style = tab 16 | indent_size = 3 17 | -------------------------------------------------------------------------------- /src/js/block/components/unsplash/menu.js: -------------------------------------------------------------------------------- 1 | import classnames from "classnames"; 2 | import Icon from "../icon"; 3 | 4 | const { Component } = wp.element; 5 | const { PluginSidebar, PluginSidebarMoreMenuItem } = wp.editPost; 6 | 7 | const UnsplashMenu = () => ( 8 | } 10 | target ="instant-images-sidebar" 11 | > 12 | Instant Images 13 | 14 | ); 15 | export default UnsplashMenu; -------------------------------------------------------------------------------- /webpack/dev.config.js: -------------------------------------------------------------------------------- 1 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 2 | var webpack = require('webpack'); 3 | var config = require('../webpack.config.js'); 4 | 5 | config.watch = true; 6 | config.devtool = "source-map", 7 | 8 | config.plugins.push( 9 | new webpack.DefinePlugin({ 10 | 'process.env': { 11 | NODE_ENV: '"development"' 12 | } 13 | }) 14 | ); 15 | 16 | config.plugins.push( 17 | new ExtractTextPlugin({ filename: './css/instant-images.css' }) 18 | ); 19 | 20 | module.exports = config; 21 | -------------------------------------------------------------------------------- /src/js/block/components/icon.js: -------------------------------------------------------------------------------- 1 | import classnames from "classnames"; 2 | 3 | function Icon({ color = "unsplash" }) { 4 | return ( 5 | 11 | 12 | Instant Images Logo 13 | 14 | 15 | 16 | ); 17 | } 18 | 19 | export default Icon; -------------------------------------------------------------------------------- /src/js/block/index.js: -------------------------------------------------------------------------------- 1 | import Icon from "./components/icon"; 2 | import Unsplash from './components/unsplash/index'; 3 | import UnsplashMenu from './components/unsplash/menu'; 4 | 5 | const { Fragment } = wp.element; 6 | const { PluginSidebar, PluginSidebarMoreMenuItem } = wp.editPost; 7 | const { registerPlugin } = wp.plugins; 8 | 9 | const InstantImages = () => ( 10 | 11 | 12 | 13 | 14 | ); 15 | 16 | // Register the sidebar plugin 17 | registerPlugin( 'instant-images', { 18 | render: InstantImages 19 | } 20 | ); 21 | -------------------------------------------------------------------------------- /webpack/prod.config.js: -------------------------------------------------------------------------------- 1 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 2 | var webpack = require('webpack'); 3 | var config = require('../webpack.config.js'); 4 | 5 | config.watch = false; 6 | config.entry = { 7 | 'instant-images.min': './src/js/index.js', 8 | 'instant-images-styles.min': './src/scss/style.scss', 9 | 'instant-images-block.min': './src/js/block/index.js', 10 | }; 11 | 12 | config.plugins.push( 13 | new webpack.DefinePlugin({ 14 | 'process.env': { 15 | NODE_ENV: '"production"' 16 | } 17 | }), 18 | ); 19 | 20 | config.plugins.push( 21 | new ExtractTextPlugin({ filename: './css/instant-images.min.css' }) 22 | ); 23 | 24 | module.exports = config; 25 | -------------------------------------------------------------------------------- /src/js/block/components/unsplash/index.js: -------------------------------------------------------------------------------- 1 | import classnames from "classnames"; 2 | import Icon from "../icon"; 3 | import SetFeaturedImage from "../setFeaturedImage"; 4 | import InsertImage from "../insertImage"; 5 | import PhotoList from "../../../components/PhotoList"; 6 | 7 | const { Component } = wp.element; 8 | const { PluginSidebar, PluginSidebarMoreMenuItem } = wp.editPost; 9 | 10 | 11 | const Unsplash = () => ( 12 | } 14 | name="instant-images-sidebar" 15 | title="Instant Images" 16 | > 17 |
18 | 19 |
20 |
21 | ); 22 | export default Unsplash; -------------------------------------------------------------------------------- /src/js/components/ResultsToolTip.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class ResultsToolTip extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | } 7 | 8 | 9 | resetSearch(){ 10 | let nav = document.querySelector('.control-nav'); 11 | let navItem = nav.querySelector('li a.latest'); 12 | navItem.click(); 13 | } 14 | 15 | 16 | render(){ 17 | return ( 18 |
19 | 20 | { this.props.total } 21 | 22 | this.resetSearch()}>x 23 |
24 | ) 25 | } 26 | } 27 | export default ResultsToolTip; -------------------------------------------------------------------------------- /dist/js/instant-images-styles.min.js: -------------------------------------------------------------------------------- 1 | !function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=219)}({219:function(e,t){}}); -------------------------------------------------------------------------------- /admin/includes/cta/permissions.php: -------------------------------------------------------------------------------- 1 | 4 | 12 |
13 |
14 |

15 |

16 |

:

17 | 18 |
19 |
20 | 21 | -------------------------------------------------------------------------------- /admin/views/unsplash.php: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 |
7 |

8 | 9 | 10 | unsplash.com'); 13 | ?> 14 |

15 | 18 |
19 | 20 | 21 | 26 |
27 |
28 |
29 |
30 |
-------------------------------------------------------------------------------- /api/test.php: -------------------------------------------------------------------------------- 1 | 'GET', 16 | 'callback' => 'instant_images_test', 17 | ) 18 | ); 19 | }); 20 | 21 | 22 | 23 | /* 24 | * test 25 | * Test REST API access 26 | * 27 | * @param $request $_POST 28 | * @return $response json 29 | * @since 3.2 30 | 31 | */ 32 | 33 | function instant_images_test( WP_REST_Request $request ) { 34 | 35 | if (is_user_logged_in() && current_user_can( apply_filters('instant_images_user_role', 'edit_theme_options') )){ 36 | 37 | error_reporting(E_ALL|E_STRICT); 38 | 39 | // Access is enable, send the response 40 | $response = array( 41 | 'success' => true 42 | ); 43 | 44 | // Send response as JSON 45 | wp_send_json($response); 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/js/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import API from './components/API'; 4 | import PhotoList from './components/PhotoList'; 5 | 6 | require('es6-promise').polyfill(); 7 | require('isomorphic-fetch'); 8 | require('./components/Helpers'); 9 | 10 | 11 | const GetPhotos = (page = 1, orderby = 'latest', service = 'unsplash') => { 12 | 13 | let container = document.querySelector('.instant-img-container'); 14 | let url = `${API.photo_api}${API.app_id}${API.posts_per_page}&page=${page}&order_by=${orderby}`; 15 | 16 | function initialize(){ 17 | 18 | // Remove init button 19 | let initWrap = container.querySelector('.initialize-wrap'); 20 | if (typeof(initWrap) != 'undefined' && initWrap != null){ 21 | initWrap.parentNode.removeChild(initWrap); 22 | } 23 | 24 | // Get Data from API 25 | fetch(url) 26 | .then((data) => data.json()) 27 | .then(function(data) { 28 | 29 | ReactDOM.render( 30 | , 31 | document.getElementById('app') 32 | ); 33 | 34 | }) 35 | .catch(function(error) { 36 | console.log(error); 37 | }); 38 | } 39 | initialize(); 40 | 41 | }; 42 | 43 | GetPhotos(1, 'latest', 'unsplash'); -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 2 | var path = require('path'); 3 | var dir = 'dist'; 4 | 5 | module.exports = { 6 | entry: { 7 | 'instant-images': './src/js/index.js', 8 | 'instant-images-styles': './src/scss/style.scss', 9 | 'instant-images-block': './src/js/block/index.js', 10 | }, 11 | output: { 12 | path: path.join(__dirname, dir), 13 | filename: "js/[name].js", 14 | }, 15 | watch: true, 16 | module: { 17 | rules: [ 18 | { 19 | test: /.jsx?$/, 20 | loader: 'babel-loader', 21 | exclude: /node_modules/, 22 | query: { 23 | presets: ['es2015', 'react'] 24 | } 25 | }, 26 | { 27 | test: /\.(jpe?g|gif|png|svg|woff|ttf|wav|mp3)$/, 28 | loader: "file-loader", 29 | options: { 30 | name: 'img/[name].[ext]', 31 | publicPath: '../' 32 | } 33 | }, 34 | { 35 | test: /\.scss$/, 36 | use: ExtractTextPlugin.extract({ 37 | fallback: "style-loader", 38 | use: [ 39 | { loader: 'css-loader', 40 | options: { 41 | sourceMap: true 42 | } 43 | }, 44 | { loader: 'postcss-loader', 45 | options: { 46 | sourceMap: true 47 | } 48 | }, 49 | { loader: 'sass-loader', 50 | options: { 51 | sourceMap: true, 52 | outputStyle: 'expanded' 53 | }, 54 | } 55 | ] 56 | }), 57 | exclude: /node_modules/, 58 | } 59 | ]}, 60 | 61 | plugins: [] 62 | 63 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "instant-images", 3 | "version": "1.0.0", 4 | "description": "One Click Unsplash Uploads", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "start": "webpack -w --mode development --config=webpack/dev.config.js", 8 | "build": "webpack -p --mode production --config=webpack/prod.config.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "classnames": "^2.2.6", 15 | "es6-promise": "^4.1.1", 16 | "imagesloaded": "^4.1.3", 17 | "isomorphic-fetch": "^2.2.1", 18 | "react": "^15.6.1", 19 | "react-dom": "^15.6.1", 20 | "resolve-url-loader": "^2.1.0", 21 | "axios": "^0.18.0" 22 | }, 23 | "devDependencies": { 24 | "autoprefixer": "^7.1.4", 25 | "babel-core": "^6.25.0", 26 | "babel-loader": "^7.1.1", 27 | "babel-polyfill": "^6.26.0", 28 | "babel-preset-env": "^1.6.0", 29 | "babel-preset-es2015": "^6.24.1", 30 | "babel-preset-react": "^6.24.1", 31 | "copy-webpack-plugin": "^4.5.0", 32 | "css-loader": "^0.28.7", 33 | "extract-text-webpack-plugin": "4.0.0-alpha.0", 34 | "file-loader": "^2.0.0", 35 | "jshint": "latest", 36 | "node-sass": "^4.5.3", 37 | "postcss-import": "^10.0.0", 38 | "postcss-loader": "^2.0.6", 39 | "sass-loader": "^6.0.6", 40 | "style-loader": "^0.18.2", 41 | "url-loader": "^1.1.2", 42 | "webpack": "^4.23.1", 43 | "webpack-cli": "^3.1.2" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/scss/partials/_gutenberg.scss: -------------------------------------------------------------------------------- 1 | .instant-images-sidebar-icon, 2 | .instant-images-sidebar-icon svg {display: inline-flex; 3 | flex: 0 0 auto; 4 | } 5 | .instant-images-sidebar-icon svg { 6 | height: 20px; 7 | width: 20px; 8 | } 9 | .instant-images-sidebar-icon svg, 10 | .instant-images-sidebar-icon svg * { 11 | stroke: #5d72c3 !important; 12 | fill: #5d72c3 !important; 13 | } 14 | 15 | .components-panel { 16 | .instant-img-container{ 17 | .load-more-wrap{ 18 | display: block; 19 | } 20 | } 21 | .no-results{ 22 | padding: 40px; 23 | h3{ 24 | font-size: 18px; 25 | } 26 | p{ 27 | font-size: 13px; 28 | } 29 | } 30 | #photos { 31 | width: 100%; 32 | margin: 0; 33 | padding: 5px; 34 | .photo{ 35 | width: 100%; 36 | display: block; 37 | opacity: 1 !important; 38 | margin: 0 0 5px; 39 | padding: 0; 40 | } 41 | } 42 | .control-nav{ 43 | padding: 0 16px 8px; 44 | border-bottom: 1px solid #e2e4e7; 45 | li{ 46 | font-size: 13px; 47 | a { 48 | padding: 16px 24px 16px 0; 49 | height: auto; 50 | line-height: 1.2; 51 | } 52 | &.search-field{ 53 | float: none; 54 | width: 100%; 55 | padding: 0; 56 | clear: both; 57 | form{ 58 | height: auto; 59 | width: calc(100% + 16px); 60 | margin-left: -8px; 61 | } 62 | &:before{ 63 | content: ""; 64 | display: table; 65 | clear: both; 66 | } 67 | input{ 68 | line-height: 34px; 69 | height: 34px; 70 | padding-right: 30px; 71 | padding-left: 8px; 72 | border-radius: 0; 73 | border-color: #e2e4e7; 74 | font-size: 13px; 75 | } 76 | button { 77 | position: absolute; 78 | right: -2px; 79 | top: 0px; 80 | width: 34px; 81 | height: 34px; 82 | } 83 | .searchResults{ 84 | right: auto; 85 | left: -5px; 86 | top: 85%; 87 | &:before, 88 | &:after{ 89 | display: none; 90 | } 91 | } 92 | } 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /vendor/connekt-plugin-installer/README.md: -------------------------------------------------------------------------------- 1 | # wordpress-plugin-installer 2 | 3 | The **Connekt Plugin Installer** is a class for displaying a list of recommended or related plugins inside of the WordPress admin. 4 | 5 | The installer displays a list of plugins that users can easily install and activate from the screen they are currently viewing. 6 | 7 | ![Connekt Plugin Installer Example](http://examples.connekthq.com/_gif/plugin-installer_2.gif) 8 | 9 | This is a perfect tool for plugin and theme developers who want to make it as easy as possible for users to install recommended or related plugins. 10 | 11 | To see a live example, install a copy of [Ajax Load More](https://wordpress.org/plugins/ajax-load-more/) and go to the Extensions section. 12 | 13 | *** 14 | 15 | ## Getting Started 16 | 17 | To get started, you'll simply need to load and initialize the class. The installer provides the required CSS and JS for display and functionality. 18 | 19 | 20 | ### Class Loader 21 | First step is to load the class into your plugin or theme. This would typically appear in `functions.php` or in the `_construct` of your plugin Class. 22 | 23 | ```php 24 | include_once('vendor/connekt-plugin-installer/class-connekt-plugin-installer.php'); 25 | ``` 26 | 27 | 28 | ### Display 29 | Next, build an array of plugin slugs and pass the array to the `init` method for display. 30 | 31 | ```php 32 | $plugin_array = array( 33 | array( 34 | 'slug' => 'ajax-load-more', 35 | ), 36 | array( 37 | 'slug' => 'velocity', 38 | ), 39 | array( 40 | 'slug' => 'instant-images' 41 | ), 42 | array( 43 | 'slug' => 'easy-query' 44 | ) 45 | ); 46 | 47 | if(class_exists('Connekt_Plugin_Installer')){ 48 | Connekt_Plugin_Installer::init($plugin_array); 49 | } 50 | ``` 51 | 52 | And that's it. Happy coding :) 53 | 54 | *** 55 | 56 | ## Notes 57 | - Plugins _must_ be available on the wordpress.org plugin repository to be installed and activated using this class. 58 | - Using this class outside of the plugins directory will require modification to the `CNKT_INSTALLER_PATH` constant for loading assets. You can define this constant in `functions.php` prior to loading the class. `define('CNKT_INSTALLER_PATH', get_template_directory_uri() .'/vendor/connekt-plugin-installer/')`; 59 | 60 | ## License 61 | 62 | The code is available under the [GPLv2 license](https://github.com/dcooney/wordpress-plugin-installer/blob/master/LICENSE) 63 | -------------------------------------------------------------------------------- /src/scss/partials/_onboarding.scss: -------------------------------------------------------------------------------- 1 | .onboarding { 2 | position: relative; 3 | position: absolute; 4 | z-index: 9999; 5 | width: auto; 6 | font-size: 14px; 7 | line-height: 1.4; 8 | color: #777; 9 | 10 | &.bottom{ 11 | //margin-top: -40px; 12 | } 13 | 14 | .inner{ 15 | display: block; 16 | position: relative; 17 | padding: 0 55px 0 25px; 18 | line-height: 46px; 19 | height: 46px; 20 | border-radius: 4px; 21 | background: #fff; 22 | border: 1px solid #ccc; 23 | box-shadow: 0 3px 3px rgba(0, 0, 0, 0.1); 24 | &:after, 25 | &:before { 26 | bottom: 100%; 27 | left: 50%; 28 | border: solid transparent; 29 | content: " "; 30 | height: 0; 31 | width: 0; 32 | position: absolute; 33 | pointer-events: none; 34 | } 35 | &:after { 36 | border-color: rgba(136, 183, 213, 0); 37 | border-bottom-color: #fff; 38 | border-width: 10px; 39 | margin-left: -10px; 40 | } 41 | &:before { 42 | border-color: rgba(194, 225, 245, 0); 43 | border-bottom-color: #ccc; 44 | border-width: 11px; 45 | margin-left: -11px; 46 | } 47 | } 48 | 49 | &.bottom{ 50 | .inner{ 51 | &:after, 52 | &:before { 53 | bottom: auto; 54 | top: 100%; 55 | } 56 | &:after { 57 | border-color: rgba(136, 183, 213, 0); 58 | border-bottom-color: transparent; 59 | border-top-color: #fff; 60 | border-width: 10px; 61 | margin-left: -10px; 62 | } 63 | &:before { 64 | border-color: rgba(194, 225, 245, 0); 65 | border-bottom-color: transparent; 66 | border-top-color: #ccc; 67 | border-width: 11px; 68 | margin-left: -11px; 69 | } 70 | } 71 | } 72 | 73 | a{ 74 | position: absolute; 75 | top: 8px; 76 | right: 8px; 77 | padding: 0 !important; 78 | margin: 0 !important; 79 | font-size: 20px; 80 | color: #222; 81 | background: #f7f7f7; 82 | border: 1px solid #efefef; 83 | line-height: 28px !important; 84 | height: 28px !important; 85 | width: 28px !important; 86 | border-radius: 3px; 87 | display: block; 88 | text-align: center; 89 | text-decoration: none; 90 | &:hover, 91 | &:focus{ 92 | color: #fff !important; 93 | background-color: #df3333 !important; 94 | border-color: #df3333 !important; 95 | } 96 | } 97 | } 98 | 99 | -------------------------------------------------------------------------------- /admin/includes/unsplash-settings.php: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 | 5 |
6 |

7 |

.

8 |
9 |
10 | 15 |
16 | 17 |
18 |
19 |
20 |
21 |
22 |
23 |

24 |

.

25 |
26 |

4.0

27 |
    28 |
  • Gutenberg Support - Instant Images directly integrates with Gutenberg as a plugin sidebar..
  • 29 |
  • Better image uploading and error handling.
  • 30 |
  • Added `instant_images_user_role` filter to allow for control over user capability.
  • 31 |
  • Updated to meet revised Unsplash API guidelines.
  • 32 |
  • Adding support for searching individual photos by Unsplash ID - searching
    id:{photo_id}
    will return a single result.
    e.g.
    id:YiUi00uqKk8
  • 33 |
34 |
35 |
36 | 37 | 'ajax-load-more', 41 | ), 42 | array( 43 | 'slug' => 'easy-query' 44 | ), 45 | array( 46 | 'slug' => 'velocity', 47 | ) 48 | ); 49 | ?> 50 |
51 |

52 |

Instant Images is made with by Connekt

53 |
54 | 59 |
60 |
61 | 62 |
63 | 64 |
-------------------------------------------------------------------------------- /src/scss/partials/_settings.scss: -------------------------------------------------------------------------------- 1 | .instant-images-settings{ 2 | display: none; 3 | background-color: #efefef; 4 | border-top: 1px solid #e7e7e7; 5 | border-bottom: 1px solid #e7e7e7; 6 | .cnkt-sidebar{ 7 | padding: 20px 25px; 8 | overflow: hidden; 9 | .cta{ 10 | float: left; 11 | width: 50%; 12 | @media screen and (max-width: $small){ 13 | float: none !important; 14 | width: 100% !important; 15 | } 16 | &.ii-settings{ 17 | width: 31.333% 18 | } 19 | &.ii-plugins{ 20 | width: 68.666%; 21 | width: calc(68.666% - 25px); 22 | float: right; 23 | } 24 | h2{ 25 | border: none; 26 | padding: 17px 20px 3px; 27 | font-size: 16px; 28 | &.w-border{ 29 | border-top: 1px solid #e7e7e7; 30 | } 31 | } 32 | h2 + p{ 33 | padding: 0 20px 15px; 34 | margin: 0 !important; 35 | border-bottom: 1px solid #e7e7e7; 36 | } 37 | h2, 38 | h2 + p{ 39 | background: #f7f7f7; 40 | margin: 0; 41 | } 42 | ul.whats-new{ 43 | list-style: disc; 44 | padding: 0 0 10px 20px; 45 | li{ 46 | margin: 10px 0 0; 47 | pre{ 48 | display: inline-block; 49 | margin: 0; 50 | padding: 3px; 51 | background: #f7f7f7; 52 | border-radius: 2px; 53 | box-shadow: 0 0 0 1px #efefef; 54 | } 55 | } 56 | } 57 | } 58 | .cta{ 59 | background: #fff; 60 | padding: 0 0 20px; 61 | margin: 0 0 20px; 62 | overflow: hidden; 63 | position: relative; 64 | border: 1px solid #e7e7e7; 65 | } 66 | .cta.padding-bottom{ 67 | padding-bottom: 66px; 68 | } 69 | .cnkt-sidebar h3, 70 | .cnkt-sidebar h4{ 71 | margin-top: 0; 72 | } 73 | .cta-wrap{ 74 | display: block; 75 | padding: 10px 20px; 76 | h4{ 77 | padding: 10px 0 7px; 78 | margin: 0; 79 | span{ 80 | display: inline-block; 81 | line-height: 1; 82 | padding: 8px 10px; 83 | border-radius: 2px; 84 | background: #ffffcc; 85 | color: #666; 86 | } 87 | } 88 | } 89 | .cnkt-plugin-installer .plugin{ 90 | width: 48%; 91 | margin: 2% 1% 0; 92 | @media screen and (max-width: 1170px){ 93 | width: 100%; 94 | margin: 2% 0 0; 95 | } 96 | h2{ 97 | border: none; 98 | padding: 0; 99 | font-size: 16px; 100 | } 101 | h2 + p{ 102 | padding: 0; 103 | margin: 0 !important; 104 | border-bottom: none; 105 | } 106 | h2, 107 | h2 + p{ 108 | background: none; 109 | margin: 0; 110 | } 111 | } 112 | } 113 | table{ 114 | margin-top: 5px; 115 | } 116 | } -------------------------------------------------------------------------------- /src/js/components/Helpers.js: -------------------------------------------------------------------------------- 1 | 2 | //Polyfill for Array.from 3 | // Production steps of ECMA-262, Edition 6, 22.1.2.1 4 | if (!Array.from) { 5 | Array.from = (function () { 6 | var toStr = Object.prototype.toString; 7 | var isCallable = function (fn) { 8 | return typeof fn === 'function' || toStr.call(fn) === '[object Function]'; 9 | }; 10 | var toInteger = function (value) { 11 | var number = Number(value); 12 | if (isNaN(number)) { return 0; } 13 | if (number === 0 || !isFinite(number)) { return number; } 14 | return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number)); 15 | }; 16 | var maxSafeInteger = Math.pow(2, 53) - 1; 17 | var toLength = function (value) { 18 | var len = toInteger(value); 19 | return Math.min(Math.max(len, 0), maxSafeInteger); 20 | }; 21 | 22 | // The length property of the from method is 1. 23 | return function from(arrayLike/*, mapFn, thisArg */) { 24 | // 1. Let C be the this value. 25 | var C = this; 26 | 27 | // 2. Let items be ToObject(arrayLike). 28 | var items = Object(arrayLike); 29 | 30 | // 3. ReturnIfAbrupt(items). 31 | if (arrayLike == null) { 32 | throw new TypeError('Array.from requires an array-like object - not null or undefined'); 33 | } 34 | 35 | // 4. If mapfn is undefined, then let mapping be false. 36 | var mapFn = arguments.length > 1 ? arguments[1] : void undefined; 37 | var T; 38 | if (typeof mapFn !== 'undefined') { 39 | // 5. else 40 | // 5. a If IsCallable(mapfn) is false, throw a TypeError exception. 41 | if (!isCallable(mapFn)) { 42 | throw new TypeError('Array.from: when provided, the second argument must be a function'); 43 | } 44 | 45 | // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined. 46 | if (arguments.length > 2) { 47 | T = arguments[2]; 48 | } 49 | } 50 | 51 | // 10. Let lenValue be Get(items, "length"). 52 | // 11. Let len be ToLength(lenValue). 53 | var len = toLength(items.length); 54 | 55 | // 13. If IsConstructor(C) is true, then 56 | // 13. a. Let A be the result of calling the [[Construct]] internal method 57 | // of C with an argument list containing the single item len. 58 | // 14. a. Else, Let A be ArrayCreate(len). 59 | var A = isCallable(C) ? Object(new C(len)) : new Array(len); 60 | 61 | // 16. Let k be 0. 62 | var k = 0; 63 | // 17. Repeat, while k < len… (also steps a - h) 64 | var kValue; 65 | while (k < len) { 66 | kValue = items[k]; 67 | if (mapFn) { 68 | A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k); 69 | } else { 70 | A[k] = kValue; 71 | } 72 | k += 1; 73 | } 74 | // 18. Let putStatus be Put(A, "length", len, true). 75 | A.length = len; 76 | // 20. Return A. 77 | return A; 78 | }; 79 | }()); 80 | } -------------------------------------------------------------------------------- /admin/assets/js/admin.js: -------------------------------------------------------------------------------- 1 | var instant_images = instant_images || {}; 2 | 3 | jQuery(document).ready(function($) { 4 | "use strict"; 5 | 6 | var init = true; 7 | var speed = 350; 8 | 9 | 10 | 11 | // Media Uploader 12 | instant_images.setEditor = function(frame){ 13 | 14 | // vars 15 | var Parent = wp.media.view.Router; 16 | 17 | wp.media.view.Router = Parent.extend({ 18 | 19 | addNav: function(){ 20 | 21 | // Button 22 | var $a = $(' '+ instant_img_localize.instant_images +''); 23 | 24 | // Click event 25 | $a.on('click', function( e ){ 26 | e.preventDefault(); 27 | // Set active state of #instant_images_modal 28 | frame.addClass('active'); 29 | }); 30 | 31 | this.$el.append($a); // append 32 | }, 33 | 34 | initialize: function(){ 35 | Parent.prototype.initialize.apply( this, arguments ); 36 | this.addNav(); // add buttons 37 | return this; // return 38 | } 39 | }); 40 | 41 | if(frame.length){ 42 | $('.close-ii-modal').on('click', function(e){ 43 | e.preventDefault(); 44 | frame.removeClass('active'); 45 | }); 46 | } 47 | }; 48 | 49 | if(wp.media){ 50 | var frame = $('#instant_images_modal'); 51 | if(frame.length){ 52 | instant_images.setEditor(frame); 53 | } 54 | } 55 | 56 | 57 | 58 | // CLose 59 | $(document).on('click', '.media-modal-backdrop', function(e){ 60 | //alert("meow"); 61 | e.preventDefault(); 62 | frame.removeClass('active'); 63 | }); 64 | 65 | 66 | 67 | // Show Settings 68 | var settingsDiv = $('.instant-images-settings'); 69 | $('.header-wrap button').on('click', function(){ 70 | var el = $(this); 71 | if(settingsDiv.hasClass('active')){ 72 | settingsDiv.slideUp(speed, function(){ 73 | el.find('i').removeClass('fa-close').addClass('fa-cog'); 74 | settingsDiv.removeClass('active'); 75 | }); 76 | }else{ 77 | settingsDiv.slideDown(speed, function(){ 78 | el.find('i').addClass('fa-close').removeClass('fa-cog'); 79 | settingsDiv.addClass('active'); 80 | }); 81 | } 82 | }); 83 | 84 | 85 | 86 | // Save Form 87 | $('#unsplash-form-options').submit(function() { 88 | $('.save-settings .loading').fadeIn(); 89 | $(this).ajaxSubmit({ 90 | success: function(){ 91 | $('.save-settings .loading').fadeOut(speed, function(){ 92 | //window.location.reload(); 93 | }); 94 | }, 95 | error: function(){ 96 | $('.save-settings .loading').fadeOut(); 97 | alert("Sorry, settings could not be saved"); 98 | } 99 | }); 100 | return false; 101 | }); 102 | 103 | 104 | }); -------------------------------------------------------------------------------- /vendor/connekt-plugin-installer/assets/installer.js: -------------------------------------------------------------------------------- 1 | var cnkt_installer = cnkt_installer || {}; 2 | 3 | jQuery(document).ready(function($) { 4 | 5 | "use strict"; 6 | 7 | var is_loading = false; 8 | 9 | 10 | 11 | /* 12 | * install_plugin 13 | * Install the plugin 14 | * 15 | * 16 | * @param el object Button element 17 | * @param plugin string Plugin slug 18 | * @since 1.0 19 | */ 20 | 21 | cnkt_installer.install_plugin = function(el, plugin){ 22 | 23 | // Confirm activation 24 | var r = confirm(cnkt_installer_localize.install_now); 25 | 26 | if (r) { 27 | 28 | is_loading = true; 29 | el.addClass('installing'); 30 | 31 | $.ajax({ 32 | type: 'POST', 33 | url: cnkt_installer_localize.ajax_url, 34 | data: { 35 | action: 'cnkt_plugin_installer', 36 | plugin: plugin, 37 | nonce: cnkt_installer_localize.admin_nonce, 38 | dataType: 'json' 39 | }, 40 | success: function(data) { 41 | if(data){ 42 | if(data.status === 'success'){ 43 | el.attr('class', 'activate button button-primary'); 44 | el.html(cnkt_installer_localize.activate_btn); 45 | } else { 46 | el.removeClass('installing'); 47 | } 48 | } else { 49 | el.removeClass('installing'); 50 | } 51 | is_loading = false; 52 | }, 53 | error: function(xhr, status, error) { 54 | console.log(status); 55 | el.removeClass('installing'); 56 | is_loading = false; 57 | } 58 | }); 59 | 60 | } 61 | } 62 | 63 | 64 | 65 | /* 66 | * activate_plugin 67 | * Activate the plugin 68 | * 69 | * 70 | * @param el object Button element 71 | * @param plugin string Plugin slug 72 | * @since 1.0 73 | */ 74 | 75 | cnkt_installer.activate_plugin = function(el, plugin){ 76 | 77 | $.ajax({ 78 | type: 'POST', 79 | url: cnkt_installer_localize.ajax_url, 80 | data: { 81 | action: 'cnkt_plugin_activation', 82 | plugin: plugin, 83 | nonce: cnkt_installer_localize.admin_nonce, 84 | dataType: 'json' 85 | }, 86 | success: function(data) { 87 | if(data){ 88 | if(data.status === 'success'){ 89 | el.attr('class', 'installed button disabled'); 90 | el.html(cnkt_installer_localize.installed_btn); 91 | } 92 | } 93 | is_loading = false; 94 | }, 95 | error: function(xhr, status, error) { 96 | console.log(status); 97 | is_loading = false; 98 | } 99 | }); 100 | 101 | }; 102 | 103 | 104 | 105 | /* 106 | * Install/Activate Button Click 107 | * 108 | * @since 1.0 109 | */ 110 | 111 | $(document).on('click', '.cnkt-plugin-installer a.button', function(e){ 112 | var el = $(this), 113 | plugin = el.data('slug'); 114 | 115 | e.preventDefault(); 116 | 117 | if(!el.hasClass('disabled')){ 118 | 119 | if(is_loading) return false; 120 | 121 | // Installation 122 | if(el.hasClass('install')){ 123 | cnkt_installer.install_plugin(el, plugin); 124 | } 125 | 126 | // Activation 127 | if(el.hasClass('activate')){ 128 | cnkt_installer.activate_plugin(el, plugin); 129 | } 130 | } 131 | }); 132 | 133 | 134 | }); 135 | -------------------------------------------------------------------------------- /vendor/connekt-plugin-installer/assets/installer.css: -------------------------------------------------------------------------------- 1 | .cnkt-plugin-installer{ 2 | box-sizing: border-box; 3 | display: -webkit-box; 4 | display: -moz-box; 5 | display: -ms-flexbox; 6 | display: -webkit-flex; 7 | display: flex; 8 | -webkit-flex-wrap: wrap; 9 | flex-wrap: wrap; 10 | flex-flow: row wrap; 11 | -webkit-flex-flow: row wrap; 12 | justify-content: space-between; 13 | width: 102%; 14 | position: relative; 15 | left: -1%; 16 | } 17 | .cnkt-plugin-installer:after { 18 | content: ""; 19 | flex: auto; 20 | } 21 | .cnkt-plugin-installer * { 22 | box-sizing: border-box; 23 | } 24 | .cnkt-plugin-installer .plugin{ 25 | width: 31.333%; 26 | margin: 0 1% 30px; 27 | padding: 0; 28 | overflow: hidden; 29 | text-align: left; 30 | border: 1px solid #e1e1e1; 31 | background: #fff; 32 | position: relative; 33 | border-radius: 2px; 34 | } 35 | .cnkt-plugin-installer .plugin:hover{ 36 | border-color: #cecece; 37 | } 38 | 39 | .cnkt-plugin-installer .plugin-wrap{ 40 | padding: 20px 20px 95px 145px; 41 | min-height: 214px; 42 | display: block; 43 | position: relative; 44 | } 45 | .cnkt-plugin-installer li a, 46 | .cnkt-plugin-installer .plugin-wrap a{ 47 | text-decoration: none; 48 | } 49 | .cnkt-plugin-installer .plugin-wrap img{ 50 | display: block; 51 | position: absolute; 52 | left: 20px; 53 | top: 20px; 54 | max-width: 108px; 55 | max-height: 108px; 56 | border: 1px solid #f7f7f7; 57 | } 58 | .cnkt-plugin-installer .plugin-wrap h2, 59 | .cnkt-plugin-installer .plugin-wrap p{ 60 | padding: 0; 61 | margin: 0; 62 | font-size: 17px; 63 | font-weight: 600; 64 | color: #333; 65 | line-height: 1.4; 66 | } 67 | .cnkt-plugin-installer .plugin-wrap p{ 68 | padding: 10px 0 0; 69 | margin: 0; 70 | font-size: 14px; 71 | font-weight: 400; 72 | color: #777; 73 | } 74 | .cnkt-plugin-installer .plugin-wrap p.plugin-author{ 75 | font-size: 13px; 76 | padding-top: 20px; 77 | font-style: italic; 78 | } 79 | 80 | .cnkt-plugin-installer .activation-row{ 81 | display: block; 82 | margin: 0; 83 | padding: 20px; 84 | background: #f7f7f7; 85 | border-top: 1px solid #e1e1e1; 86 | border-radius: 0 0 2px 2px; 87 | position: absolute; 88 | bottom: 0; 89 | width: 100%; 90 | overflow: hidden; 91 | border-radius: 0 0 2px 2px; 92 | text-align: left; 93 | } 94 | .cnkt-plugin-installer .activation-row li{ 95 | display: inline-block; 96 | vertical-align: top; 97 | margin: 0 10px 0 0; 98 | font-size: 13px; 99 | line-height: 27px; 100 | } 101 | 102 | @media screen and (max-width: 1170px){ 103 | .cnkt-plugin-installer .plugin{ 104 | width: 48%; 105 | margin: 0 1% 15px; 106 | } 107 | } 108 | @media screen and (max-width: 960px){ 109 | .cnkt-plugin-installer .plugin-wrap{ 110 | padding: 20px 20px 95px 115px; 111 | min-height: 214px; 112 | } 113 | .cnkt-plugin-installer .plugin-wrap img{ 114 | max-width: 78px; 115 | max-height: 78px; 116 | } 117 | } 118 | @media screen and (max-width: 640px){ 119 | .cnkt-plugin-installer{ 120 | width: 100%; 121 | position: static; 122 | } 123 | .cnkt-plugin-installer .plugin{ 124 | width: 100%; 125 | margin: 0 0 20px; 126 | } 127 | } 128 | 129 | 130 | -------------------------------------------------------------------------------- /dist/js/instant-images-styles.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | /******/ 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | /******/ 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) { 10 | /******/ return installedModules[moduleId].exports; 11 | /******/ } 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ i: moduleId, 15 | /******/ l: false, 16 | /******/ exports: {} 17 | /******/ }; 18 | /******/ 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | /******/ 22 | /******/ // Flag the module as loaded 23 | /******/ module.l = true; 24 | /******/ 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | /******/ 29 | /******/ 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | /******/ 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | /******/ 36 | /******/ // define getter function for harmony exports 37 | /******/ __webpack_require__.d = function(exports, name, getter) { 38 | /******/ if(!__webpack_require__.o(exports, name)) { 39 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 40 | /******/ } 41 | /******/ }; 42 | /******/ 43 | /******/ // define __esModule on exports 44 | /******/ __webpack_require__.r = function(exports) { 45 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 46 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 47 | /******/ } 48 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 49 | /******/ }; 50 | /******/ 51 | /******/ // create a fake namespace object 52 | /******/ // mode & 1: value is a module id, require it 53 | /******/ // mode & 2: merge all properties of value into the ns 54 | /******/ // mode & 4: return value when already ns object 55 | /******/ // mode & 8|1: behave like require 56 | /******/ __webpack_require__.t = function(value, mode) { 57 | /******/ if(mode & 1) value = __webpack_require__(value); 58 | /******/ if(mode & 8) return value; 59 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 60 | /******/ var ns = Object.create(null); 61 | /******/ __webpack_require__.r(ns); 62 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 63 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 64 | /******/ return ns; 65 | /******/ }; 66 | /******/ 67 | /******/ // getDefaultExport function for compatibility with non-harmony modules 68 | /******/ __webpack_require__.n = function(module) { 69 | /******/ var getter = module && module.__esModule ? 70 | /******/ function getDefault() { return module['default']; } : 71 | /******/ function getModuleExports() { return module; }; 72 | /******/ __webpack_require__.d(getter, 'a', getter); 73 | /******/ return getter; 74 | /******/ }; 75 | /******/ 76 | /******/ // Object.prototype.hasOwnProperty.call 77 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 78 | /******/ 79 | /******/ // __webpack_public_path__ 80 | /******/ __webpack_require__.p = ""; 81 | /******/ 82 | /******/ 83 | /******/ // Load entry module and return exports 84 | /******/ return __webpack_require__(__webpack_require__.s = "./src/scss/style.scss"); 85 | /******/ }) 86 | /************************************************************************/ 87 | /******/ ({ 88 | 89 | /***/ "./src/scss/style.scss": 90 | /*!*****************************!*\ 91 | !*** ./src/scss/style.scss ***! 92 | \*****************************/ 93 | /*! no static exports found */ 94 | /***/ (function(module, exports) { 95 | 96 | // removed by extract-text-webpack-plugin 97 | 98 | /***/ }) 99 | 100 | /******/ }); 101 | //# sourceMappingURL=instant-images-styles.js.map -------------------------------------------------------------------------------- /admin/includes/settings.php: -------------------------------------------------------------------------------- 1 | ' . __('Manage your media upload settings', 'instant-images') . '.

'; 65 | } 66 | 67 | 68 | /* 69 | * unsplash_sanitize 70 | * Sanitize our form fields 71 | * 72 | * @since 1.0 73 | */ 74 | 75 | function unsplash_sanitize( $input ) { 76 | return $input; 77 | } 78 | 79 | 80 | /* 81 | * unsplash_download_w_callback 82 | * Max File download width 83 | * 84 | * @since 1.0 85 | */ 86 | 87 | function unsplash_download_w_callback(){ 88 | $options = get_option( 'instant_img_settings' ); 89 | 90 | if(!isset($options['unsplash_download_w'])) 91 | $options['unsplash_download_w'] = '1600'; 92 | 93 | echo ''; 94 | echo ' '; 95 | } 96 | 97 | 98 | 99 | /* 100 | * unsplash_download_h_callback 101 | * Max File download height 102 | * 103 | * @since 1.0 104 | */ 105 | 106 | function unsplash_download_h_callback(){ 107 | $options = get_option( 'instant_img_settings' ); 108 | 109 | if(!isset($options['unsplash_download_h'])) 110 | $options['unsplash_download_h'] = '1200'; 111 | 112 | echo ''; 113 | echo ' '; 114 | } 115 | 116 | /* 117 | * instant_images_button_display_callback 118 | * Show the Instant Images button in media context 119 | * 120 | * @since 3.2.1 121 | */ 122 | 123 | function instant_images_button_display_callback(){ 124 | $options = get_option( 'instant_img_settings' ); 125 | if(!isset($options['instant_img_btn_display'])) 126 | $options['instant_img_btn_display'] = '0'; 127 | 128 | $style = 'style="position: absolute; left: 0; top: 9px;"'; // CSS style 129 | 130 | $html = ''; 131 | $html .= ''; 136 | 137 | echo $html; 138 | } 139 | -------------------------------------------------------------------------------- /api/upload.php: -------------------------------------------------------------------------------- 1 | 'POST', 16 | 'callback' => 'instant_images_upload_image', 17 | ) 18 | ); 19 | }); 20 | 21 | 22 | 23 | /* 24 | * upload_image 25 | * Upload Image to /uploads directory 26 | * 27 | * @param $request $_POST 28 | * @return $response json 29 | * @since 3.0 30 | * @updated 3.3 31 | */ 32 | 33 | function instant_images_upload_image( WP_REST_Request $request ) { 34 | 35 | if (is_user_logged_in() && current_user_can( apply_filters('instant_images_user_role', 'edit_theme_options') )){ 36 | 37 | error_reporting(E_ALL|E_STRICT); 38 | 39 | // Create /instant-images directory inside /uploads to temporarily store images 40 | if(!is_dir(INSTANT_IMG_UPLOAD_PATH)){ 41 | wp_mkdir_p(INSTANT_IMG_UPLOAD_PATH); 42 | } 43 | 44 | // Is directory writeable, if not exit with an error 45 | if (!is_writable(INSTANT_IMG_UPLOAD_PATH.'/')) { 46 | $response = json_encode( 47 | array( 48 | 'error' => true, 49 | 'msg' => __('Unable to save image, check your server permissions of `uploads/instant-instants`', 'instant-images') 50 | ) 51 | ); 52 | wp_send_json($response); 53 | } 54 | 55 | $body = json_decode($request->get_body(), true); // Get contents of request 56 | $data = json_decode($body['data']); // Info about image 57 | $path = INSTANT_IMG_UPLOAD_PATH.'/'; // Temp Image Path 58 | 59 | 60 | // Get data params from the $body 61 | if($data){ 62 | $id = sanitize_key($data->id); // Image ID 63 | $img = sanitize_text_field($data->image); // Image URL 64 | } 65 | 66 | 67 | // If ID and IMG not set, exit 68 | if(!isset($id) || !isset($img)){ 69 | $response = array( 70 | 'error' => true, 71 | 'msg' => __('An issue occurred retrieving image info via the REST API.', 'instant-images'), 72 | 'path' => $path, 73 | 'filename' => $filename 74 | ); 75 | wp_send_json($response); 76 | } 77 | 78 | 79 | // Create temp. image variables 80 | $filename = $id.'.jpg'; 81 | $img_path = $path .''.$filename; 82 | 83 | 84 | if(function_exists('copy')){ 85 | 86 | // Save file to server using copy() function 87 | $saved_file = @copy($img.'jpg', $img_path); 88 | 89 | // Was the temporary image saved? 90 | if ($saved_file) { 91 | 92 | if(file_exists($path.''.$filename)){ 93 | 94 | // SUCCESS - Image saved 95 | $response = array( 96 | 'error' => false, 97 | 'msg' => __('Image successfully uploaded to server.', 'instant-images'), 98 | 'path' => $path, 99 | 'filename' => $filename 100 | ); 101 | 102 | }else{ 103 | 104 | // ERROR - File does NOT exist 105 | $response = array( 106 | 'error' => true, 107 | 'msg' => __('Uploaded image not found, please ensure you have proper permissions set on the uploads directory.', 'instant-images'), 108 | 'path' => '', 109 | 'filename' => '' 110 | ); 111 | 112 | } 113 | 114 | } else { 115 | 116 | // ERROR - Error on save 117 | $response = array( 118 | 'error' => true, 119 | 'msg' => __('Unable to download image to server, please check the server permissions of the instant-images folder in your WP uploads directory.', 'instant-images'), 120 | 'path' => '', 121 | 'filename' => '' 122 | ); 123 | 124 | } 125 | 126 | } 127 | 128 | // copy() not enabled 129 | else{ 130 | 131 | $response = array( 132 | 'error' => true, 133 | 'msg' => __('The core PHP copy() function is not available on your server. Please contact your server administrator to upgrade your PHP version.', 'instant-images'), 134 | 'path' => $path, 135 | 'filename' => $filename 136 | ); 137 | 138 | } 139 | 140 | wp_send_json($response); 141 | 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /api/resize.php: -------------------------------------------------------------------------------- 1 | 'POST', 16 | 'callback' => 'instant_images_resize_image', 17 | ) 18 | ); 19 | }); 20 | 21 | 22 | 23 | /* 24 | * resize_image 25 | * Resize Image and run thru media uploader 26 | * 27 | * @param $request $_POST 28 | * @return $response json 29 | * @since 3.0 30 | */ 31 | 32 | function instant_images_resize_image( WP_REST_Request $request ) { 33 | 34 | if (is_user_logged_in() && current_user_can( apply_filters('instant_images_user_role', 'edit_theme_options') )){ 35 | 36 | error_reporting(E_ALL|E_STRICT); 37 | 38 | require_once( ABSPATH . 'wp-admin/includes/file.php' ); // download_url() 39 | require_once( ABSPATH . 'wp-admin/includes/image.php' ); // wp_read_image_metadata() 40 | 41 | // WP Options 42 | $options = get_option( 'instant_img_settings' ); 43 | $download_w = isset($options['unsplash_download_w']) ? $options['unsplash_download_w'] : 1600; // width 44 | $download_h = isset($options['unsplash_download_h']) ? $options['unsplash_download_h'] : 1200; // height 45 | 46 | // Get JSON Data 47 | $body = json_decode($request->get_body(), true); // Get contents of request body 48 | $data = json_decode($body['data']); // Get contents of data 49 | 50 | if($body && $data){ 51 | 52 | $path = sanitize_text_field($data->path); // Path on server 53 | $name = sanitize_text_field($data->filename); // name 54 | $filename = $path . $name; // full filename 55 | $filetype = wp_check_filetype( basename( $filename ), null ); 56 | $title = sanitize_text_field($data->title); // Title 57 | $alt = sanitize_text_field($data->alt); // Alt text 58 | $caption = sanitize_text_field($data->caption); // Caption text 59 | $custom_filename = sanitize_title($data->custom_filename); // Custom filename 60 | 61 | $name = (!empty($custom_filename)) ? $custom_filename .'.jpg' : $name; 62 | 63 | // Resize image to max size (set in Settings) 64 | $image = wp_get_image_editor( $filename ); 65 | if ( ! is_wp_error( $image ) ) { 66 | $image->resize( $download_w, $download_h, false ); 67 | $image->save( $filename ); 68 | } 69 | 70 | // Get upload directory 71 | $wp_upload_dir = wp_upload_dir(); // ['path'] ['basedir'] 72 | 73 | // Copy file from uploads/instant-images to a media library directory. 74 | $new_filename = $wp_upload_dir['path'] .'/'. $name; 75 | $copy_file = @copy($filename , $new_filename); 76 | 77 | if(!$copy_file){ 78 | 79 | // Error 80 | $response = array( 81 | 'success' => false, 82 | 'msg' => __('Unable to copy image to the media library. Please check your server permissions.', 'instant-images') 83 | ); 84 | 85 | } else { 86 | 87 | // Build attachment array 88 | $attachment = array( 89 | 'guid'=> $wp_upload_dir['url'] . basename( $new_filename ), 90 | 'post_mime_type' => $filetype['type'], 91 | 'post_title' => $title, 92 | 'post_excerpt' => $caption, 93 | 'post_content' => '', 94 | 'post_status' => 'inherit' 95 | ); 96 | 97 | $image_id = wp_insert_attachment($attachment, $new_filename, 0); // Insert as attachment 98 | 99 | update_post_meta( $image_id, '_wp_attachment_image_alt', $alt ); // Add alt text 100 | 101 | $attach_data = wp_generate_attachment_metadata( $image_id, $new_filename ); // Generate metadata 102 | wp_update_attachment_metadata( $image_id, $attach_data ); // Add metadata 103 | 104 | 105 | // Response 106 | if(file_exists($new_filename)){ // If image was uploaded temporary image 107 | 108 | // Success 109 | $response = array( 110 | 'success' => true, 111 | 'msg' => __('Image successfully uploaded to your media library!', 'instant-images'), 112 | 'id' => $image_id, 113 | 'url' => wp_get_attachment_url( $image_id ) 114 | ); 115 | 116 | }else{ 117 | 118 | // Error 119 | $response = array( 120 | 'success' => false, 121 | 'msg' => __('There was an error sending the image to your media library. Please check your server permissions and confirm the upload_max_filesize setting (php.ini) is large enough for the downloaded image (8mb minimum is recommended).', 'instant-images'), 122 | 'id' => '', 123 | 'url' => '' 124 | ); 125 | } 126 | } 127 | 128 | // Delete temporary image 129 | if(file_exists($filename)){ 130 | unlink($filename); 131 | } 132 | 133 | wp_send_json($response); // Send response as JSON 134 | 135 | } else { 136 | 137 | $response = array( 138 | 'success' => false, 139 | 'msg' => __('There was an error resizing the image, please try again.', 'instant-images'), 140 | 'id' => '', 141 | 'url' => '' 142 | ); 143 | wp_send_json($response); // Send response as JSON 144 | 145 | } 146 | 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /admin/admin.php: -------------------------------------------------------------------------------- 1 | __('Instant Images', 'instant-images') ); 113 | $tabs = array_merge( $tabs, $newtab ); 114 | return $tabs; 115 | } 116 | } 117 | add_filter('media_upload_tabs', 'instant_img_media_upload_tabs_handler'); 118 | 119 | 120 | 121 | /** 122 | * instant_img_media_buttons_context_handler 123 | * Add pop up content to edit, new and post pages 124 | * 125 | * @since 3.2.1 126 | */ 127 | function instant_img_media_buttons_context_handler() { 128 | $show_tab = instant_img_show_tabs(); 129 | if($show_tab){ 130 | return ' '. __('Instant Images', 'instant-images') .' '; 131 | } 132 | } 133 | add_filter('media_buttons_context', 'instant_img_media_buttons_context_handler'); 134 | 135 | 136 | 137 | /** 138 | * media_upload_instant_images_handler 139 | * Add instant images to the iframe 140 | * 141 | * @since 3.2.1 142 | */ 143 | function media_upload_instant_images_handler() { 144 | wp_iframe('media_instant_img_tab'); 145 | } 146 | add_action('media_upload_instant_img_tab', 'media_upload_instant_images_handler'); 147 | 148 | 149 | 150 | /** 151 | * media_instant_img_popup_content 152 | * Add pop up content to edit, new and post pages 153 | * 154 | * @since 2.0 155 | */ 156 | function media_instant_img_tab() { 157 | //media_upload_header(); 158 | instant_img_scripts(); 159 | $show_settings = false; 160 | ?> 161 |
162 | 163 |
164 | '; 179 | include( INSTANT_IMG_PATH . 'admin/views/unsplash.php'); 180 | echo ''; 181 | } 182 | 183 | 184 | 185 | /* 186 | * instant_img_filter_admin_footer_text 187 | * Filter the WP Admin footer text 188 | * 189 | * @since 2.0 190 | */ 191 | 192 | function instant_img_filter_admin_footer_text( $text ) { 193 | $screen = get_current_screen(); 194 | $base = 'media_page_'.INSTANT_IMG_NAME; 195 | if($screen->base === $base){ 196 | echo INSTANT_IMG_TITLE .' '.'is made with by Connekt | Leave a Review | FAQs'; 197 | } 198 | } 199 | add_filter( 'admin_footer_text', 'instant_img_filter_admin_footer_text'); // Admin menu text 200 | 201 | 202 | -------------------------------------------------------------------------------- /src/scss/partials/_nav.scss: -------------------------------------------------------------------------------- 1 | .instant-img-container{ 2 | .control-nav{ 3 | display: block; 4 | margin: 0; 5 | padding: 25px 0; 6 | list-style: none; 7 | &:after{ 8 | content: ""; 9 | display: table; 10 | clear: both; 11 | } 12 | li{ 13 | padding: 0; 14 | margin: 0 3px 0 0; 15 | float: left; 16 | background: none; 17 | font-size: 18px; 18 | position: relative; 19 | a{ 20 | padding: 0 24px 0 2px; 21 | height: 48px; 22 | line-height: 48px; 23 | display: block; 24 | color: #999; 25 | text-decoration: none; 26 | background-position: 96% center; 27 | background-repeat: no-repeat; 28 | &.active{ 29 | color: #111; 30 | cursor: default; 31 | font-weight: 600; 32 | } 33 | &:hover, 34 | &:focus{ 35 | color: #111; 36 | outline: none; 37 | box-shadow: none; 38 | } 39 | &.loading{ 40 | background-image: url('../img/ajax-loader.gif'); 41 | } 42 | } 43 | @media screen and (max-width: $small){ 44 | font-size: 16px; 45 | margin: 0; 46 | a{ 47 | padding-left: 3px; 48 | } 49 | } 50 | 51 | @media screen and (max-width: $xsmall){ 52 | width: 33.333%; 53 | text-align: center; 54 | margin: 0; 55 | padding-bottom: 15px; 56 | } 57 | &.search-field{ 58 | float: right; 59 | width: 49%; 60 | margin: 0; 61 | max-width: 500px; 62 | @media screen and (max-width: $xsmall){ 63 | width: 100%; 64 | display: block; 65 | position: static; 66 | padding-bottom: 15px; 67 | text-align: left; 68 | max-width: 100%; 69 | } 70 | .searchResults{ 71 | position: absolute; 72 | right: 100.5%; 73 | top: 7px; 74 | width: auto; 75 | height: 34px; 76 | line-height: 34px; 77 | padding: 0 27px 0 10px; 78 | background: #ffffbf; 79 | border: 1px solid #ebebae; 80 | border-radius: 3px; 81 | z-index: 99; 82 | font-size: 13px; 83 | font-weight: 600; 84 | transition: all 0.25s ease; 85 | color: #444; 86 | text-shadow: 0 1px 1px rgba(255, 255, 255, 0.3); 87 | box-shadow: 0 2px 3px rgba(0, 0, 0, 0.05); 88 | span{ 89 | cursor: help; 90 | } 91 | span, a{ 92 | line-height: 34px; 93 | } 94 | a{ 95 | padding: 0 10px; 96 | height: 34px; 97 | line-height: 33px; 98 | position: absolute; 99 | right: 0; 100 | top: 0; 101 | } 102 | &:after, 103 | &:before { 104 | left: 100%; 105 | top: 50%; 106 | border: solid transparent; 107 | content: " "; 108 | height: 0; 109 | width: 0; 110 | position: absolute; 111 | pointer-events: none; 112 | z-index: 100; 113 | } 114 | &:after { 115 | border-color: rgba(223, 225, 173, 0); 116 | border-left-color: #ffffbf; 117 | border-width: 6px; 118 | margin-top: -6px; 119 | } 120 | &:before { 121 | border-color: rgba(0, 0, 0, 0); 122 | border-left-color: #ebebae; 123 | border-width: 7px; 124 | margin-top: -7px; 125 | } 126 | &.hide{ 127 | opacity: 0; 128 | visibility: hidden; 129 | } 130 | } 131 | form{ 132 | padding: 0 1px 0 0; 133 | margin: 0; 134 | position: relative; 135 | height: 48px; 136 | display: block; 137 | &:hover{ 138 | button{ 139 | opacity: 1; 140 | } 141 | } 142 | } 143 | input{ 144 | width: 100%; 145 | padding: 0 45px 0 10px; 146 | border: 1px solid #e1e1e1; 147 | background-color: #f7f7f7 !important; 148 | height: 46px; 149 | line-height: 46px; 150 | border-radius: 5px; 151 | font-size: 16px; 152 | transition: padding 0.15s ease; 153 | &:focus{ 154 | border-color: #999; 155 | } 156 | &.searching{ 157 | padding-left: 34px; 158 | background-image: url('../img/ajax-loader.gif'); 159 | background-position: 10px center; 160 | background-repeat: no-repeat; 161 | } 162 | } 163 | button{ 164 | position: absolute; 165 | right: -2px; 166 | top: -1px; 167 | width: 48px; 168 | height: 48px; 169 | z-index: 1; 170 | border: none !important; 171 | background: transparent !important; 172 | cursor: pointer; 173 | color: #666; 174 | box-shadow: none !important; 175 | transition: all 0.25s ease; 176 | opacity: 0.5; 177 | &:hover{ 178 | outline: none; 179 | color: #5d72c3; 180 | } 181 | &:focus{ 182 | outline: none; 183 | color: #5d72c3; 184 | } 185 | } 186 | input[type=search]::-webkit-input-placeholder { 187 | color: #ccc; 188 | font-weight: 300; 189 | font-style: normal; 190 | font-size: 14px; 191 | } 192 | input[type=search]:-moz-placeholder { 193 | color: #ccc; 194 | font-weight: 300; 195 | font-style: normal; 196 | font-size: 14px; 197 | } 198 | input[type=search]::-moz-placeholder { 199 | color: #ccc; 200 | font-weight: 300; 201 | font-style: normal; 202 | font-size: 14px; 203 | } 204 | input[type=search]:-ms-input-placeholder { 205 | color: #ccc; 206 | font-weight: 300; 207 | font-style: normal; 208 | font-size: 14px; 209 | } 210 | } 211 | } 212 | } 213 | } -------------------------------------------------------------------------------- /instant-images.php: -------------------------------------------------------------------------------- 1 | includes(); 73 | $this->constants(); 74 | } 75 | 76 | 77 | 78 | /** 79 | * instant_img__block_enqueue 80 | * Enqueue script for Gutenberg Blocks 81 | * 82 | * @since 4.0 83 | */ 84 | function instant_img_block_enqueue() { 85 | $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min'; // Use minified libraries if SCRIPT_DEBUG is turned off 86 | wp_enqueue_script('instant-images-block', INSTANT_IMG_URL. 'dist/js/instant-images-block'. $suffix .'.js', '', INSTANT_IMG_VERSION, true); 87 | wp_enqueue_style('admin-instant-images', INSTANT_IMG_URL. 'dist/css/instant-images'. $suffix .'.css', '', INSTANT_IMG_VERSION); 88 | InstantImages::instant_img_localize( 'instant-images-block' ); 89 | } 90 | 91 | 92 | 93 | /** 94 | * instant_img_localize 95 | * Localization strings 96 | * 97 | * @since 2.0 98 | */ 99 | public static function instant_img_localize($script = 'instant-images-react'){ 100 | 101 | $options = get_option( 'instant_img_settings' ); 102 | $download_w = isset($options['unsplash_download_w']) ? $options['unsplash_download_w'] : 1600; // width of download file 103 | $download_h = isset($options['unsplash_download_h']) ? $options['unsplash_download_h'] : 1200; // height of downloads 104 | 105 | wp_localize_script( 106 | $script, 'instant_img_localize', array( 107 | 'instant_images' => __('Instant Images', 'instant-images'), 108 | 'root' => esc_url_raw( rest_url() ), 109 | 'nonce' => wp_create_nonce( 'wp_rest' ), 110 | 'ajax_url' => admin_url('admin-ajax.php'), 111 | 'admin_nonce' => wp_create_nonce('instant_img_nonce'), 112 | 'download_width' => $download_w, 113 | 'download_height' => $download_h, 114 | 'unsplash_default_app_id' => INSTANT_IMG_DEFAULT_APP_ID, 115 | 'unsplash_app_id' => INSTANT_IMG_DEFAULT_APP_ID, 116 | 'error_msg_title' => __('Error accessing Unsplash API', 'instant-images'), 117 | 'error_msg_desc' => __('Please check your Application ID.', 'instant-images'), 118 | 'error_upload' => __('Unable to download image to server, please check your server permissions.', 'instant-images'), 119 | 'error_resize' => __('There was an error sending the image to your media library. Please check your server permissions and confirm the upload_max_filesize setting (php.ini) is large enough for the downloaded image.', 'instant-images'), 120 | 'error_restapi' => __('There was an error accessing the WP REST API - Instant Images requires access to the WP REST API to fetch and upload images to your media library.', 'instant-images'), 121 | 'photo_by' => __('Photo by', 'instant-images'), 122 | 'view_all' => __('View All Photos by', 'instant-images'), 123 | 'upload' => __('Click Image to Upload', 'instant-images'), 124 | 'upload_btn' => __('Click to Upload', 'instant-images'), 125 | 'full_size' => __('View Full Size', 'instant-images'), 126 | 'likes' => __('Like(s)', 'instant-images'), 127 | 'saving' => __('Downloading Image...', 'instant-images'), 128 | 'resizing' => __('Resizing Image...', 'instant-images'), 129 | 'no_results' => __('Sorry, nothing matched your query', 'instant-images'), 130 | 'no_results_desc' => __('Please try adjusting your search criteria', 'instant-images'), 131 | 'latest' => __('New', 'instant-images'), 132 | 'oldest' => __('Oldest', 'instant-images'), 133 | 'popular' => __('Popular', 'instant-images'), 134 | 'load_more' => __('Load More Images', 'instant-images'), 135 | 'search' => __('Search for Toronto, Coffee + Breakfast etc...', 'instant-images'), 136 | 'search_results' => __('images found for', 'instant-images'), 137 | 'clear_search' => __('Clear Search Results', 'instant-images'), 138 | 'view_on_unsplash' => __('View Photo on Unsplash', 'instant-images'), 139 | 'set_as_featured' => __('Set as Featured Image', 'instant-images'), 140 | 'insert_into_post' => __('Insert Into Post', 'instant-images'), 141 | 'edit_filename' => __('Filename', 'instant-images'), 142 | 'edit_alt' => __('Alt Text', 'instant-images'), 143 | 'edit_caption' => __('Caption', 'instant-images'), 144 | 'edit_details' => __('Edit Image Details', 'instant-images'), 145 | 'edit_details_intro' => __('Update and save image details prior to uploading', 'instant-images'), 146 | 'cancel' => __('Cancel', 'instant-images'), 147 | 'save' => __('Save', 'instant-images') 148 | ) 149 | ); 150 | } 151 | 152 | 153 | 154 | /** 155 | * includes 156 | * Include these files in the admin 157 | * 158 | * @since 2.0 159 | */ 160 | private function includes(){ 161 | if( is_admin()){ 162 | include_once('admin/admin.php'); 163 | include_once('admin/includes/settings.php'); 164 | include_once('vendor/connekt-plugin-installer/class-connekt-plugin-installer.php'); 165 | } 166 | // REST API Routes 167 | include_once('api/resize.php'); 168 | include_once('api/test.php'); 169 | include_once('api/upload.php'); 170 | } 171 | 172 | 173 | 174 | /* 175 | * constants 176 | * Include these files in the admin 177 | * 178 | * @since 2.0 179 | */ 180 | 181 | private function constants(){ 182 | define('INSTANT_IMG_VERSION', '3.3.0'); 183 | define('INSTANT_IMG_RELEASE', 'January 10, 2019'); 184 | define('INSTANT_IMG_TITLE', 'Instant Images'); 185 | $upload_dir = wp_upload_dir(); 186 | define('INSTANT_IMG_UPLOAD_PATH', $upload_dir['basedir'].'/instant-images'); 187 | define('INSTANT_IMG_UPLOAD_URL', $upload_dir['baseurl'].'/instant-images/'); 188 | define('INSTANT_IMG_PATH', plugin_dir_path(__FILE__)); 189 | define('INSTANT_IMG_URL', plugins_url( '/', __FILE__)); 190 | define('INSTANT_IMG_ADMIN_URL', plugins_url('admin/', __FILE__)); 191 | define('INSTANT_IMG_WPADMIN_URL', admin_url( 'upload.php?page=instant-images' )); 192 | define('INSTANT_IMG_NAME', 'instant-images'); 193 | define('INSTANT_IMG_DEFAULT_APP_ID', '5746b12f75e91c251bddf6f83bd2ad0d658122676e9bd2444e110951f9a04af8'); 194 | } 195 | 196 | 197 | 198 | /* 199 | * instant_images_add_action_links 200 | * Add custom links to plugins.php 201 | * 202 | * @since 2.0 203 | */ 204 | function instant_images_add_action_links ( $links ) { 205 | $mylinks = array( 206 | 'Upload Photos', 207 | ); 208 | return array_merge( $mylinks, $links ); 209 | } 210 | 211 | } 212 | 213 | 214 | 215 | /* 216 | * InstantImages 217 | * The main function responsible for returning the one true InstantImages Instance. 218 | * 219 | * @since 2.0 220 | */ 221 | 222 | function InstantImages(){ 223 | global $InstantImages; 224 | if( !isset($InstantImages)){ 225 | $InstantImages = new InstantImages(); 226 | } 227 | return $InstantImages; 228 | } 229 | // initialize 230 | InstantImages(); 231 | -------------------------------------------------------------------------------- /lang/instant-images.pot: -------------------------------------------------------------------------------- 1 | #, fuzzy 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: Instant Images\n" 5 | "POT-Creation-Date: 2019-02-12 09:42-0500\n" 6 | "PO-Revision-Date: 2017-09-21 10:40-0500\n" 7 | "Last-Translator: Darren Cooney \n" 8 | "Language-Team: \n" 9 | "Language: en_CA\n" 10 | "MIME-Version: 1.0\n" 11 | "Content-Type: text/plain; charset=UTF-8\n" 12 | "Content-Transfer-Encoding: 8bit\n" 13 | "X-Generator: Poedit 2.2.1\n" 14 | "X-Poedit-Basepath: ..\n" 15 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" 16 | "X-Poedit-KeywordsList: __;_e\n" 17 | "X-Poedit-SearchPath-0: .\n" 18 | "X-Poedit-SearchPathExcluded-0: dist\n" 19 | "X-Poedit-SearchPathExcluded-1: src\n" 20 | "X-Poedit-SearchPathExcluded-2: webpack\n" 21 | 22 | #: admin/admin.php:112 admin/admin.php:130 instant-images.php:104 23 | msgid "Instant Images" 24 | msgstr "" 25 | 26 | #: admin/includes/cta/permissions.php:14 27 | msgid "Permissions Error" 28 | msgstr "" 29 | 30 | #: admin/includes/cta/permissions.php:15 31 | msgid "Instant Images is unable to download and process images on your server." 32 | msgstr "" 33 | 34 | #: admin/includes/cta/permissions.php:16 35 | msgid "Please enable read/write access to the following directory" 36 | msgstr "" 37 | 38 | #: admin/includes/settings.php:21 admin/includes/unsplash-settings.php:6 39 | msgid "Unsplash Settings" 40 | msgstr "" 41 | 42 | #: admin/includes/settings.php:29 43 | msgid "Upload Image Width" 44 | msgstr "" 45 | 46 | #: admin/includes/settings.php:38 47 | msgid "Upload Image Height" 48 | msgstr "" 49 | 50 | #: admin/includes/settings.php:47 51 | msgid "Button" 52 | msgstr "" 53 | 54 | #: admin/includes/settings.php:64 admin/includes/unsplash-settings.php:7 55 | msgid "Manage your media upload settings" 56 | msgstr "" 57 | 58 | #: admin/includes/settings.php:93 59 | msgid "Max Image Upload Width:" 60 | msgstr "" 61 | 62 | #: admin/includes/settings.php:112 63 | msgid "Max Image Upload Height:" 64 | msgstr "" 65 | 66 | #: admin/includes/settings.php:130 67 | msgid "Button:" 68 | msgstr "" 69 | 70 | #: admin/includes/settings.php:134 71 | msgid "Hide Instant Images button next to \"Add Media\" on post edit screens." 72 | msgstr "" 73 | 74 | #: admin/includes/unsplash-settings.php:16 75 | msgid "Save Settings" 76 | msgstr "" 77 | 78 | #: admin/includes/unsplash-settings.php:23 79 | msgid "What's New" 80 | msgstr "" 81 | 82 | #: admin/includes/unsplash-settings.php:24 83 | msgid "The latest Instant Images updates" 84 | msgstr "" 85 | 86 | #: admin/includes/unsplash-settings.php:60 87 | msgid "Our Plugins" 88 | msgstr "" 89 | 90 | #: admin/views/unsplash.php:11 91 | #, php-format 92 | msgid "One click photo uploads from %s" 93 | msgstr "" 94 | 95 | #: admin/views/unsplash.php:16 96 | msgid "Settings" 97 | msgstr "" 98 | 99 | #: api/resize.php:82 100 | msgid "" 101 | "Unable to copy image to the media library. Please check your server " 102 | "permissions." 103 | msgstr "" 104 | 105 | #: api/resize.php:111 106 | msgid "Image successfully uploaded to your media library!" 107 | msgstr "" 108 | 109 | #: api/resize.php:121 110 | msgid "" 111 | "There was an error sending the image to your media library. Please check " 112 | "your server permissions and confirm the upload_max_filesize setting (php." 113 | "ini) is large enough for the downloaded image (8mb minimum is recommended)." 114 | msgstr "" 115 | 116 | #: api/resize.php:139 117 | msgid "There was an error resizing the image, please try again." 118 | msgstr "" 119 | 120 | #: api/upload.php:49 121 | msgid "" 122 | "Unable to save image, check your server permissions of `uploads/instant-" 123 | "instants`" 124 | msgstr "" 125 | 126 | #: api/upload.php:71 127 | msgid "An issue occurred retrieving image info via the REST API." 128 | msgstr "" 129 | 130 | #: api/upload.php:97 131 | msgid "Image successfully uploaded to server." 132 | msgstr "" 133 | 134 | #: api/upload.php:107 135 | msgid "" 136 | "Uploaded image not found, please ensure you have proper permissions set on " 137 | "the uploads directory." 138 | msgstr "" 139 | 140 | #: api/upload.php:119 141 | msgid "" 142 | "Unable to download image to server, please check the server permissions of " 143 | "the instant-images folder in your WP uploads directory." 144 | msgstr "" 145 | 146 | #: api/upload.php:133 147 | msgid "" 148 | "The core PHP copy() function is not available on your server. Please contact " 149 | "your server administrator to upgrade your PHP version." 150 | msgstr "" 151 | 152 | #: instant-images.php:113 153 | msgid "Error accessing Unsplash API" 154 | msgstr "" 155 | 156 | #: instant-images.php:114 157 | msgid "Please check your Application ID." 158 | msgstr "" 159 | 160 | #: instant-images.php:115 161 | msgid "" 162 | "Unable to download image to server, please check your server permissions." 163 | msgstr "" 164 | 165 | #: instant-images.php:116 166 | msgid "" 167 | "There was an error sending the image to your media library. Please check " 168 | "your server permissions and confirm the upload_max_filesize setting (php." 169 | "ini) is large enough for the downloaded image." 170 | msgstr "" 171 | 172 | #: instant-images.php:117 173 | msgid "" 174 | "There was an error accessing the WP REST API - Instant Images requires " 175 | "access to the WP REST API to fetch and upload images to your media library." 176 | msgstr "" 177 | 178 | #: instant-images.php:118 179 | msgid "Photo by" 180 | msgstr "" 181 | 182 | #: instant-images.php:119 183 | msgid "View All Photos by" 184 | msgstr "" 185 | 186 | #: instant-images.php:120 187 | msgid "Click Image to Upload" 188 | msgstr "" 189 | 190 | #: instant-images.php:121 191 | msgid "Click to Upload" 192 | msgstr "" 193 | 194 | #: instant-images.php:122 195 | msgid "View Full Size" 196 | msgstr "" 197 | 198 | #: instant-images.php:123 199 | msgid "Like(s)" 200 | msgstr "" 201 | 202 | #: instant-images.php:124 203 | msgid "Downloading Image..." 204 | msgstr "" 205 | 206 | #: instant-images.php:125 207 | msgid "Resizing Image..." 208 | msgstr "" 209 | 210 | #: instant-images.php:126 211 | msgid "Sorry, nothing matched your query" 212 | msgstr "" 213 | 214 | #: instant-images.php:127 215 | msgid "Please try adjusting your search criteria" 216 | msgstr "" 217 | 218 | #: instant-images.php:128 219 | msgid "New" 220 | msgstr "" 221 | 222 | #: instant-images.php:129 223 | msgid "Oldest" 224 | msgstr "" 225 | 226 | #: instant-images.php:130 227 | msgid "Popular" 228 | msgstr "" 229 | 230 | #: instant-images.php:131 231 | msgid "Load More Images" 232 | msgstr "" 233 | 234 | #: instant-images.php:132 235 | msgid "Search for Toronto, Coffee + Breakfast etc..." 236 | msgstr "" 237 | 238 | #: instant-images.php:133 239 | msgid "images found for" 240 | msgstr "" 241 | 242 | #: instant-images.php:134 243 | msgid "Clear Search Results" 244 | msgstr "" 245 | 246 | #: instant-images.php:135 247 | msgid "View Photo on Unsplash" 248 | msgstr "" 249 | 250 | #: instant-images.php:136 251 | msgid "Set as Featured Image" 252 | msgstr "" 253 | 254 | #: instant-images.php:137 255 | msgid "Insert Into Post" 256 | msgstr "" 257 | 258 | #: instant-images.php:138 259 | msgid "Filename" 260 | msgstr "" 261 | 262 | #: instant-images.php:139 263 | msgid "Alt Text" 264 | msgstr "" 265 | 266 | #: instant-images.php:140 267 | msgid "Caption" 268 | msgstr "" 269 | 270 | #: instant-images.php:141 271 | msgid "Edit Image Details" 272 | msgstr "" 273 | 274 | #: instant-images.php:142 275 | msgid "Update and save image details prior to uploading" 276 | msgstr "" 277 | 278 | #: instant-images.php:143 279 | msgid "Cancel" 280 | msgstr "" 281 | 282 | #: instant-images.php:144 283 | msgid "Save" 284 | msgstr "" 285 | 286 | #: vendor/connekt-plugin-installer/class-connekt-plugin-installer.php:52 287 | #: vendor/connekt-plugin-installer/class-connekt-plugin-installer.php:365 288 | msgid "Install Now" 289 | msgstr "" 290 | 291 | #: vendor/connekt-plugin-installer/class-connekt-plugin-installer.php:87 292 | #: vendor/connekt-plugin-installer/class-connekt-plugin-installer.php:367 293 | msgid "Activated" 294 | msgstr "" 295 | 296 | #: vendor/connekt-plugin-installer/class-connekt-plugin-installer.php:91 297 | #: vendor/connekt-plugin-installer/class-connekt-plugin-installer.php:366 298 | msgid "Activate" 299 | msgstr "" 300 | 301 | #: vendor/connekt-plugin-installer/class-connekt-plugin-installer.php:129 302 | msgid "By" 303 | msgstr "" 304 | 305 | #: vendor/connekt-plugin-installer/class-connekt-plugin-installer.php:142 306 | msgid "More Details" 307 | msgstr "" 308 | 309 | #: vendor/connekt-plugin-installer/class-connekt-plugin-installer.php:164 310 | msgid "Sorry, you are not allowed to install plugins on this site." 311 | msgstr "" 312 | 313 | #: vendor/connekt-plugin-installer/class-connekt-plugin-installer.php:171 314 | #: vendor/connekt-plugin-installer/class-connekt-plugin-installer.php:242 315 | msgid "Error - unable to verify nonce, please try again." 316 | msgstr "" 317 | 318 | #: vendor/connekt-plugin-installer/class-connekt-plugin-installer.php:235 319 | msgid "Sorry, you are not allowed to activate plugins on this site." 320 | msgstr "" 321 | 322 | #: vendor/connekt-plugin-installer/class-connekt-plugin-installer.php:364 323 | msgid "Are you sure you want to install this plugin?" 324 | msgstr "" 325 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | === Instant Images - One Click Unsplash Uploads === 2 | Contributors: dcooney, connekthq 3 | Donate link: https://connekthq.com/donate/ 4 | Tags: stock photo, unsplash, prototyping, photos, upload, media library, image upload, free photos 5 | Requires at least: 4.0 6 | Tested up to: 5.1.1 7 | Stable tag: 4.0.1 8 | License: GPLv2 or later 9 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 10 | 11 | One click uploads of Unsplash photos directly to your WordPress media library. 12 | 13 | == Description == 14 | 15 | Instantly upload photos from Unsplash to your website without leaving WordPress! 16 | 17 | **Instant Images** is the fastest and easiest way to upload high quality FREE photos from [unsplash.com](http://unsplash.com) directly to your media library. 18 | 19 | [youtube https://www.youtube.com/watch?v=s6Q7Kfi2f1c] 20 | 21 | The perfect tool for users who want to save time and frustration by uploading images directly inside their WordPress installation and for developers who want to prototype and develop using real world imagery. 22 | 23 | **[Visit Plugin Website](https://connekthq.com/plugins/instant-images/)** 24 | 25 | = Features = 26 | 27 | * **Image Search** - The Instant Images search let’s you quickly find and upload images for any subject in a matter of seconds! 28 | * **Time Saver** - Quickly upload amazing stock photos without leaving the comfort of your WordPress admin. 29 | * **Theme/Plugin Developers** - A great tool for developers who want to prototype and develop using real world imagery. 30 | * **Gutenberg** - Instant Images directly integrates with Gutenberg as a plugin sidebar. 31 | * **Edit Image Metadata** - Easily edit image filename, alt text and caption prior to uploading to your media library. 32 | * **Easy to Use** - It couldn't get much more simple, just click an image and it's automatically uploaded to your media library for use on your site. 33 | 34 | 35 | 36 | *** 37 | 38 | = Tested Browsers = 39 | 40 | * Firefox (Mac + PC) 41 | * Chrome (Mac + PC) 42 | * Safari (Mac) 43 | * IE 11 > 44 | 45 | *** 46 | 47 | = How Can You Contribute? = 48 | Pull requests can be submitted via [GitHub](https://github.com/dcooney/instant-images). 49 | 50 | *** 51 | 52 | 53 | = Website = 54 | [https://connekthq.com/plugins/instant-images/](https://connekthq.com/plugins/instant-images/) 55 | 56 | *** 57 | 58 | 59 | == Frequently Asked Questions == 60 | 61 | = Can I legally use these photos on my website? = 62 | All photos published on Unsplash are licensed under Creative Commons Zero which means you can copy, modify, distribute and use the photos for free, including commercial purposes, without asking permission from or providing attribution to the photographer or Unsplash. 63 | [Learn More](http://creativecommons.org/publicdomain/zero/1.0/) 64 | 65 | 66 | = Can I search for individual photos by ID? = 67 | Yes! You can enter `id:{photo_id}` into the search box to return a single result. 68 | e.g. `id:YiUi00uqKk8` 69 | 70 | 71 | = I'm unable to download images, what is the cause of this? = 72 | Unfortunately, there are a number of reasons why Instant Images may not work in your current hosting/server environment. Please read through the [FAQ on our website](https://connekthq.com/plugins/instant-images/#faqs) to view some potential causes. 73 | 74 | 75 | = Are the images upload to the Media Library? = 76 | Yes, once clicked, the images are processed on the server then uploaded to the Media Library into the various sizes set in your theme. 77 | 78 | 79 | = Are raw uploads stored on the server? = 80 | No, once an image has be uploaded and resized the raw download will be removed from your server. 81 | 82 | 83 | = Are there server requirements? = 84 | Yes, this plugin is required to write temporary images into an `/instant-images` directory within your WordPress `uploads` directory for image processing prior to being uploaded to the media library. 85 | 86 | Some hosts lock down their servers and you may be required to update your php.ini or .htaccess in order to use this plugin. 87 | 88 | 89 | == Installation == 90 | 91 | How to install Instant Images. 92 | 93 | = Using The WordPress Dashboard = 94 | 95 | 1. Navigate to the 'Add New' in the plugins dashboard 96 | 2. Search for 'Instant Images' 97 | 3. Click 'Install Now' 98 | 4. Activate the plugin on the Plugin dashboard 99 | 100 | = Uploading in WordPress Dashboard = 101 | 102 | 1. Navigate to the 'Add New' in the plugins dashboard 103 | 2. Navigate to the 'Upload' area 104 | 3. Select `instant-images.zip` from your computer 105 | 4. Click 'Install Now' 106 | 5. Activate the plugin in the Plugin dashboard 107 | 108 | = Using FTP = 109 | 110 | 1. Download `instant-images.zip` 111 | 2. Extract the `instant-images` directory to your computer 112 | 3. Upload the `instant-images` directory to the `/wp-content/plugins/` directory 113 | 4. Activate the plugin in the Plugin dashboard 114 | 115 | 116 | == Screenshots == 117 | 118 | 1. Dashboard - Browse, search and upload images to your WordPress media library 119 | 2. Search - Find and upload images for any subject in a matter of seconds! 120 | 3. Image Metadata - Easily edit image filename, alt text and caption prior to uploading to your media library. 121 | 4. Post/Page Edit - Unsplash images in a lightbox on your post edit/new/post pages. 122 | 5. Gutenberg post edit screens. Add as featured image, insert into post or just upload photo. 123 | 124 | 125 | == Changelog == 126 | 127 | = 4.0.1 - April 18, 2019 = 128 | * FIX - Fixed issue where Instant Images sidebar plugin would not appear in Gutenberg if removed as a pinned item. 129 | 130 | 131 | = 4.0.0 - February 12, 2019 = 132 | 133 | * 4.0 adds Gutenberg support. You can now access instant images directly from inside the block editor. 134 | * NEW - Added Instant Images to Gutenberg as a Plugin Sidebar. 135 | * NEW - Added Gutenberg featured image support. 136 | * NEW - Added Gutenberg Create Image Block support. 137 | 138 | * UPDATE - Improved a11y (accessibility) of photo listing items. 139 | * UPDATE - Updated REST API methods to prefix function names. 140 | * UPDATE - Various other UI/UX enhancements. 141 | 142 | 143 | = 3.3.0 - January 10, 2019 = 144 | * UPDATE - Removed cURL usage for downloading images in place of core `copy()` PHP function. 145 | * NEW - Adding Axios for HTTP requests 146 | * NEW - Removing `/instant-images` folder in uploads directory on plugin de-activation. 147 | * FIX - Added fix for directory permission issue when creating `uploads/instant-images`. 148 | 149 | = 3.2.1 - September 25, 2018 = 150 | * NEW - Added Instant Images to media upload tabs. You can now upload a photo and insert it into a page or page immediately. Please note, this is currently not working with the Gutenberg editor. 151 | * UPDATE - Better cURL error handling (hopefully). 152 | 153 | 154 | = 3.2 - July 31, 2018 155 | ** NEW - Added functionality to edit image details (filename, alt text and caption) prior to uploading - edit image detail by clicking the options icon in the top right corner of each image 👍. 156 | ** UPDATE - Improved error handling and messaging for common REST API and cURL issues. 157 | 158 | 159 | = 3.1.1 - June 15, 2018 = 160 | ** NEW - More stable image uploading 🎉. 161 | ** NEW - Added `instant_images_user_role` filter to allow for control over user capability. 162 | ** FIX - Fixing permission issues with uploads when using basic HTTP authentication on domain. 163 | ** UPDATE - Better error handling 164 | ** UPDATE - Added permission 755 to the uploads/instant-images directory created on activation. 165 | 166 | 167 | = 3.1 - January 2, 2018 = 168 | ** NEW - Adding support for searching individual photos by ID. Prefix a search term with `id:` to search by Unsplash ID. e.g. `id:ixddk_CepZY`. 169 | ** UPDATED - Updated to meet revised Unsplash API guidelines. 170 | ** UPDATED - Better Error messaging for upload/resize errors. 171 | ** NEW - Added `clear search` button to remove search results. 172 | ** FIX - Fixed JS error that occured when `SCRIPT_DEBUG` was set to `true`. 173 | 174 | 175 | = 3.0 - September 21, 2017 = 176 | ** NEW - Instant Images has been completely re-built using React and the WordPress REST API. 177 | 178 | 179 | = 2.1.1 - June 6, 2017 = 180 | ** NEW - Added infinite scroll while viewing Instant Images on large screens. 181 | ** FIX - Fixed missing js file error in browser console. 182 | ** UPDATE - Updated Masonry/Imagesloaded image load functionality. 183 | 184 | 185 | = 2.1 - May 12, 2017 = 186 | ** UPDATE - Remove App ID setting - Unsplash API is now open for everyone without API limit restrictions. 187 | ** UPDATE - Updating default image upload from 'Full' to 'Raw'. Raw files are significantly smaller size and should make uploads quicker on slower connections and help to reduce upload errors. 188 | ** UPDATE - UI/UX tweaks and updates. 189 | ** FIX - Updating media_buttons hook. Was causing issues with other plugins. 190 | 191 | 192 | = 2.0.1 - January 12, 2017 = 193 | * FIX - Update to instant_img_resize_image function to remove unnecessary function arguments. These args were causing issues on some servers. 194 | * NEW - Refresh Media Library content when uploading images through the Instant Images uploader on edit screen for posts and pages. 195 | * UI Enhancements 196 | 197 | 198 | = 2.0 = 199 | * Initial Commit 200 | * Updating plugin from UnsplashWP to Instant Images 201 | 202 | 203 | == Upgrade Notice == 204 | 205 | * This is an upgrade from UnsplashWP 206 | 207 | 208 | -------------------------------------------------------------------------------- /src/scss/partials/_admin.scss: -------------------------------------------------------------------------------- 1 | $xlarge: 1570px; 2 | $large: 1270px; 3 | $medium: 1024px; 4 | $small: 800px; 5 | $wp_small: 640px; 6 | $xsmall: 600px; 7 | 8 | html { overflow-y: scroll; } 9 | 10 | body.media_page_instant-images{ 11 | background: #fff; 12 | #wpcontent{ 13 | padding-left: 0; 14 | padding-bottom: 40px; 15 | @media screen and (max-width: $small){ 16 | padding-bottom: 0; 17 | } 18 | } 19 | #wpbody-content{ 20 | padding-bottom: 0; 21 | } 22 | #wpfooter{ 23 | padding-top: 0; 24 | padding-bottom: 0; 25 | line-height: 40px; 26 | background: #f7f7f7; 27 | border-top: 1px solid #efefef; 28 | position: fixed; 29 | bottom: 0; 30 | z-index: 1100; 31 | p{ 32 | line-height: 40px; 33 | } 34 | @media screen and (max-width: $small){ 35 | display: none; 36 | } 37 | } 38 | } 39 | 40 | .instant-img-container{ 41 | font-size: 14px; 42 | color:#666; 43 | position: relative; 44 | 45 | .offscreen{ 46 | position: absolute; 47 | overflow: hidden; 48 | clip: rect(0 0 0 0); 49 | height: 1px; width: 1px; 50 | margin: -1px; padding: 0; border: 0; 51 | } 52 | *{ 53 | box-sizing: border-box; 54 | } 55 | a{ 56 | color: #5d72c3; 57 | transition: all 0.25s ease; 58 | &:hover{ 59 | color: #5568ae; 60 | text-decoration: none; 61 | } 62 | } 63 | img{ 64 | max-width: 100%; 65 | } 66 | p{ 67 | display: block; 68 | color:#666; 69 | width: 100%; 70 | display: block; 71 | clear: both; 72 | text-transform: none; 73 | padding: 0; 74 | margin: 0 0 15px; 75 | font-size: 14px; 76 | } 77 | &.loading{ 78 | .loading-block{ 79 | display: block; 80 | } 81 | } 82 | 83 | .error-messaging{ 84 | display: none; 85 | &.active{ 86 | padding: 17px 17px 17px 57px; 87 | border-radius: 3px; 88 | background: #df3333; 89 | color: #fff; 90 | font-size: 13px; 91 | margin-bottom: 25px; 92 | display: block; 93 | position: relative; 94 | &:before{ 95 | font-family: 'FontAwesome'; 96 | content: '\f06a'; 97 | display: block; 98 | left: 17px; 99 | top: 50%; 100 | transform: translateY(-50%); 101 | position: absolute; 102 | font-size: 30px; 103 | opacity: 0.75; 104 | } 105 | } 106 | } 107 | 108 | 109 | /* 110 | Admin Screens - Setting page 111 | wp-admin/upload.php?page=instant-images 112 | */ 113 | 114 | .header-wrap{ 115 | background: #f7f7f7 url('../img/logo-48x48.png') no-repeat 25px 20px; 116 | padding: 20px 25px 20px 83px; 117 | min-height: 88px; 118 | overflow: hidden; 119 | border-bottom: 1px solid #efefef; 120 | position: relative; 121 | @media screen and (max-width: $small){ 122 | background-position: center 20px; 123 | padding: 80px 25px 20px; 124 | text-align: center; 125 | } 126 | h1{ 127 | padding: 0; 128 | margin: 4px 0 0; 129 | font-weight: 700; 130 | font-size: 26px; 131 | max-width: 70%; 132 | @media screen and (max-width: $small){ 133 | max-width: 100%; 134 | width: 100%; 135 | text-align: center; 136 | } 137 | span{ 138 | display: block; 139 | padding: 8px 0 0; 140 | color: #888; 141 | font-size: 15px; 142 | font-weight: 400; 143 | } 144 | } 145 | button{ 146 | position: absolute; 147 | right: 25px; 148 | bottom: 26px; 149 | @media screen and (max-width: $small){ 150 | position: static; 151 | margin-top: 20px; 152 | display: inline-block; 153 | } 154 | i{ 155 | margin-right: 2px; 156 | } 157 | } 158 | } 159 | .instant-images-wrapper{ 160 | padding: 0 25px; 161 | display: block; 162 | overflow: hidden; 163 | min-height: 400px; 164 | background: url('../img/ajax-loader-lg.gif') no-repeat center center; 165 | &.loaded{ 166 | background: none; 167 | } 168 | } 169 | .permissions-warning{ 170 | padding: 0 25px; 171 | .inner{ 172 | border-bottom: 1px solid #efefef; 173 | padding: 32px 0; 174 | } 175 | input{ 176 | max-width: 500px; 177 | } 178 | h3{ 179 | font-size: 22px; 180 | margin: 0 0 15px; 181 | i{ 182 | margin: 0 2px 0 0; 183 | position: relative; 184 | 185 | } 186 | } 187 | p:first-of-type{ 188 | font-size: 18px; 189 | margin: 0 0 2px; 190 | } 191 | } 192 | .loading-block{ 193 | display: none; 194 | padding: 50px; 195 | background: url('../img/ajax-loader-lg.gif') no-repeat center center; 196 | } 197 | 198 | // Load More btn 199 | .load-more-wrap{ 200 | margin: 1% 0 0; 201 | padding: 25px 0; 202 | text-align: center; 203 | display: none; 204 | border-top: 1px solid #efefef; 205 | 206 | button{ 207 | display: inline-block; 208 | margin: 0; 209 | padding: 12px 15px; 210 | font-size: 15px; 211 | font-weight: 600; 212 | transition: all 0.1s ease; 213 | height: auto; 214 | line-height: 1; 215 | cursor: pointer; 216 | background-image: none; 217 | background-repeat: no-repeat !important; 218 | background-position: 15px center !important; 219 | &.disabled{ 220 | opacity: 0.3; 221 | cursor: default; 222 | } 223 | } 224 | } 225 | } 226 | 227 | 228 | 229 | /* 230 | Columns 231 | */ 232 | 233 | .instant-img-container .cnkt-main{ 234 | width: 100%; 235 | float: none; 236 | background: none !important; 237 | position: relative; 238 | } 239 | .instant-img-container h2, 240 | .instant-img-container h3, 241 | .instant-img-container h4{ 242 | margin-top: 0; 243 | } 244 | 245 | 246 | 247 | 248 | /* 249 | * Settings 250 | */ 251 | 252 | .instant-img-container .save-settings{ 253 | p.submit{ 254 | float: left; 255 | margin: 0 2px 0 0; 256 | width: auto; 257 | } 258 | .loading{ 259 | width: 46px; 260 | height: 28px; 261 | display: none; 262 | float: left; 263 | background: #fff url('../img/ajax-loader.gif') no-repeat center center; 264 | } 265 | } 266 | 267 | 268 | 269 | /* 270 | * Thickbox styles 271 | */ 272 | 273 | #TB_ajaxContent { 274 | clear: both; 275 | line-height: 1.4em; 276 | overflow: auto; 277 | text-align: left; 278 | width: 100% !important; 279 | box-sizing: border-box; 280 | padding: 15px !important; 281 | } 282 | 283 | 284 | /* Table */ 285 | .cnkt-sidebar .form-table { 286 | margin: 0; 287 | border: none; 288 | } 289 | .cnkt-sidebar .form-table td, 290 | .cnkt-sidebar .form-table label, 291 | .cnkt-sidebar .form-table p{ 292 | font-size: 13px; 293 | } 294 | .cnkt-sidebar .form-table label{ 295 | display: block; 296 | clear: both; 297 | float: none; 298 | } 299 | .cnkt-sidebar .form-table label span{ 300 | opacity: 0.8; 301 | font-size: 13px; 302 | font-style: italic; 303 | } 304 | .cnkt-sidebar .form-table th{ 305 | display: none; 306 | } 307 | .instant-img-container .form-table td{ 308 | border-top: 0; 309 | padding: 5px 0 20px; 310 | float: left; 311 | width: 100%; 312 | margin: 0; 313 | } 314 | .instant-img-container .form-table tr:first-of-type td{ 315 | padding: 10px 0; 316 | } 317 | .cnkt-sidebar .form-table .form-msg, 318 | .cnkt-main .form-msg{ 319 | display: block; 320 | line-height: 18px; 321 | padding: 12px 12px 12px 15px; 322 | margin: 15px 0 0; 323 | color: #666; 324 | background-color: #fff9ea; 325 | border-left: 5px solid #dfd8c2; 326 | border-radius: 2px; 327 | } 328 | .cnkt-sidebar .form-table .form-msg span, 329 | .cnkt-main .form-msg span{ 330 | display: block; 331 | padding: 6px 0 3px; 332 | } 333 | 334 | 335 | 336 | .instant-img-container h1, 337 | .instant-img-container h3, 338 | .instant-img-container h4{ 339 | color:#222; 340 | margin-top: 0; 341 | } 342 | .instant-img-container h4 + p{ 343 | margin-top: -6px; 344 | } 345 | .instant-img-container p.small{ 346 | font-size: 12px; 347 | margin-top: -10px; 348 | opacity: 0.7; 349 | } 350 | 351 | .instant-img-container ul{ 352 | padding: 0; 353 | margin: 0; 354 | list-style: none; 355 | } 356 | .instant-img-container label, 357 | .instant-img-container input, 358 | .instant-img-container select, 359 | .instant-img-container textarea{ 360 | box-shadow: none; 361 | } 362 | .instant-img-container label{ 363 | padding: 5px 0; 364 | } 365 | #unsplash-form-options h2, 366 | #unsplash-form-options p.desc{ 367 | display: none; 368 | } 369 | .instant-img-container input[type=text], 370 | .instant-img-container input[type=number], 371 | .instant-img-container textarea{ 372 | padding: 10px; 373 | line-height: 1; 374 | border: 1px solid #ccc; 375 | background: #f7f7f7; 376 | width: 100%; 377 | border-radius: 2px; 378 | height: auto; 379 | } 380 | .instant-img-container input[type=text]:focus, 381 | .instant-img-container textarea:focus{ 382 | border-color: #999; 383 | box-shadow: 0 0 3px #ccc; 384 | background: #efefef; 385 | } 386 | 387 | /* Helpers */ 388 | .spacer{ 389 | display: block; 390 | height: 40px; 391 | overflow: hidden; 392 | clear: both; 393 | width: 100%; 394 | &.sm{ 395 | height: 20px; 396 | } 397 | } 398 | 399 | input:-webkit-autofill { 400 | -webkit-box-shadow: 0 0 0px 1000px white inset; 401 | } 402 | 403 | -------------------------------------------------------------------------------- /vendor/connekt-plugin-installer/class-connekt-plugin-installer.php: -------------------------------------------------------------------------------- 1 | 44 | 45 |
46 | sanitize_file_name($plugin['slug']), 57 | 'fields' => array( 58 | 'short_description' => true, 59 | 'sections' => false, 60 | 'requires' => false, 61 | 'downloaded' => true, 62 | 'last_updated' => false, 63 | 'added' => false, 64 | 'tags' => false, 65 | 'compatibility' => false, 66 | 'homepage' => false, 67 | 'donate_link' => false, 68 | 'icons' => true, 69 | 'banners' => true, 70 | ), 71 | ) 72 | ); 73 | 74 | //echo '
';
 75 |                //print_r($api);
 76 |                //echo '
'; 77 | 78 | 79 | if ( !is_wp_error( $api ) ) { // confirm error free 80 | 81 | $main_plugin_file = Connekt_Plugin_Installer::get_plugin_file($plugin['slug']); // Get main plugin file 82 | //echo $main_plugin_file; 83 | if(self::check_file_extension($main_plugin_file)){ // check file extension 84 | if(is_plugin_active($main_plugin_file)){ 85 | // plugin activation, confirmed! 86 | $button_classes = 'button disabled'; 87 | $button_text = __('Activated', 'framework'); 88 | } else { 89 | // It's installed, let's activate it 90 | $button_classes = 'activate button button-primary'; 91 | $button_text = __('Activate', 'framework'); 92 | } 93 | } 94 | 95 | // Send plugin data to template 96 | self::render_template($plugin, $api, $button_text, $button_classes); 97 | 98 | } 99 | 100 | endforeach; 101 | ?> 102 |
103 | 123 |
124 |
125 | 126 |

name; ?>

127 |

short_description; ?>

128 | 129 |

author; ?>

130 |
131 | 146 |
147 | $plugin, 184 | 'fields' => array( 185 | 'short_description' => false, 186 | 'sections' => false, 187 | 'requires' => false, 188 | 'rating' => false, 189 | 'ratings' => false, 190 | 'downloaded' => false, 191 | 'last_updated' => false, 192 | 'added' => false, 193 | 'tags' => false, 194 | 'compatibility' => false, 195 | 'homepage' => false, 196 | 'donate_link' => false, 197 | ), 198 | ) 199 | ); 200 | 201 | $skin = new WP_Ajax_Upgrader_Skin(); 202 | $upgrader = new Plugin_Upgrader( $skin ); 203 | $upgrader->install($api->download_link); 204 | 205 | if($api->name){ 206 | $status = 'success'; 207 | $msg = $api->name .' successfully installed.'; 208 | } else { 209 | $status = 'failed'; 210 | $msg = 'There was an error installing '. $api->name .'.'; 211 | } 212 | 213 | $json = array( 214 | 'status' => $status, 215 | 'msg' => $msg, 216 | ); 217 | 218 | wp_send_json($json); 219 | 220 | } 221 | 222 | 223 | 224 | 225 | /* 226 | * cnkt_plugin_activation 227 | * Activate plugin via Ajax. 228 | * 229 | * @return $json 230 | * 231 | * @since 1.0 232 | */ 233 | public function cnkt_plugin_activation(){ 234 | if ( ! current_user_can('install_plugins') ) 235 | wp_die( __( 'Sorry, you are not allowed to activate plugins on this site.', 'framework' ) ); 236 | 237 | $nonce = $_POST["nonce"]; 238 | $plugin = $_POST["plugin"]; 239 | 240 | // Check our nonce, if they don't match then bounce! 241 | if (! wp_verify_nonce( $nonce, 'cnkt_installer_nonce' )) 242 | die( __( 'Error - unable to verify nonce, please try again.', 'framework' ) ); 243 | 244 | 245 | // Include required libs for activation 246 | require_once( ABSPATH . 'wp-admin/includes/plugin-install.php' ); 247 | require_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' ); 248 | require_once( ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php' ); 249 | 250 | 251 | // Get Plugin Info 252 | $api = plugins_api( 'plugin_information', 253 | array( 254 | 'slug' => $plugin, 255 | 'fields' => array( 256 | 'short_description' => false, 257 | 'sections' => false, 258 | 'requires' => false, 259 | 'rating' => false, 260 | 'ratings' => false, 261 | 'downloaded' => false, 262 | 'last_updated' => false, 263 | 'added' => false, 264 | 'tags' => false, 265 | 'compatibility' => false, 266 | 'homepage' => false, 267 | 'donate_link' => false, 268 | ), 269 | ) 270 | ); 271 | 272 | 273 | if($api->name){ 274 | $main_plugin_file = Connekt_Plugin_Installer::get_plugin_file($plugin); 275 | $status = 'success'; 276 | if($main_plugin_file){ 277 | activate_plugin($main_plugin_file); 278 | $msg = $api->name .' successfully activated.'; 279 | } 280 | } else { 281 | $status = 'failed'; 282 | $msg = 'There was an error activating '. $api->name .'.'; 283 | } 284 | 285 | $json = array( 286 | 'status' => $status, 287 | 'msg' => $msg, 288 | ); 289 | 290 | wp_send_json($json); 291 | 292 | } 293 | 294 | 295 | 296 | 297 | /* 298 | * get_plugin_file 299 | * A method to get the main plugin file. 300 | * 301 | * 302 | * @param $plugin_slug String - The slug of the plugin 303 | * @return $plugin_file 304 | * 305 | * @since 1.0 306 | */ 307 | 308 | public static function get_plugin_file( $plugin_slug ) { 309 | require_once( ABSPATH . '/wp-admin/includes/plugin.php' ); // Load plugin lib 310 | $plugins = get_plugins(); 311 | 312 | foreach( $plugins as $plugin_file => $plugin_info ) { 313 | 314 | // Get the basename of the plugin e.g. [askismet]/askismet.php 315 | $slug = dirname( plugin_basename( $plugin_file ) ); 316 | 317 | if($slug){ 318 | if ( $slug == $plugin_slug ) { 319 | return $plugin_file; // If $slug = $plugin_name 320 | } 321 | } 322 | } 323 | return null; 324 | } 325 | 326 | 327 | 328 | 329 | /* 330 | * check_file_extension 331 | * A helper to check file extension 332 | * 333 | * 334 | * @param $filename String - The filename of the plugin 335 | * @return boolean 336 | * 337 | * @since 1.0 338 | */ 339 | public static function check_file_extension( $filename ) { 340 | if( substr( strrchr($filename, '.' ), 1 ) === 'php' ){ 341 | // has .php exension 342 | return true; 343 | } else { 344 | // ./wp-content/plugins 345 | return false; 346 | } 347 | } 348 | 349 | 350 | 351 | 352 | /* 353 | * enqueue_scripts 354 | * Enqueue admin scripts and scripts localization 355 | * 356 | * 357 | * @since 1.0 358 | */ 359 | public function enqueue_scripts(){ 360 | wp_enqueue_script( 'plugin-installer', CNKT_INSTALLER_PATH. 'assets/installer.js', array( 'jquery' )); 361 | wp_localize_script( 'plugin-installer', 'cnkt_installer_localize', array( 362 | 'ajax_url' => admin_url('admin-ajax.php'), 363 | 'admin_nonce' => wp_create_nonce('cnkt_installer_nonce'), 364 | 'install_now' => __('Are you sure you want to install this plugin?', 'framework'), 365 | 'install_btn' => __('Install Now', 'framework'), 366 | 'activate_btn' => __('Activate', 'framework'), 367 | 'installed_btn' => __('Activated', 'framework') 368 | )); 369 | 370 | wp_enqueue_style( 'plugin-installer', CNKT_INSTALLER_PATH. 'assets/installer.css'); 371 | } 372 | 373 | } 374 | 375 | 376 | // initialize 377 | $connekt_plugin_installer = new Connekt_Plugin_Installer(); 378 | $connekt_plugin_installer->start(); 379 | } 380 | -------------------------------------------------------------------------------- /src/scss/partials/_photos.scss: -------------------------------------------------------------------------------- 1 | #photos{ 2 | width: 100%; 3 | width: calc(100% + 10px); 4 | margin: 0 0 0 -5px; 5 | padding: 0; 6 | position: relative; 7 | .photo{ 8 | width: 20%; 9 | margin: 0; 10 | padding: 0 5px 10px; 11 | opacity: 0; 12 | transition: opacity 0.3s ease; 13 | &--wrap{ 14 | position: relative; 15 | } 16 | &.in-view{ 17 | opacity: 1; 18 | } 19 | &.in-progress{ 20 | .fade{ 21 | opacity: 0 !important; 22 | visibility: hidden !important; 23 | } 24 | } 25 | .img-wrap{ 26 | display: block; 27 | overflow: hidden; 28 | position: relative; 29 | } 30 | @media screen and (min-width: 2000px){ 31 | width: 20%; 32 | } 33 | @media screen and (max-width: $xlarge){ 34 | width: 25%; 35 | } 36 | @media screen and (max-width: $large){ 37 | width: 33.333333%; 38 | } 39 | @media screen and (max-width: $small){ 40 | width: 50%; 41 | } 42 | @media screen and (max-width: $xsmall){ 43 | width: 100%; 44 | margin: 0 0 2%; 45 | } 46 | &:focus{ 47 | a.upload img{ 48 | opacity: 0.6; 49 | } 50 | .fade{ 51 | opacity: 1; 52 | visibility: visible; 53 | &.user{ 54 | opacity: 0.7; 55 | } 56 | } 57 | } 58 | 59 | &:focus-within{ 60 | .user-controls{ 61 | opacity: 1; 62 | } 63 | } 64 | 65 | a.upload{ 66 | display: block; 67 | position: relative; 68 | background-color: #222; 69 | background-position: center center; 70 | background-repeat: no-repeat; 71 | background-image: url('../img/ajax-loader.gif'); 72 | overflow: hidden; 73 | &.loaded{ 74 | background-image: none; 75 | } 76 | &:focus, 77 | &:active{ 78 | outline: none; 79 | border: none; 80 | } 81 | img{ 82 | transition: all 0.5s ease; 83 | width: 100%; 84 | height: auto !important; 85 | padding: 0; 86 | vertical-align: top; 87 | } 88 | 89 | // Status 90 | .status{ 91 | visibility: hidden; 92 | opacity: 0; 93 | transition: all 0.2575s ease-in-out; 94 | width: 60px; 95 | height: 60px; 96 | line-height: 60px; 97 | border-radius: 100%; 98 | position: absolute; 99 | left: 50%; 100 | top: 50%; 101 | z-index: 5; 102 | transform: translate(-50%, -50%) scale(1.5); 103 | box-shadow: 0 0 4px rgba(0, 0, 0, 0.15); 104 | &:before{ 105 | font-family: 'FontAwesome'; 106 | display: block; 107 | color: #fff; 108 | font-size: 22px; 109 | opacity: 0.8; 110 | } 111 | } 112 | 113 | &.uploading .status, 114 | &.resizing .status, 115 | &.success .status, 116 | &.errors .status{ 117 | text-align: center; 118 | left: 50%; 119 | top: 50%; 120 | transform: translate(-50%, -50%) scale(1); 121 | } 122 | 123 | 124 | // Uploading 125 | &.uploading{ 126 | cursor: default !important; 127 | .status{ 128 | visibility: visible; 129 | opacity: 1; 130 | background-color: #5d72c3; 131 | &:before{ 132 | content: '\f019'; 133 | } 134 | } 135 | } 136 | 137 | // Resizing 138 | &.resizing{ 139 | cursor: default !important; 140 | .status{ 141 | visibility: visible; 142 | opacity: 1; 143 | background-color: #e4c452; 144 | &:before{ 145 | color: #fff; 146 | content: '\f065'; 147 | } 148 | } 149 | } 150 | 151 | // Success 152 | &.success{ 153 | cursor: default !important; 154 | .status{ 155 | visibility: visible; 156 | opacity: 1; 157 | width: 70px; 158 | height: 70px; 159 | line-height: 70px; 160 | background-color: #63d875; 161 | border-radius: 100%; 162 | &:before{ 163 | content: '\f00c'; 164 | color: #fff; 165 | } 166 | } 167 | } 168 | 169 | // Error 170 | &.errors{ 171 | cursor: help !important; 172 | .status{ 173 | visibility: visible; 174 | opacity: 1; 175 | width: 60px; 176 | height: 60px; 177 | line-height: 60px; 178 | background-color: #df3333; 179 | border-radius: 100%; 180 | &:before{ 181 | content: '\f12a'; 182 | color: #fff; 183 | opacity: 0.8; 184 | } 185 | } 186 | } 187 | } 188 | 189 | // Upload Complete 190 | &.uploaded{ 191 | a.upload { 192 | img{ 193 | opacity: 0.25 !important; 194 | } 195 | } 196 | } 197 | 198 | // Hover and Progress 199 | &:hover, 200 | &.in-progress{ 201 | a.upload img{ 202 | opacity: 0.6; 203 | transform: scale(1.075); 204 | } 205 | .options{ 206 | opacity: 1; 207 | visibility: visible; 208 | i.heart-like{ 209 | transform: scale(1); 210 | } 211 | } 212 | .user-controls{ 213 | opacity: 1; 214 | } 215 | } 216 | 217 | &.in-progress{ 218 | .notice-msg{ 219 | top: 0; 220 | opacity: 1; 221 | } 222 | .user-controls{ 223 | opacity: 0; 224 | } 225 | } 226 | 227 | .options{ 228 | position: absolute; 229 | top: 5px; 230 | right: 5px; 231 | z-index: 6; 232 | width: auto; 233 | display: inline-block; 234 | width: auto; 235 | cursor: default !important; 236 | transition: all 0.3s ease; 237 | opacity: 0; 238 | visibility: hidden; 239 | font-size: 13px; 240 | i{ 241 | font-size: 14px; 242 | } 243 | i.heart-like{ 244 | color: #d13714; 245 | margin-right: 1px; 246 | transition: all 0.25s ease 0.05s; 247 | transform: scale(0.55); 248 | margin-right: 1px; 249 | position: relative; 250 | left: -1px; 251 | top: 0; 252 | font-size: 14px; 253 | opacity: 0.9; 254 | } 255 | a, span{ 256 | display: inline-block; 257 | vertical-align: top; 258 | line-height: 30px; 259 | padding: 0 10px; 260 | padding-top: 1px; 261 | background: rgba(255, 255, 255, 0.5); 262 | margin: 0; 263 | border-radius: 2px 0 0 2px; 264 | color: #23282d; 265 | transition: all 0.3s ease; 266 | } 267 | span{ 268 | cursor: default; 269 | &:hover, 270 | &:focus{ 271 | background-color: #fff; 272 | } 273 | } 274 | a{ 275 | margin-left: 1px; 276 | border-radius: 0 2px 2px 0; 277 | &:hover, 278 | &:focus{ 279 | background-color: #fff; 280 | } 281 | i{ 282 | position: relative; 283 | top: 1px; 284 | left: 1px; 285 | } 286 | } 287 | } 288 | 289 | .user-controls{ 290 | position: absolute; 291 | z-index: 6; 292 | width: 100%; 293 | bottom: 0; 294 | left: 0; 295 | width: 100%; 296 | background: rgba(0, 0, 0, 0.45); 297 | padding: 0; 298 | opacity: 0.4; 299 | transition: all 0.3s ease; 300 | } 301 | 302 | .photo-options{ 303 | float: right; 304 | text-align: right; 305 | max-width: 50%; 306 | } 307 | 308 | .fade{ 309 | transition: all 0.35s ease; 310 | color: #fff; 311 | background: rgba(255, 255, 255, 0.75); 312 | background: transparent; 313 | border-radius: 1px; 314 | height: 34px; 315 | line-height: 34px; 316 | font-size: 17px; 317 | z-index: 6; 318 | float: left; 319 | margin: 1px 1px 1px 0; 320 | padding: 0; 321 | color: rgba(255, 255, 255, 0.75); 322 | &.edit-photo, 323 | &.set-featured, 324 | &.insert{ 325 | width: auto; 326 | display: inline-block; 327 | width: 34px; 328 | text-align: center; 329 | i{ 330 | line-height: 27px; 331 | position: relative; 332 | left: 1px; 333 | top: 1px; 334 | } 335 | &:hover, 336 | &:focus{ 337 | color: #222; 338 | background: rgba(255, 255, 255, 0.95); 339 | } 340 | } 341 | &.set-featured{ 342 | i{ 343 | top: 0; 344 | left: 0; 345 | } 346 | } 347 | &.edit-photo{ 348 | i{ 349 | left: 0; 350 | top: 0; 351 | } 352 | } 353 | &.user{ 354 | background: none; 355 | text-decoration: none; 356 | font-size: 13px; 357 | max-width: 48%; 358 | cursor: pointer; 359 | text-decoration: none; 360 | border: none; 361 | line-height: 35px; 362 | height: 36px; 363 | margin: 0; 364 | &:hover, 365 | &:focus{ 366 | text-decoration: underline; 367 | } 368 | .user-wrap{ 369 | position: relative; 370 | padding-left: 35px; 371 | display: block; 372 | width: 100%; 373 | overflow: hidden; 374 | text-overflow: ellipsis; 375 | white-space: nowrap; 376 | } 377 | img{ 378 | width: 20px; 379 | max-width: 20px; 380 | border-radius: 100%; 381 | position: absolute; 382 | left: 8px; 383 | top: 8px; 384 | } 385 | } 386 | } 387 | 388 | // Notice Msg 389 | .notice-msg{ 390 | position: absolute; 391 | z-index: 999; 392 | top: -40px; 393 | left: 0; 394 | height: 40px; 395 | line-height: 40px; 396 | width: 100%; 397 | background: rgba(0, 0, 0, 0.6); 398 | text-align: center; 399 | color: #e0e4f5; 400 | font-size: 13px; 401 | margin: 0; 402 | padding: 0; 403 | transition: all 0.25s ease-in-out; 404 | opacity: 0; 405 | z-index: 9999; 406 | } 407 | } 408 | 409 | .edit-screen{ 410 | position: absolute; 411 | left: 0; 412 | top: 0; 413 | width: 100%; 414 | height: 100%; 415 | z-index: 999; 416 | background: rgba(255, 255, 255, 0.9); 417 | opacity: 0; 418 | visibility: hidden; 419 | transition: all 0.25s ease; 420 | padding: 8px; 421 | overflow-y: auto; 422 | -webkit-overflow-scrolling: touch; 423 | border: 1px solid #e1e1e1; 424 | transform: scale(1.025); 425 | &.editing{ 426 | visibility: visible; 427 | opacity: 1; 428 | transform: scale(1); 429 | } 430 | &--controls, 431 | &--title{ 432 | display: block; 433 | background: #f7f7f7; 434 | border: 1px solid #e1e1e1; 435 | padding: 15px; 436 | border-radius: 2px 2px 0 0; 437 | .button-primary{ 438 | float: right; 439 | } 440 | } 441 | &--controls{ 442 | border-radius: 0 0 2px 2px; 443 | } 444 | &--title{ 445 | border-bottom: none; 446 | p{ 447 | font-size: 12px; 448 | line-height: 1.25; 449 | margin: 0; 450 | color: #999; 451 | &.heading{ 452 | color: #222; 453 | margin: 0 0 5px; 454 | font-weight: 600; 455 | text-transform: uppercase; 456 | } 457 | } 458 | } 459 | label{ 460 | margin: 0; 461 | padding: 15px; 462 | display: block; 463 | background: #fff; 464 | border: 1px solid #e1e1e1; 465 | border-bottom: none; 466 | border-radius: 2px; 467 | position: relative; 468 | } 469 | span{ 470 | display: block; 471 | font-size: 11px; 472 | text-transform: uppercase; 473 | font-weight: 600; 474 | margin: 0 0 5px; 475 | color: #222; 476 | line-height: 1; 477 | } 478 | textarea{ 479 | resize: none; 480 | } 481 | input{ 482 | font-size: 12px; 483 | padding: 0 5px; 484 | margin: 0; 485 | height: 30px; 486 | line-height: 30px; 487 | } 488 | em{ 489 | position: absolute; 490 | bottom: 15px; 491 | right: 15px; 492 | height: 30px; 493 | line-height: 30px; 494 | background: #777; 495 | border-radius: 0 2px 2px 0; 496 | color: #e1e1e1; 497 | font-style: normal; 498 | font-size: 11px; 499 | padding: 0 10px; 500 | } 501 | } 502 | } -------------------------------------------------------------------------------- /src/js/components/PhotoList.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import ReactDOMServer from 'react-dom/server'; 4 | import imagesloaded from 'imagesloaded'; 5 | import Photo from './Photo'; 6 | import ResultsToolTip from './ResultsToolTip'; 7 | import API from './API'; 8 | 9 | class PhotoList extends React.Component { 10 | 11 | constructor(props) { 12 | 13 | super(props); 14 | 15 | this.results = (this.props.results) ? this.props.results : []; 16 | this.state = { results: this.results }; 17 | 18 | this.service = this.props.service; // Unsplash, Pixabay, etc. 19 | this.orderby = this.props.orderby; // Orderby 20 | this.page = this.props.page; // Page 21 | 22 | this.is_search = false; 23 | this.search_term = ''; 24 | this.total_results = 0; 25 | 26 | this.isLoading = false; // loading flag 27 | this.isDone = false; // Done flag - no photos remain 28 | 29 | this.errorMsg = ''; 30 | this.msnry = ''; 31 | 32 | this.editor = (this.props.editor) ? this.props.editor : 'classic'; 33 | this.is_block_editor = (this.props.editor === 'gutenberg') ? true : false; 34 | this.SetFeaturedImage = (this.props.SetFeaturedImage) ? this.props.SetFeaturedImage.bind(this) : ''; 35 | this.InsertImage = (this.props.InsertImage) ? this.props.InsertImage.bind(this) : ''; 36 | 37 | if(this.is_block_editor){ // Gutenberg 38 | this.container = document.querySelector('body'); 39 | this.container.classList.add('loading'); 40 | this.wrapper = document.querySelector('body'); 41 | 42 | } else { // Classic editor 43 | this.container = document.querySelector('.instant-img-container'); 44 | this.container.classList.add('loading'); 45 | this.wrapper = document.querySelector('.instant-images-wrapper'); 46 | 47 | } 48 | 49 | } 50 | 51 | 52 | 53 | /** 54 | * test() 55 | * Test access to the REST API 56 | * 57 | * @since 3.2 58 | */ 59 | test(){ 60 | 61 | let self = this; 62 | 63 | let target = document.querySelector('.error-messaging'); // Target element 64 | 65 | let testURL = instant_img_localize.root + 'instant-images/test/'; // REST Route 66 | var restAPITest = new XMLHttpRequest(); 67 | restAPITest.open('GET', testURL, true); 68 | restAPITest.setRequestHeader('X-WP-Nonce', instant_img_localize.nonce); 69 | restAPITest.setRequestHeader('Content-Type', 'application/json'); 70 | restAPITest.send(); 71 | 72 | restAPITest.onload = function() { 73 | if (restAPITest.status >= 200 && restAPITest.status < 400) { // Success 74 | 75 | let response = JSON.parse(restAPITest.response); 76 | let success = response.success; 77 | 78 | if(!success){ 79 | self.renderTestError(target); 80 | } 81 | 82 | } else { 83 | // Error 84 | self.renderTestError(target); 85 | } 86 | } 87 | 88 | restAPITest.onerror = function(errorMsg) { 89 | console.log(errorMsg); 90 | self.renderTestError(errorTarget); 91 | }; 92 | 93 | } 94 | 95 | renderTestError(target){ 96 | target.classList.add('active'); 97 | target.innerHTML = instant_img_localize.error_restapi; 98 | } 99 | 100 | 101 | 102 | /** 103 | * search() 104 | * Trigger Unsplash Search 105 | * 106 | * @param e element the search form 107 | * @since 3.0 108 | */ 109 | search(e){ 110 | 111 | e.preventDefault(); 112 | let input = document.querySelector('#photo-search'); 113 | let term = input.value; 114 | 115 | if(term.length > 2){ 116 | input.classList.add('searching'); 117 | this.container.classList.add('loading'); 118 | this.search_term = term; 119 | this.is_search = true; 120 | this.doSearch(this.search_term); 121 | } else { 122 | input.focus(); 123 | } 124 | 125 | } 126 | 127 | 128 | 129 | /** 130 | * doSearch 131 | * Run the search 132 | * 133 | * @param term string the search term 134 | * @param type string the type of search, standard or by ID 135 | * @since 3.0 136 | * @updated 3.1 137 | */ 138 | doSearch(term){ 139 | 140 | let self = this; 141 | let type = 'term'; 142 | this.page = 1; // reset page num 143 | 144 | let url = `${API.search_api}${API.app_id}${API.posts_per_page}&page=${this.page}&query=${this.search_term}`; 145 | 146 | // Search by ID 147 | // allow users to search by photo by prepending id:{photo_id} to search terms 148 | let search_type = term.substring(0, 3); 149 | if(search_type === 'id:'){ 150 | type = 'id'; 151 | term = term.replace('id:', ''); 152 | url = `${API.photo_api}/${term}${API.app_id}`; 153 | } 154 | 155 | let input = document.querySelector('#photo-search'); 156 | 157 | fetch(url) 158 | .then((data) => data.json()) 159 | .then(function(data) { 160 | 161 | // Term Search 162 | if(type === 'term'){ 163 | 164 | self.total_results = data.total; 165 | 166 | // Check for returned data 167 | self.checkTotalResults(data.results.length); 168 | 169 | // Update Props 170 | self.results = data.results; 171 | self.setState({ results: self.results }); 172 | 173 | } 174 | 175 | // Search by photo ID 176 | if(type === 'id' && data){ 177 | 178 | // Convert return data to array 179 | let photoArray = []; 180 | 181 | if(data.errors){ // If error was returned 182 | 183 | self.total_results = 0; 184 | self.checkTotalResults('0'); 185 | 186 | } else { // No errors, display results 187 | 188 | photoArray.push(data); 189 | 190 | self.total_results = 1; 191 | self.checkTotalResults('1'); 192 | 193 | } 194 | 195 | self.results = photoArray; 196 | self.setState({ results: self.results }); 197 | } 198 | 199 | input.classList.remove('searching'); 200 | 201 | }) 202 | .catch(function(error) { 203 | console.log(error); 204 | self.isLoading = false; 205 | }); 206 | 207 | } 208 | 209 | 210 | 211 | /** 212 | * clearSearch 213 | * Reset search results and results view 214 | * 215 | * @since 3.0 216 | */ 217 | clearSearch(){ 218 | let input = document.querySelector('#photo-search'); 219 | input.value = ''; 220 | this.total_results = 0; 221 | this.is_search = false; 222 | this.search_term = ''; 223 | } 224 | 225 | 226 | 227 | /** 228 | * getPhotos 229 | * Load next set of photos, infinite scroll style 230 | * 231 | * @since 3.0 232 | */ 233 | getPhotos(){ 234 | 235 | let self = this; 236 | this.page = parseInt(this.page) + 1; 237 | this.container.classList.add('loading'); 238 | this.isLoading = true; 239 | 240 | let url = `${API.photo_api}${API.app_id}${API.posts_per_page}&page=${this.page}&order_by=${this.orderby}`; 241 | if(this.is_search){ 242 | url = `${API.search_api}${API.app_id}${API.posts_per_page}&page=${this.page}&query=${this.search_term}`; 243 | } 244 | 245 | fetch(url) 246 | .then((data) => data.json()) 247 | .then(function(data) { 248 | 249 | if(self.is_search){ 250 | data = data.results; // Search results are recieved in different JSON format 251 | } 252 | 253 | // Loop results, push items into array 254 | data.map( data => { 255 | self.results.push(data); 256 | }); 257 | 258 | // Check for returned data 259 | self.checkTotalResults(data.length); 260 | 261 | // Update Props 262 | self.setState({ results: self.results }); 263 | 264 | }) 265 | .catch(function(error) { 266 | console.log(error); 267 | self.isLoading = false; 268 | }); 269 | 270 | } 271 | 272 | 273 | 274 | /** 275 | * togglePhotoList 276 | * Toogles the photo view (New/Popular/Old) 277 | * 278 | * @param view string Current view 279 | * @param e element Clicked element 280 | * @since 3.0 281 | */ 282 | togglePhotoList(view, e){ 283 | 284 | let el = e.target; 285 | if(el.classList.contains('active')) return false; // exit if active 286 | 287 | el.classList.add('loading'); // Add class to nav btn 288 | this.isLoading = true; 289 | let self = this; 290 | this.page = 1; 291 | this.orderby = view; 292 | this.results = []; 293 | this.clearSearch(); 294 | 295 | let url = `${API.photo_api}${API.app_id}${API.posts_per_page}&page=${this.page}&order_by=${this.orderby}`; 296 | fetch(url) 297 | .then((data) => data.json()) 298 | .then(function(data) { 299 | 300 | // Check for returned data 301 | self.checkTotalResults(data.length); 302 | 303 | // Update Props 304 | self.results = data; 305 | self.setState({ results: data }); 306 | 307 | el.classList.remove('loading'); // Remove class from nav btn 308 | }) 309 | .catch(function(error) { 310 | console.log(error); 311 | self.isLoading = false; 312 | }); 313 | } 314 | 315 | 316 | 317 | /** 318 | * renderLayout 319 | * Renders the Masonry layout 320 | * 321 | * @since 3.0 322 | */ 323 | renderLayout() { 324 | if(this.is_block_editor){ 325 | return false; 326 | } 327 | let self = this; 328 | let photoListWrapper = document.querySelector('#photos'); 329 | imagesLoaded(photoListWrapper, function() { 330 | self.msnry = new Masonry( photoListWrapper, { 331 | itemSelector: '.photo' 332 | }); 333 | [...document.querySelectorAll('#photos .photo')].forEach(el => el.classList.add('in-view')); 334 | }); 335 | } 336 | 337 | 338 | 339 | /** 340 | * onScroll 341 | * Scrolling function 342 | * 343 | * @since 3.0 344 | */ 345 | onScroll(){ 346 | let wHeight = window.innerHeight; 347 | let scrollTop = window.pageYOffset; 348 | let scrollH = document.body.scrollHeight - 200; 349 | if ((wHeight + scrollTop) >= scrollH && !this.isLoading && !this.isDone) { 350 | this.getPhotos(); 351 | } 352 | 353 | } 354 | 355 | 356 | 357 | /** 358 | * checkTotalResults 359 | * A checker to determine is there are remaining search results. 360 | * 361 | * @param num int Total search results 362 | * @since 3.0 363 | */ 364 | checkTotalResults(num){ 365 | this.isDone = (num == 0) ? true : false; 366 | } 367 | 368 | 369 | 370 | /** 371 | * setActiveState 372 | * Sets the main navigation active state 373 | * 374 | * @since 3.0 375 | */ 376 | setActiveState(){ 377 | let self = this; 378 | // Remove .active class 379 | [...document.querySelectorAll('.control-nav a')].forEach(el => el.classList.remove('active')); 380 | 381 | // Set active item, if not search 382 | if(!this.is_search){ 383 | let active = document.querySelector(`.control-nav li a.${this.orderby}`); 384 | active.classList.add('active'); 385 | } 386 | setTimeout(function(){ 387 | self.isLoading = false; 388 | self.container.classList.remove('loading'); 389 | }, 1000); 390 | } 391 | 392 | 393 | 394 | // Component Updated 395 | componentDidUpdate() { 396 | this.renderLayout(); 397 | this.setActiveState(); 398 | } 399 | 400 | 401 | 402 | // Component Init 403 | componentDidMount() { 404 | this.renderLayout(); 405 | this.setActiveState(); 406 | this.test(); 407 | this.container.classList.remove('loading'); 408 | this.wrapper.classList.add('loaded'); 409 | 410 | if(this.is_block_editor){ // Gutenberg 411 | this.page = 0; 412 | this.getPhotos(); 413 | 414 | } else { 415 | // Add scroll event 416 | window.addEventListener('scroll', () => this.onScroll()); 417 | 418 | } 419 | 420 | } 421 | 422 | 423 | 424 | render(){ 425 | 426 | return ( 427 |
428 | 429 | 441 | 442 |
443 | 444 |
445 | {this.state.results.map((result, iterator) => 446 | 447 | )} 448 |
449 | 450 |
451 |

{ instant_img_localize.no_results }

452 |

{ instant_img_localize.no_results_desc }

453 |
454 | 455 |
456 | 457 |
458 | 459 |
460 | 461 |
462 | ) 463 | } 464 | } 465 | 466 | export default PhotoList; -------------------------------------------------------------------------------- /vendor/connekt-plugin-installer/LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. -------------------------------------------------------------------------------- /src/js/components/Photo.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import API from './API'; 3 | import axios from 'axios'; 4 | 5 | class Photo extends React.Component { 6 | 7 | constructor(props) { 8 | super(props); 9 | 10 | this.id = this.props.result.id; 11 | this.thumb = this.props.result.urls.thumb; 12 | this.img = this.props.result.urls.small; 13 | this.full_size = this.props.result.urls.raw; 14 | this.author = this.props.result.user.name; 15 | this.img_title = `${instant_img_localize.photo_by} ${this.author}`; 16 | this.filename = this.props.result.id; 17 | 18 | this.alt = ''; 19 | this.caption = ''; 20 | this.user = this.props.result.user.username; 21 | this.user_photo = this.props.result.user.profile_image.small; 22 | this.link = this.props.result.links.html; 23 | this.likes = this.props.result.likes; 24 | this.view_all = instant_img_localize.view_all; 25 | this.like_text = instant_img_localize.likes; 26 | this.inProgress = false; 27 | this.container = document.querySelector('.instant-img-container'); 28 | 29 | this.setAsFeaturedImage = false; 30 | this.insertIntoPost = false; 31 | 32 | 33 | // Gutenberg Editor 34 | this.is_block_editor = this.props.blockEditor; 35 | this.SetFeaturedImage = this.props.SetFeaturedImage; 36 | this.InsertImage = this.props.InsertImage; 37 | 38 | 39 | // Photo state 40 | this.state = { 41 | filename: this.filename, 42 | alt: this.alt, 43 | caption: this.caption 44 | } 45 | 46 | } 47 | 48 | 49 | 50 | /* 51 | * uploadPhoto 52 | * Function to trigger image upload 53 | * 54 | * @param target element clicked item 55 | * @since 3.0 56 | */ 57 | uploadPhoto(e){ 58 | e.preventDefault(); 59 | 60 | let self = this; 61 | let target = e.currentTarget; // get current 62 | let photo = target.parentElement.parentElement.parentElement; // Get parent .photo el 63 | let notice = photo.querySelector('.notice-msg'); // Locate .notice-msg div 64 | 65 | if(!target.classList.contains('upload')){ // If target is .download-photo, switch target definition 66 | target = photo.querySelector('a.upload'); 67 | } 68 | 69 | if(target.classList.contains('success') || this.inProgress) 70 | return false; // Exit if already uploaded or in progress. 71 | 72 | target.classList.add('uploading'); 73 | photo.classList.add('in-progress'); 74 | notice.innerHTML = instant_img_localize.saving; 75 | this.inProgress = true; 76 | 77 | // Create Data Array 78 | let data = { 79 | id : target.getAttribute('data-id'), 80 | image : target.getAttribute('data-url') 81 | } 82 | 83 | // REST API URL 84 | let url = instant_img_localize.root + 'instant-images/upload/'; 85 | 86 | axios({ 87 | method: 'POST', 88 | url: url, 89 | headers: { 90 | 'X-WP-Nonce': instant_img_localize.nonce, 91 | 'Content-Type': 'application/json' 92 | }, 93 | data: { 94 | 'data': JSON.stringify(data) 95 | } 96 | }) 97 | .then(function (res) { 98 | 99 | //console.log(res); 100 | 101 | let response = res.data; 102 | 103 | if(response && res.status == 200){ 104 | 105 | // Successful response from server 106 | let hasError = response.error; 107 | let path = response.path; 108 | let filename = response.filename; 109 | 110 | if(hasError){ // Upload Error 111 | self.uploadError(target, photo, response.msg); 112 | 113 | } else { // Upload Success 114 | self.resizeImage(path, filename, target, photo, notice); 115 | self.triggerUnsplashDownload(data.id); 116 | 117 | } 118 | 119 | } else { 120 | 121 | // Error 122 | self.uploadError(target, photo, instant_img_localize.error_upload); 123 | } 124 | 125 | }) 126 | .catch(function (error) { 127 | console.log(error); 128 | }); 129 | 130 | } 131 | 132 | 133 | 134 | /* 135 | * resizeImage 136 | * Function to trigger image resize 137 | * 138 | * @param path string full server path to image 139 | * @param filename string clicked item 140 | * @param target element clicked item 141 | * @param photo element Nearest parent .photo 142 | * @param notice string Text to be display 143 | * @since 3.0 144 | */ 145 | resizeImage(path, filename, target, photo, notice){ 146 | 147 | let self = this; 148 | 149 | target.classList.remove('uploading'); 150 | target.classList.add('resizing'); 151 | notice.innerHTML = instant_img_localize.resizing; 152 | 153 | // Create Data Array 154 | let data = { // store data in JSON to pass to XMLHttpRequest 155 | 'path' : path, 156 | 'filename' : filename, 157 | 'title' : target.getAttribute('data-title'), 158 | 'custom_filename' : target.getAttribute('data-filename'), 159 | 'alt' : target.getAttribute('data-alt'), 160 | 'caption' : target.getAttribute('data-caption'), 161 | } 162 | 163 | // REST API URL 164 | let url= instant_img_localize.root + 'instant-images/resize/'; 165 | let msg = ''; 166 | 167 | axios({ 168 | method: 'POST', 169 | url: url, 170 | headers: { 171 | 'X-WP-Nonce': instant_img_localize.nonce, 172 | 'Content-Type': 'application/json' 173 | }, 174 | data: { 175 | 'data': JSON.stringify(data) 176 | } 177 | }) 178 | .then(function (res) { 179 | 180 | //console.log(res); 181 | 182 | let response = res.data; 183 | 184 | if(response && res.status == 200){ 185 | 186 | // Successful response from server 187 | let success = response.success; 188 | let attachment_id = response.id; 189 | let attachment_url = response.url; 190 | msg = response.msg; 191 | 192 | if(success){ 193 | 194 | // Success/Upload Complete 195 | self.uploadComplete(target, photo, msg); 196 | 197 | 198 | // Set as featured Image in Gutenberg 199 | if(self.is_block_editor && self.setAsFeaturedImage){ 200 | self.SetFeaturedImage(attachment_id); 201 | self.setAsFeaturedImage = false; 202 | } 203 | 204 | 205 | // Insrt Image into block editor 206 | if(self.is_block_editor && self.insertIntoPost){ 207 | if(attachment_url){ 208 | self.InsertImage(attachment_url, data.caption, data.alt); 209 | } 210 | self.insertIntoPost = false; 211 | } 212 | 213 | 214 | // If is media popup, redirect user to media-upload settings 215 | if(self.container.dataset.mediaPopup === 'true' && !self.is_block_editor){ 216 | window.location = 'media-upload.php?type=image&tab=library&attachment_id='+attachment_id; 217 | } 218 | 219 | 220 | }else{ 221 | 222 | // Error 223 | self.uploadError(target, photo, msg); 224 | 225 | } 226 | 227 | } else { 228 | 229 | // Error 230 | self.uploadError(target, photo, instant_img_localize.error_upload); 231 | } 232 | 233 | }) 234 | .catch(function (error) { 235 | console.log(error); 236 | }); 237 | 238 | } 239 | 240 | 241 | 242 | /* 243 | * triggerUnsplashDownload 244 | * Function to trigger download action at unsplash.com 245 | * This is used to give authors download credits and nothing more 246 | * 247 | * @param id string The ID of the image 248 | * @since 3.1 249 | */ 250 | triggerUnsplashDownload(id){ 251 | 252 | let url = `${API.photo_api}/${id}/download/${API.app_id}`; 253 | 254 | fetch(url) 255 | .then((data) => data.json()) 256 | .then(function(data) { 257 | // Success, nothing else happens here 258 | }) 259 | .catch(function(error) { 260 | console.log(error); 261 | }); 262 | } 263 | 264 | 265 | 266 | /* 267 | * setFeaturedImageClick 268 | * Function used to trigger a download and then set as featured image 269 | * 270 | * @since 4.0 271 | */ 272 | setFeaturedImageClick(e){ 273 | let target = e.currentTarget; 274 | if(!target){ 275 | return false; 276 | } 277 | 278 | let parent = target.parentNode.parentNode.parentNode; 279 | let photo = parent.querySelector('a.upload'); 280 | if(photo){ 281 | this.setAsFeaturedImage = true; 282 | photo.click(); 283 | } 284 | } 285 | 286 | 287 | 288 | /* 289 | * insertImageIntoPost 290 | * Function used to insert an image directly into the block (Gutenberg) editor. 291 | * 292 | * @since 4.0 293 | */ 294 | insertImageIntoPost(e){ 295 | let target = e.currentTarget; 296 | if(!target){ 297 | return false; 298 | } 299 | 300 | let parent = target.parentNode.parentNode.parentNode; 301 | let photo = parent.querySelector('a.upload'); 302 | if(photo){ 303 | this.insertIntoPost = true; 304 | photo.click(); 305 | } 306 | } 307 | 308 | 309 | 310 | /* 311 | * uploadComplete 312 | * Function runs when upload has completed 313 | * 314 | * @param target element clicked item 315 | * @param photo element Nearest parent .photo 316 | * @param msg string Success Msg 317 | * @since 3.0 318 | */ 319 | uploadComplete(target, photo, msg){ 320 | 321 | this.setImageTitle(target, msg); 322 | 323 | photo.classList.remove('in-progress'); 324 | photo.classList.add('uploaded'); 325 | 326 | photo.querySelector('.edit-photo').style.display = 'none'; // Hide edit-photo button 327 | 328 | if(this.is_block_editor){ 329 | photo.querySelector('.insert').style.display = 'none'; // Hide insert button 330 | photo.querySelector('.set-featured').style.display = 'none'; // Hide set-featured button 331 | } 332 | 333 | target.classList.remove('uploading'); 334 | target.classList.remove('resizing'); 335 | target.classList.add('success'); 336 | 337 | this.inProgress = false; 338 | 339 | // Refresh Media Library contents on edit pages 340 | if(this.container.classList.contains('editor')){ 341 | //console.log(wp.media.frame.setState()); 342 | if(typeof wp.media != 'undefined'){ 343 | if(wp.media.frame.content.get() !== null){ 344 | wp.media.frame.content.get().collection.props.set({ignore: (+ new Date())}); 345 | wp.media.frame.content.get().options.selection.reset(); 346 | }else{ 347 | wp.media.frame.library.props.set({ignore: (+ new Date())}); 348 | } 349 | } 350 | } 351 | 352 | } 353 | 354 | 355 | 356 | /* 357 | * uploadError 358 | * Function runs when error occurs on upload or resize 359 | * 360 | * @param target element Current clicked item 361 | * @param photo element Nearest parent .photo 362 | * @param msg string Error Msg 363 | * @since 3.0 364 | */ 365 | uploadError(target, photo, msg){ 366 | target.classList.remove('uploading'); 367 | target.classList.remove('resizing'); 368 | target.classList.add('errors'); 369 | this.setImageTitle(target, msg); 370 | this.inProgress = false; 371 | console.warn(msg); 372 | } 373 | 374 | 375 | 376 | /* 377 | * setImageTitle 378 | * Set the title attribute of target 379 | * 380 | * @param target element Current clicked item 381 | * @param msg string Title Msg from JSON 382 | * @since 3.0 383 | */ 384 | setImageTitle(target, msg){ 385 | target.setAttribute("title", msg); // Remove 'Click to upload...', set new value 386 | } 387 | 388 | 389 | 390 | /* 391 | * showEditScreen 392 | * Displays the edit screen 393 | * 394 | * @since 3.2 395 | */ 396 | showEditScreen(e){ 397 | e.preventDefault(); 398 | let el = e.currentTarget; 399 | let photo = el.closest('.photo'); 400 | let filename = photo.querySelector('input[name="filename"]'); 401 | let editScreen = photo.querySelector('.edit-screen'); 402 | 403 | editScreen.classList.add('editing'); // Show edit screen 404 | 405 | // Set focus on edit screen 406 | setTimeout(function(){ 407 | editScreen.focus(); 408 | }, 150); 409 | 410 | } 411 | 412 | 413 | 414 | /* 415 | * handleEditChange 416 | * Handles the change event for the edit screen 417 | * 418 | * @since 3.2 419 | */ 420 | handleEditChange(e) { 421 | let target = e.target.name; 422 | 423 | if(target === 'filename'){ 424 | this.setState({ 425 | filename: e.target.value 426 | }); 427 | } 428 | if(target === 'alt'){ 429 | this.setState({ 430 | alt: e.target.value 431 | }); 432 | } 433 | if(target === 'caption'){ 434 | this.setState({ 435 | caption: e.target.value 436 | }); 437 | } 438 | } 439 | 440 | 441 | 442 | /* 443 | * saveEditChange 444 | * Handles the save event for the edit screen 445 | * 446 | * @since 3.2 447 | */ 448 | saveEditChange(e) { 449 | 450 | let el = e.currentTarget; 451 | let photo = el.closest('.photo'); 452 | 453 | let filename = photo.querySelector('input[name="filename"]'); 454 | this.filename = filename.value; 455 | let alt = photo.querySelector('input[name="alt"]'); 456 | this.alt = alt.value; 457 | let caption = photo.querySelector('textarea[name="caption"]'); 458 | this.caption = caption.value; 459 | 460 | photo.querySelector('.edit-screen').classList.remove('editing'); // Hide edit screen 461 | photo.querySelector('a.upload').focus(); 462 | } 463 | 464 | 465 | 466 | 467 | /* 468 | * cancelEditChange 469 | * Handles the cancel event for the edit screen 470 | * 471 | * @since 3.2 472 | */ 473 | cancelEditChange(e) { 474 | 475 | let el = e.currentTarget; 476 | let photo = el.closest('.photo'); 477 | if(photo){ 478 | let target = photo.querySelector('a.upload'); 479 | 480 | let filename = photo.querySelector('input[name="filename"]'); 481 | filename.value = this.filename; 482 | let alt = photo.querySelector('input[name="alt"]'); 483 | alt.value = this.alt; 484 | let caption = photo.querySelector('textarea[name="caption"]'); 485 | caption.value = this.caption; 486 | 487 | photo.querySelector('.edit-screen').classList.remove('editing'); // Hide edit screen 488 | target.focus(); 489 | } 490 | } 491 | 492 | 493 | 494 | 495 | render(){ 496 | 497 | return ( 498 |
499 |
500 |
501 | this.uploadPhoto(e)}> 512 | 513 |
514 | 515 | 516 | 579 | 580 |
581 |
582 |

{instant_img_localize.edit_details}

583 |

{instant_img_localize.edit_details_intro}.

584 |
585 | 590 | 594 | 598 |
599 |   600 | 601 |
602 |
603 | 604 |
605 |
606 | ) 607 | } 608 | } 609 | 610 | export default Photo; --------------------------------------------------------------------------------