register(new LFDrive_Elementor_Widget());}); foreach(['file','image','pdf','video','audio','sale','uploader'] as $type)add_shortcode('lfdrive_'.$type,static function($a)use($type){return self::render($type,(array)$a);}); } // Legacy reads preserve connections and attachment URLs created before 1.2.4. // New writes use the full lfdrive_ prefix; no migration or network request runs at activation. static function option($key,$default=false) { $value=get_option($key,null); if($value!==null)return $value; $legacy=['lfdrive_token'=>'lfd_token','lfdrive_account'=>'lfd_account']; return isset($legacy[$key])?get_option($legacy[$key],$default):$default; } static function meta($id,$key,$single=true) { if(metadata_exists('post',$id,$key))return get_post_meta($id,$key,$single); return get_post_meta($id,str_replace('_lfdrive_','_lfd_',$key),$single); } static function delete_meta($id,$key) { delete_post_meta($id,$key); delete_post_meta($id,str_replace('_lfdrive_','_lfd_',$key)); } static function attachment_ids($file_id,$limit) { $conditions=[]; foreach(['file_id'=>(string)$file_id,'account'=>(string)self::option('lfdrive_account','')] as $key=>$value){ $new='_lfdrive_'.$key;$legacy='_lfd_'.$key; $conditions[]=['relation'=>'OR',['key'=>$new,'value'=>$value],['relation'=>'AND',['key'=>$new,'compare'=>'NOT EXISTS'],['key'=>$legacy,'value'=>$value]]]; } // Both file ID and integration account are required; legacy keys are fallback-only. // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query return get_posts(['post_type'=>'attachment','post_status'=>'inherit','posts_per_page'=>$limit,'fields'=>'ids','meta_query'=>array_merge(['relation'=>'AND'],$conditions)]); } static function encrypt($token) {if(!function_exists('openssl_encrypt'))return new WP_Error('lfdrive_crypto','На сервере WordPress требуется расширение OpenSSL');$iv=random_bytes(12);$tag='';$c=openssl_encrypt($token,'aes-256-gcm',hash('sha256',wp_salt('auth'),true),OPENSSL_RAW_DATA,$iv,$tag);return $c===false?new WP_Error('lfdrive_crypto','Ошибка защиты токена'):base64_encode($iv.$tag.$c);} static function token() {$v=base64_decode((string)self::option('lfdrive_token',''),true);if(!$v||strlen($v)<29||!function_exists('openssl_decrypt'))return '';return openssl_decrypt(substr($v,28),'aes-256-gcm',hash('sha256',wp_salt('auth'),true),OPENSSL_RAW_DATA,substr($v,0,12),substr($v,12,16))?:'';} static function api($action,$args=[],$file=null,$override=null) { $token=$override??self::token();if(!$token)return new WP_Error('lfdrive_token','Подключите LF Drive в настройках плагина'); $opts=['timeout'=>60,'redirection'=>0,'sslverify'=>true,'headers'=>['Authorization'=>'Bearer '.$token,'Accept'=>'application/json']]; if($file){$boundary='lfdrive'.bin2hex(random_bytes(16));$body='';foreach(['action'=>$action]+$args as $key=>$val)$body.='--'.$boundary."\r\nContent-Disposition: form-data; name=\"".$key."\"\r\n\r\n".$val."\r\n"; $name=str_replace(['"',"\r","\n",'\\'],['_','','','_'],basename($file['name']));$body.='--'.$boundary."\r\nContent-Disposition: form-data; name=\"file\"; filename=\"".$name."\"\r\nContent-Type: application/octet-stream\r\n\r\n".file_get_contents($file['tmp_name'])."\r\n--".$boundary."--\r\n";$opts['headers']['Content-Type']='multipart/form-data; boundary='.$boundary;$opts['body']=$body;$opts['method']='POST';$url=self::API; }elseif($action==='create_form'){$opts['method']='POST';$opts['body']=['action'=>$action]+$args;$url=self::API;} else{$url=add_query_arg(['action'=>$action]+$args,self::API);} $r=wp_remote_request($url,$opts);if(is_wp_error($r))return new WP_Error('lfdrive_network','Нет ответа LF Drive. Для загрузки проверьте результат операции перед повтором.');$json=json_decode(wp_remote_retrieve_body($r),true); if(!is_array($json))return new WP_Error('lfdrive_response','LF Drive вернул некорректный ответ');if(empty($json['ok']))return new WP_Error('lfdrive_remote',sanitize_text_field($json['error']??'Ошибка LF Drive'));return $json; } static function ajax() { check_ajax_referer('lfdrive','nonce');if(!current_user_can('upload_files'))wp_send_json_error(['error'=>'Недостаточно прав'],403); $action=sanitize_key($_POST['operation']??'');$args=[]; if($action==='connect') {if(!current_user_can('manage_options'))wp_send_json_error(['error'=>'Требуются права администратора'],403);$token=isset($_POST['token'])&&is_string($_POST['token'])?sanitize_text_field(wp_unslash($_POST['token'])):'';if(!preg_match('/^lf_wp_[a-f0-9]{64}$/D',$token))wp_send_json_error(['error'=>'Нужен отдельный токен WordPress'],400);$result=self::api('me',[],null,$token);if(!is_wp_error($result)){$enc=self::encrypt($token);if(is_wp_error($enc))$result=$enc;else {update_option('lfdrive_token',$enc,false);update_option('lfdrive_account',hash('sha256',(string)$result['user']['id'].':'.$result['user']['email']),false);delete_option('lfd_token');delete_option('lfd_account');}}} elseif($action==='disconnect'){if(!current_user_can('manage_options'))wp_send_json_error(['error'=>'Требуются права администратора'],403);delete_option('lfdrive_token');delete_option('lfdrive_account');delete_option('lfd_token');delete_option('lfd_account');$result=['ok'=>true];} elseif($action==='pdf_preview'){$url=isset($_POST['url'])&&is_string($_POST['url'])?esc_url_raw(wp_unslash($_POST['url'])):'';$proxy=LFDrive_Design::proxy_url($url);$result=is_wp_error($proxy)?$proxy:['ok'=>true,'proxy'=>$proxy];} elseif($action==='attachment'){$r=self::api('file',['id'=>absint($_POST['id']??0)]);$result=is_wp_error($r)?$r:self::attach($r['file'],min(100000,absint($_POST['width']??0)),min(100000,absint($_POST['height']??0)));} else { if(!in_array($action,['me','files','file','stats','forms','upload','replace','create_form','operation'],true))wp_send_json_error(['error'=>'Неизвестное действие'],400); foreach(['id','page','project_id','file_id','days'] as $key)if(isset($_POST[$key]))$args[$key]=absint($_POST[$key]); foreach(['search','type','title','request_id'] as $key)if(isset($_POST[$key]))$args[$key]=sanitize_text_field(wp_unslash($_POST[$key])); $file=null;if(in_array($action,['upload','replace'],true)){ $file=isset($_FILES['file'])&&is_array($_FILES['file'])?map_deep($_FILES['file'],'sanitize_text_field'):null;if(!$file||is_array($file['error'])||(int)$file['error']!==UPLOAD_ERR_OK||!is_uploaded_file($file['tmp_name']))wp_send_json_error(['error'=>'Файл не получен целиком'],400); if(filesize($file['tmp_name'])>min(wp_max_upload_size(),64*1024*1024))wp_send_json_error(['error'=>'Файл превышает лимит WordPress или 64 МБ для загрузки через плагин'],400); $check=wp_check_filetype_and_ext($file['tmp_name'],sanitize_file_name($file['name']));if(!$check['ext']||!$check['type'])wp_send_json_error(['error'=>'WordPress не разрешает этот тип файла'],400); } $result=self::api($action,$args,$file); if(!is_wp_error($result)&&$action==='replace'&&!empty($result['file']))self::refresh($result['file']); } if(is_wp_error($result))wp_send_json_error(['error'=>$result->get_error_message()],400);wp_send_json_success($result); } static function attach($f,$width=0,$height=0) { if(!empty($f['protected'])||!empty($f['sale']))return new WP_Error('lfdrive_private','Защищённый или платный файл вставляйте через блок LF Drive, а не обычную медиатеку'); if(!self::safe_url($f['url']))return new WP_Error('lfdrive_url','Некорректная ссылка файла'); // Attachment lookup is constrained to one LF Drive file and account. // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query $ids=self::attachment_ids((int)$f['id'],1); if($ids){$id=$ids[0];self::refresh($f);}else{$id=wp_insert_attachment(['post_title'=>sanitize_text_field($f['name']),'post_mime_type'=>sanitize_mime_type($f['mime']),'post_status'=>'inherit','guid'=>esc_url_raw($f['url'])],false,0,true);if(is_wp_error($id))return $id;update_post_meta($id,'_lfdrive_file_id',(int)$f['id']);update_post_meta($id,'_lfdrive_account',self::option('lfdrive_account'));update_post_meta($id,'_lfdrive_url',esc_url_raw($f['url']));update_post_meta($id,'_lfdrive_size',(int)$f['size']);} if((strncmp($f['mime'],'image/',6)===0)&&$width&&$height){update_post_meta($id,'_lfdrive_width',$width);update_post_meta($id,'_lfdrive_height',$height);}return ['ok'=>true,'attachment'=>wp_prepare_attachment_for_js($id)]; } // Matching attachments must all be refreshed when a remote file changes. // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query static function refresh($f) {foreach(self::attachment_ids((int)$f['id'],-1) as $id){update_post_meta($id,'_lfdrive_url',esc_url_raw($f['url']));update_post_meta($id,'_lfdrive_size',(int)$f['size']);self::delete_meta($id,'_lfdrive_width');self::delete_meta($id,'_lfdrive_height');wp_update_post(['ID'=>$id,'post_title'=>sanitize_text_field($f['name']),'post_mime_type'=>sanitize_mime_type($f['mime'])]);clean_post_cache($id);}do_action('lfdrive_file_replaced',$f);} static function attachment_url($url,$id){return self::meta($id,'_lfdrive_url',true)?:$url;} static function attachment_meta($meta,$id){if(!self::meta($id,'_lfdrive_url',true))return $meta;return ['width'=>(int)self::meta($id,'_lfdrive_width',true),'height'=>(int)self::meta($id,'_lfdrive_height',true),'file'=>basename(wp_parse_url(self::meta($id,'_lfdrive_url',true),PHP_URL_PATH)),'sizes'=>[]];} static function downsize($out,$id,$size){$url=self::meta($id,'_lfdrive_url',true);return $url?[$url,(int)self::meta($id,'_lfdrive_width',true),(int)self::meta($id,'_lfdrive_height',true),false]:$out;} static function srcset($sources,$size,$src,$meta,$id){return self::meta($id,'_lfdrive_url',true)?false:$sources;} static function attachment_js($r,$post,$meta){$url=self::meta($post->ID,'_lfdrive_url',true);if(!$url)return $r;$r['url']=$url;$r['link']=$url;$r['filename']=basename(wp_parse_url($url,PHP_URL_PATH));$r['filesizeInBytes']=(int)self::meta($post->ID,'_lfdrive_size',true);$r['lfdrive']=true;if((strncmp($post->post_mime_type,'image/',6)===0)){$r['width']=(int)self::meta($post->ID,'_lfdrive_width',true);$r['height']=(int)self::meta($post->ID,'_lfdrive_height',true);$r['sizes']=['full'=>['url'=>$url,'width'=>$r['width'],'height'=>$r['height'],'orientation'=>$r['width']>=$r['height']?'landscape':'portrait']];}return $r;} static function safe_url($url){$p=wp_parse_url($url);return is_array($p)&&($p['scheme']??'')==='https'&&strtolower($p['host']??'')==='lfdrive.ru'&&!isset($p['user'])&&!isset($p['pass'])&&!isset($p['port']);} static function render($type,$a){return LFDrive_Design::render($type,$a);} static function blocks(){ wp_register_script('lfdrive-blocks',plugins_url('assets/blocks.js',__FILE__),['wp-blocks','wp-element','wp-components','wp-block-editor','wp-i18n','wp-data','lfdrive-admin','lfdrive-front'],'1.2.4',true); foreach(['library','file','image','pdf','video','audio','sale','uploader'] as $type){ $settings=['api_version'=>3,'attributes'=>array_merge(array_fill_keys(['url','title','label','description','price','alt','height'],['type'=>'string','default'=>'']),LFDrive_Design::attributes()),'render_callback'=>static fn($attributes)=>self::render($type,$attributes),'view_script_handles'=>['lfdrive-front'],'view_style_handles'=>['lfdrive-front']]; // Core may enqueue registered editor dependencies independently of our editor hook. if(current_user_can('upload_files'))$settings['editor_script']='lfdrive-blocks'; register_block_type('lfdrive/'.$type,$settings); } } static function menu(){self::$page_hook=add_media_page('LF Drive','LF Drive','upload_files','lf-drive',[__CLASS__,'page']);} static function media_button(){if(current_user_can('upload_files'))echo '';} static function register_assets(){ LFDrive_Design::register_assets(); wp_register_script('lfdrive-admin',plugins_url('assets/admin.js',__FILE__),['jquery','media-views','wp-util'],'1.2.4',true); wp_register_style('lfdrive-admin',plugins_url('assets/admin.css',__FILE__),[],'1.2.4'); } static function assets($hook='') { if(!current_user_can('upload_files'))return; $allowed=['media_page_lf-drive','post.php','post-new.php','upload.php','site-editor.php','widgets.php','customize.php']; if($hook!==self::$page_hook&&!in_array($hook,$allowed,true))return; if($hook===''&&self::$page_hook==='')return; wp_enqueue_media();self::media_assets(); } static function media_assets(){ if(self::$media_loaded||!current_user_can('upload_files'))return; self::$media_loaded=true; self::register_assets();LFDrive_Design::assets(); wp_enqueue_script('lfdrive-admin'); wp_localize_script('lfdrive-admin','LFDriveConfig',['ajax'=>admin_url('admin-ajax.php'),'nonce'=>wp_create_nonce('lfdrive'),'canManage'=>current_user_can('manage_options'),'maxUpload'=>min(wp_max_upload_size(),64*1024*1024),'connectUrl'=>'https://lfdrive.ru/app/wordpress.php','siteHost'=>wp_parse_url(home_url('/'),PHP_URL_HOST)]); wp_enqueue_style('lfdrive-admin'); } static function editor_assets(){ if(!current_user_can('upload_files'))return; wp_enqueue_media();self::media_assets();wp_enqueue_script('lfdrive-blocks'); } static function elementor_assets(){ if(!current_user_can('upload_files'))return; wp_enqueue_media();self::media_assets(); } static function editor_styles(){ if(!is_admin()||!current_user_can('upload_files')||!function_exists('get_current_screen'))return; $screen=get_current_screen(); if(!$screen||!method_exists($screen,'is_block_editor')||!$screen->is_block_editor())return; wp_enqueue_style('lfdrive-front',plugins_url('assets/front.css',__FILE__),[],'1.2.4'); wp_enqueue_style('lfdrive-admin',plugins_url('assets/admin.css',__FILE__),[],'1.2.4'); } static function page(){echo '

LF Drive

Файлы для вашего сайта. Загружайте, вставляйте и обновляйте без смены ссылок.

';} } require_once __DIR__.'/design.php'; add_action('wp_enqueue_scripts',['LFDrive_Design','frontend_assets']); add_action('wp_ajax_lfdrive_pdf',['LFDrive_Design','pdf_proxy']);add_action('wp_ajax_nopriv_lfdrive_pdf',['LFDrive_Design','pdf_proxy']); LFDrive_Plugin::boot();