ok

Mini Shell

Direktori : /var/softaculous/sitepad/editor/site-data/plugins/documentor/
Upload File :
Current File : //var/softaculous/sitepad/editor/site-data/plugins/documentor/documentor.php

<?php
/**
 * Plugin Name:  Documentor
 * Description:  Online Documentation Engine for WordPress
 * Version:	  1.0.0
 * Author:	   softaculous
 * Author URI:   https://softaculous.com
 * License:	  GPLv2 or later
 * License URI:  https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain:  documentor
 *
 * @package documentor
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Documentor class
 *
 * @class Documentor The class that holds the entire Documentor plugin
 */
class Documentor {
	/**
	 * The single class instance.
	 *
	 * @var $instance
	 */
	private static $instance = null;

	/**
	 * Path to the plugin directory
	 *
	 * @var $plugin_path
	 */
	public $plugin_path;

	/**
	 * URL to the plugin directory
	 *
	 * @var $plugin_url
	 */
	public $plugin_url;

	/**
	 * Theme templates directory path
	 *
	 * @var $theme_dir_path
	 */
	public $theme_dir_path;

	/**
	 * Path to template folder
	 *
	 * @var $theme_dir_path
	 */
	public $template_path;

	/**
	 * Post type name for documents
	 *
	 * @var $post_type
	 */
	public $post_type = 'docs';

	/**
	 * Current Page - is Docs Archive. Will be changed from Template Loader class
	 *
	 * @var $post_type
	 */
	public $is_archive = false;

	/**
	 * Current Page - is Docs Single. Will be changed from Template Loader class
	 *
	 * @var $post_type
	 */
	public $is_single = false;

	/*
	* @var $version 
	*/
	public $version = '1.0.1';
	
	/**
	 * Main Instance
	 * Ensures only one instance of this class exists in memory at any one time.
	 */
	public static function instance() {
		if ( is_null( self::$instance ) ) {
			self::$instance = new self();
			self::$instance->plugin_init();
		}
		return self::$instance;
	}

	/**
	 * Plugin init.
	 */
	public function plugin_init() {
		$this->plugin_path	= plugin_dir_path( __FILE__ );
		$this->plugin_url	 = plugin_dir_url( __FILE__ );
		$this->theme_dir_path = 'documentor/';
		$this->template_path  = $this->plugin_path . '/templates/';

		$this->include_dependencies();

		$this->maybe_setup();

		$this->add_image_sizes();
		
		// Update checker
		add_action('plugins_loaded', array( $this, 'update_check'));
		
		// load textdomain.
		load_plugin_textdomain( 'documentor', false, basename( dirname( __FILE__ ) ) . '/languages' );

		// custom post type register.
		add_action( 'init', array( $this, 'register_post_type' ) );

		// Loads frontend scripts and styles.
		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );

		register_deactivation_hook( __FILE__, array( $this, 'deactivation_hook' ) );
		register_activation_hook( __FILE__, array( $this, 'activation_hook' ) );
	}

	/**
	 * Activation hook.
	 */
	public function activation_hook() {
		$author = get_role( 'author' );
		$admin  = get_role( 'administrator' );

		/* Add documentor manager role */
		$documentor_manager = add_role( 'documentor_manager', __( 'Documentor Manager', 'documentor' ), $author->capabilities );

		$full_cap = array(
			'read_doc',
			'read_private_doc',
			'read_private_docs',
			'edit_doc',
			'edit_docs',
			'edit_others_docs',
			'edit_private_docs',
			'edit_published_docs',
			'delete_doc',
			'delete_docs',
			'delete_others_docs',
			'delete_private_docs',
			'delete_published_docs',
			'publish_docs',
		);

		/**
		 * Add full capacities to admin and docs manager roles
		 */
		foreach ( $full_cap as $cap ) {
			if ( null !== $admin ) {
				$admin->add_cap( $cap );
			}
			if ( null !== $documentor_manager ) {
				$documentor_manager->add_cap( $cap );
			}
		}

		// Create Docs page if not created.
		$settings = get_option( 'documentor_settings', array() );
		if ( ! $settings || ! $settings['docs_page_id'] ) {
			$documentor_page = wp_insert_post(
				array(
					'post_title'  => 'Docs',
					'post_type'   => 'page',
					'post_author' => get_current_user_id(),
					'post_status' => 'publish',
					'post_name'   => 'docs',
				)
			);

			if ( ! is_wp_error( $documentor_page ) ) {
				$settings['docs_page_id'] = $documentor_page;

				update_option( 'documentor_settings', $settings );
			}
		}

		// need to flush rules to reset permalinks.
		add_option( 'documentor_setup', 'pending' );
		
		// Enable for pagelayer as well
		$pl = get_option('pl_support_ept');
		
		if(empty($pl)){
			$pl = ['post', 'page'];
		}
		
		if(defined('PAGELAYER_VERSION') && !in_array($this->post_type, $pl)){
			$pl[] = $this->post_type;
			update_option('pl_support_ept', $pl);
		}
		
		include_once documentor()->plugin_path.'includes/template.php';
		
		documentor_import_template_content( json_decode(documentor_get_conf(),true), documentor_get_content());

	}

	/**
	 * Deactivation hook.
	 */
	public function deactivation_hook() {
		/* Deactivation actions */
	}

	/**
	 * Update checker hook.
	 */
	public function update_check() {
		/* Update checker hook */

		$current_version = get_option('documentor_version');
		$version = (int) str_replace('.', '', $current_version);

		// No update required
		if($current_version == $this->version){
			return true;
		}
		
		if($version < 101){
			
			$args = array(
				'post_type' => 'pagelayer-template',
				'post_status' => array('publish'),
				'meta_query' => array(
					array(
						'key' => 'documentor_imported_content',
					)
				)
			);
			
			$query = new WP_Query($args);

			if(empty($query->posts)){
				include_once documentor()->plugin_path.'includes/template.php';
				documentor_import_template_content( json_decode(documentor_get_conf(),true), documentor_get_content());
			}
		}
		
		// Save the new Version
		update_option('documentor_version', $this->version);
	}

	/**
	 * Maybe run setup code and rewrite rules.
	 */
	public function maybe_setup() {
		$documentor_archive_id = documentor()->get_option( 'docs_page_id', 'documentor_settings', false );
		$docs_page			= $documentor_archive_id ? get_post( $documentor_archive_id ) : false;
		$slug				 = $docs_page ? get_post_field( 'post_name', $docs_page ) : 'docs';

		if (
			get_option( 'documentor_setup', false ) === 'pending' ||
			get_option( 'documentor_current_slug', 'docs' ) !== $slug
		) {
			add_action( 'init', 'flush_rewrite_rules', 11, 0 );
			add_action( 'admin_init', 'flush_rewrite_rules', 11, 0 );

			delete_option( 'documentor_setup' );
			update_option( 'documentor_current_slug', $slug );
		}
	}

	/**
	 * Add image sizes.
	 */
	public function add_image_sizes() {
		// custom image sizes.
		add_image_size( 'documentor_archive_sm', 20, 20, true );
		add_image_size( 'documentor_archive', 40, 40, true );
		add_filter( 'image_size_names_choose', array( $this, 'image_size_names_choose' ) );
	}

	/**
	 * Custom image sizes
	 *
	 * @param array $sizes - registered image sizes.
	 *
	 * @return array
	 */
	public function image_size_names_choose( $sizes ) {
		return array_merge(
			$sizes,
			array(
				'documentor_archive_sm' => esc_html__( 'Archive Thumbnail Small (Documentor)', 'documentor' ),
				'documentor_archive'	=> esc_html__( 'Archive Thumbnail (Documentor)', 'documentor' ),
			)
		);
	}

	/**
	 * Include dependencies
	 */
	public function include_dependencies() {
		include_once documentor()->plugin_path . 'includes/class-template-loader.php';
		include_once documentor()->plugin_path . 'includes/class-walker-docs.php';
		include_once documentor()->plugin_path . 'includes/class-suggestion.php';

		if ( is_admin() ) {
			include_once documentor()->plugin_path . 'includes/admin/class-admin.php';
		}

		if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
			include_once documentor()->plugin_path . 'includes/class-ajax.php';
		}
	}

	/**
	 * Enqueue admin scripts
	 *
	 * Allows plugin assets to be loaded.
	 *
	 * @uses wp_enqueue_script()
	 * @uses wp_localize_script()
	 * @uses wp_enqueue_style
	 */
	public function enqueue_scripts() {
		
		$deps = array( 'jquery' );
		wp_register_style( 'documentor', documentor()->plugin_url . 'assets/css/style.min.css', array(), $this->version );
	
		if( documentor()->get_option( 'show_anchor_links', 'documentor_single', true ) ) {
			wp_register_script( 'anchor-js', documentor()->plugin_url . 'assets/vendor/anchor/anchor.min.js', array(),  $this->version, true );
			$deps[] = 'anchor-js';
		}
		
		wp_register_script( 'documentor', documentor()->plugin_url . 'assets/js/script.min.js', $deps, $this->version, true );
	
		if ( ! ( $this->is_archive || $this->is_single ) ) {
			return;
		}

		wp_enqueue_style( 'documentor');
		wp_style_add_data( 'documentor', 'rtl', 'replace' );
		wp_style_add_data( 'documentor', 'suffix', '.min' );

		if ( documentor()->get_option( 'show_anchor_links', 'documentor_single', true ) ) {
			wp_enqueue_script( 'anchor-js');
		}
		wp_enqueue_script( 'documentor' );
		wp_localize_script(
			'documentor',
			'documentor_vars',
			array(
				'ajaxurl' => admin_url( 'admin-ajax.php' ),
				'nonce'   => wp_create_nonce( 'documentor-ajax' ),
			)
		);

		// Custom script for AJAX.
		$ajax	  = documentor()->get_option( 'ajax', 'documentor_single', true );
		$custom_js = documentor()->get_option( 'ajax_custom_js', 'documentor_single', '' );
		if ( $ajax && $custom_js ) {
			wp_add_inline_script(
				'documentor',
				'
				(function ($) {
					$(document).on("documentor_ajax_loaded", function (event, new_page) {
						' . $custom_js . '
					});
				}(jQuery));
			'
			);
		}

		if ( $ajax && $this->is_single) {
			wp_add_inline_script('documentor', 'jQuery(".pagelayer-content").addClass("documentor-single-ajax");');
		}
	}

	/**
	 * Register the post type
	 *
	 * @return void
	 */
	public function register_post_type() {
		$documentor_archive_id = documentor()->get_option( 'docs_page_id', 'documentor_settings', false );
		$docs_page			= $documentor_archive_id ? get_post( $documentor_archive_id ) : false;

		$labels = array(
			'name'			   => $docs_page ? get_the_title( $docs_page ) : _x( 'Documentor', 'Post Type General Name', 'documentor' ),
			'singular_name'	  => _x( 'Doc', 'Post Type Singular Name', 'documentor' ),
			'menu_name'		  => __( 'Documentation', 'documentor' ),
			'parent_item_colon'  => __( 'Parent Doc', 'documentor' ),
			'all_items'		  => __( 'All Documentations', 'documentor' ),
			'view_item'		  => __( 'View Documentation', 'documentor' ),
			'add_new_item'	   => __( 'Add Documentation', 'documentor' ),
			'add_new'			=> __( 'Add New', 'documentor' ),
			'edit_item'		  => __( 'Edit Documentation', 'documentor' ),
			'update_item'		=> __( 'Update Documentation', 'documentor' ),
			'search_items'	   => __( 'Search Documentation', 'documentor' ),
			'not_found'		  => __( 'Not documentation found', 'documentor' ),
			'not_found_in_trash' => __( 'Not found in Trash', 'documentor' ),
		);

		$rewrite = array(
			'slug'	   => $docs_page ? get_post_field( 'post_name', $docs_page ) : 'docs',
			'with_front' => false,
			'pages'	  => true,
			'feeds'	  => true,
		);

		$args = array(
			'labels'			  => $labels,
			'supports'			=> array( 'title', 'editor', 'thumbnail', 'revisions', 'page-attributes', 'comments' ),
			'hierarchical'		=> true,
			'public'			  => true,
			'show_ui'			 => true,
			'show_in_menu'		=> false,
			'show_in_nav_menus'   => true,
			'show_in_admin_bar'   => true,
			'menu_position'	   => 5,
			'menu_icon'		   => 'dashicons-media-document',
			'can_export'		  => true,
			'has_archive'		 => $docs_page ? urldecode( get_page_uri( $documentor_archive_id ) ) : 'docs',
			'exclude_from_search' => false,
			'publicly_queryable'  => true,
			'show_in_rest'		=> true,
			'rewrite'			 => $rewrite,
			'map_meta_cap'		=> true,
			'capability_type'	 => array( 'doc', 'docs' ),
		);

		register_post_type( $this->post_type, $args );

		register_taxonomy(
			'docs_category',
			$this->post_type,
			array(
				'label'			  => esc_html__( 'Docs Categories', 'documentor' ),
				'labels'			 => array(
					'menu_name' => esc_html__( 'Categories', 'documentor' ),
				),
				'rewrite'			=> array(
					'slug' => 'docs-category',
				),
				'hierarchical'	   => false,
				'publicly_queryable' => false,
				'show_in_nav_menus'  => false,
				'show_in_rest'	   => true,
				'show_admin_column'  => true,
			)
		);
	}

	/**
	 * Get the value of a settings field
	 *
	 * @param string $option settings field name.
	 * @param string $section the section name this field belongs to.
	 * @param string $default default text if it's not found.
	 *
	 * @return mixed
	 */
	public function get_option( $option, $section, $default = '' ) {

		$options = get_option( $section );

		if ( isset( $options[ $option ] ) ) {
			return 'off' === $options[ $option ] ? false : ( 'on' === $options[ $option ] ? true : $options[ $option ] );
		}

		return $default;
	}

	/**
	 * Get template part or docs templates
	 * Looks at the theme directory first
	 *
	 * @param string $name template file name.
	 * @param string $data template data.
	 */
	public function get_template_part( $name, $data = array() ) {
		$name = (string) $name;

		// lookup at documentor/name.php.
		$template = locate_template(
			array(
				documentor()->theme_dir_path . "{$name}.php",
			)
		);

		// fallback to plugin default template.
		if ( ! $template && $name && file_exists( documentor()->template_path . "{$name}.php" ) ) {
			$template = documentor()->template_path . "{$name}.php";
		}

		if ( $template ) {
			$this->load_template( $template, $data );
		}
	}

	/**
	 * Load template with additional data.
	 *
	 * @param string $template_path - template path.
	 * @param array  $template_data - template data array.
	 */
	public function load_template( $template_path, $template_data ) {
		if ( isset( $template_data ) && is_array( $template_data ) ) {
			// phpcs:ignore
			extract( $template_data );
		}

		if ( file_exists( $template_path ) ) {
			include $template_path;
		}
	}

	/**
	 * Is Archive
	 *
	 * @return bool
	 */
	public function is_archive() {
		return $this->is_archive;
	}

	/**
	 * Is Single
	 *
	 * @return bool
	 */
	public function is_single() {
		return $this->is_single;
	}

	/**
	 * Get current document ID
	 *
	 * @return int
	 */
	public function get_current_doc_id() {
		global $post;

		if ( $post->post_parent ) {
			$ancestors = get_post_ancestors( $post->ID );
			$root	  = count( $ancestors ) - 1;
			$parent	= $ancestors[ $root ];
		} else {
			$parent = $post->ID;
		}

		return apply_filters( 'documentor_current_doc_id', $parent );
	}

	/**
	 * Get document page title
	 *
	 * @return string
	 */
	public function get_docs_page_title() {
		$title		= esc_html__( 'Documentation', 'documentor' );
		$docs_page_id = documentor()->get_option( 'docs_page_id', 'documentor_settings' );

		if ( $docs_page_id ) {
			$title = get_the_title( $docs_page_id );
		}

		return apply_filters( 'documentor_page_title', $title );
	}

	/**
	 * Get document page content
	 *
	 * @return string
	 */
	public function get_docs_page_content() {
		$content	  = '';
		$docs_page_id = documentor()->get_option( 'docs_page_id', 'documentor_settings' );

		if ( $docs_page_id ) {
			$content = get_post_field( 'post_content', $docs_page_id );
		}

		return apply_filters( 'documentor_page_content', $content );
	}

	/**
	 * Get breadcrumbs array
	 *
	 * @return array
	 */
	public function get_breadcrumbs_array() {
		global $post;

		$result	   = array();
		$docs_page_id = documentor()->get_option( 'docs_page_id', 'documentor_settings' );

		$result[] = array(
			'label'	=> __( 'Home', 'documentor' ),
			'url'	  => home_url( '/' ),
		);

		if ( $docs_page_id ) {
			$result[] = array(
				'label' => get_the_title( $docs_page_id ) ? get_the_title( $docs_page_id ) : __( 'Docs', 'documentor' ),
				'url'   => get_permalink( $docs_page_id ),
			);
		}

		if ( 'docs' === $post->post_type && $post->post_parent ) {
			$parent_id   = $post->post_parent;
			$temp_crumbs = array();

			while ( $parent_id ) {
				$page		  = get_post( $parent_id );
				$temp_crumbs[] = array(
					'label'	=> get_the_title( $page->ID ),
					'url'	  => get_permalink( $page->ID ),
				);
				$parent_id	 = $page->post_parent;
			}

			$temp_crumbs = array_reverse( $temp_crumbs );

			foreach ( $temp_crumbs as $crumb ) {
				$result[] = $crumb;
			}
		}

		return apply_filters( 'documentor_breadcrumbs_array', $result );
	}

	/**
	 * Next doc ID for the current doc page
	 *
	 * @return int
	 */
	public function get_next_adjacent_doc_id() {
		global $post, $wpdb;

		$parent = pagelayer_is_live() || wp_doing_ajax('adjacent_links') ? '0' : $post->post_parent;
		$menu_order = pagelayer_is_live() || wp_doing_ajax('adjacent_links') ? 1 : $post->menu_order;

		$next_query = "SELECT ID FROM $wpdb->posts
		WHERE post_parent = $parent and post_type = 'docs' and post_status = 'publish' and menu_order > $menu_order
		ORDER BY menu_order ASC
		LIMIT 0, 1";

		// phpcs:ignore
		return (int) $wpdb->get_var( $next_query );
	}

	/**
	 * Previous doc ID for the current doc page
	 *
	 * @return int
	 */
	public function get_previous_adjacent_doc_id() {
		global $post, $wpdb;

		$parent = pagelayer_is_live()  || wp_doing_ajax('adjacent_links') ? '0' : $post->post_parent;
		$menu_order = pagelayer_is_live() || wp_doing_ajax('adjacent_links') ? 1 : $post->menu_order;

		$prev_query = "SELECT ID FROM $wpdb->posts
		WHERE post_parent = $parent and post_type = 'docs' and post_status = 'publish' and menu_order < $menu_order
		ORDER BY menu_order DESC
		LIMIT 0, 1";

		// phpcs:ignore
		return (int) $wpdb->get_var( $prev_query );
	}

} // Documentor

/**
 * Initialize the plugin
 *
 * @return \Documentor
 */
function documentor() {
	return Documentor::instance();
}
documentor();
	
// To find posts when we submit /docs/space/arctile-name instead of /docs/space/category/arctile-name
add_filter('request', 'documentor_query_vars');
function documentor_query_vars($vars){// Handle direct links
	//print_r($vars);die();
	
	$doc = documentor();
	
	// Must be docs
	if(empty($vars['post_type']) || $vars['post_type'] != documentor()->post_type || empty($vars['name'])){
		return $vars;
	}
	
	if(!empty($_GET['debug'])){
		//print_r($vars);die();
	}
	
	$docs_archive_id = documentor()->get_option( 'docs_page_id', 'documentor_settings', false );
	$permalink = get_permalink($docs_archive_id);
	
	$docs = explode('/', $vars['docs']);
	
	// Find the post
	$args = array(
		'name'		=> $vars['name'],
		'post_type'   => 'docs',
		'post_status' => 'publish',
		'numberposts' => 10
	);
	
	$posts = get_posts($args);	
	//print_r($post);
	
	// Did we get it ?
	if(empty($posts)){
		
		// Ok lets try to see if we get with another method
		$tmp = explode('/', str_replace('_', '-', $vars['name']));
		$args['name'] = $tmp[count($tmp)-1];
		$posts = get_posts($args);	
		
		// If we still didnt get it, then lets return original vars
		if(empty($posts)){
			return $vars;
		}
		
	}
	
	$post = $posts[0];
	
	// If we have 2 parts, then find the closest as well
	if(count($docs) > 1){
		
		// If we found more than 1 results
		foreach($posts as $p){
			
			$url = get_permalink($p);			
			$_section = trim(str_replace($permalink, '', $url), '/');
			$parts = explode('/', $_section);
			
			if($parts[0] == $docs[0]){
				$post = $p;
				break;
			}
			
		}
		
	}
			
	if(!empty($_GET['debug'])){
		//print_r($post);die();
	}
	
	$url = get_permalink($post);
	$_section = trim(str_replace($permalink, '', $url), '/');
	//echo $url."\n";
	//echo $_section.' - '.$vars['name']."\n";die();
	
	// If the LINK is not same as the original, then redirect
	if($_section != trim($vars['name'], '/')){
		//die('redirecting !');
		wp_redirect($url.(!empty($_SERVER['QUERY_STRING']) ? '?'.$_SERVER['QUERY_STRING'] : ''));
		die();
	}
	
	$section = get_page_uri($post);
	//echo $section."\n";
	
	$vars['name'] = $vars['docs'] = trim($section, '/');
	//print_r($vars);
	//die($section);
	
	return $vars;
}

// No pagelayer content template should render
add_action( 'template_redirect', 'documentor_no_pagelayer_content', 101);
function documentor_no_pagelayer_content(){
	global $wp_query, $pagelayer;
	
	$docs = documentor();
	
	if(empty($wp_query->query['post_type']) || $wp_query->query['post_type'] !== $docs->post_type || !is_search()){
		return;
	}
	
	$pagelayer->template_post = null;
		
}

//Show pagination
add_action('pre_get_posts', 'documentor_show_posts');
function documentor_show_posts($query){

	//Return if the action is not 'search'
	if(!($query->is_search())){
		return;
	}
	
	if(@$query->query['post_type'] !== documentor()->post_type){
		return;
	}
	
	$query->query_vars['posts_per_page'] = 50;
	$query->query_vars['paged'] = (get_query_var('paged') ? get_query_var('paged') : 1);
}

// All post types are editable by Pagelayer
add_filter('pagelayer_supported_post_type', 'documentor_supported_post_type', 10, 1);
function documentor_supported_post_type($types){
	if(!in_array('docs', $types)){
		$types[] = 'docs';
	}
	return $types;
}

// Add shortcode in Pagelayer shortcode loader
// Loads the shortcodes
add_action( 'pagelayer_load_custom_widgets', 'documentor_load_shortcodes' );
function documentor_load_shortcodes(){
	include_once documentor()->plugin_path.'includes/shortcode_functions.php';
	include_once documentor()->plugin_path.'includes/shortcodes.php';
}

// Load editor js and css
add_action( 'pagelayer_custom_editor_enqueue', 'documentor_custom_editor_enqueue' );
function documentor_custom_editor_enqueue(){
	global $wp_query, $pagelayer;
	
	$docs = documentor();
	
	// Enqueue JS
	wp_register_script('documentor-editor', documentor()->plugin_url.'assets/js/widgets.js', array('jquery'), documentor()->version);
	wp_enqueue_script('documentor-editor');

	if(empty($wp_query->query['post_type']) || !($wp_query->query['post_type'] == $docs->post_type || $wp_query->query['post_type'] == $pagelayer->builder['name'])){
		return;
	}
	
	// Enqueue frontend js and CSS
	wp_enqueue_style( 'documentor');

	if ( documentor()->get_option( 'show_anchor_links', 'documentor_single', true ) ) {
		wp_enqueue_script( 'anchor-js');
	}

	wp_enqueue_script( 'documentor' );
}

// Load editor js and css
add_action( 'pagelayer_live_body_head', 'documentor_icon_enqueue' );
function documentor_icon_enqueue(){
	echo '<link rel="stylesheet" href="'.documentor()->plugin_url.'assets/css/documentor-icon.css">';
}

add_action('init', 'documentor_languages');
function documentor_languages(){
	global $pagelayer;

	$lang = @file_get_contents( documentor()->plugin_url .'/languages/en.json');
	$lang = @json_decode($lang, true);

	foreach($lang as $k => $v){
		
		if(!isset($pagelayer->l[$k])){
			$pagelayer->l[$k] = $v;
		}
	}
}

Zerion Mini Shell 1.0