×

Как генерируются ссылки на файлы изображений в MediaWiki?

Казалось бы, что может быть проще: получить ссылку на картинку? Но если вы имеете доступ только к базе данных сайта на движке mediawiki, то это не совсем тривиальная задача.

Описание проблемы

Имеется некоторое количество статей с картинками и ссылками на файлы в тексте. Нужно получить список всех используемых картинок и файлов в виде относительных ссылок (от корневой директории сайта).

Подводные камни

По умолчанию загружаемые файлы в вики лежат в папке images. Но загружаются они не просто в корень директории, а распихиваются по подпапкам. При стандартных настройках в LocalSettings.php уровень вложенности директорий равен двум. Название первой вложенной директории состоит из одного символа, второй из двух. Дальше уже идут файлы.

Если внимательно изучить неплохую документацию по структуре БД mediawiki, то несложно заметить, что ни в одной из таблиц нет ссылок на файлы в явном виде.

В таблице image хранятся названия файлов, но ссылок нет. Зато в столбце img_sha1 этой таблицы есть хэш, который, скорее всего, и используется для генерации ссылок. Но сходу понять логику генерации ссылок у меня не получилось.

Поиск решения

За ответом нужно обратиться к исходникам в директории includes. Ответ логично искать в файле File.php (у меня старая версия mediawiki, код может незначительно отличаться).

Вот нужный нам метод:

/**
 * Get urlencoded relative path of the file
 */
function getUrlRel() {
	return $this->getHashPath() . rawurlencode( $this->getName() );
}

Тут нас интересует функция getHashPath():

/**
 * Get the filename hash component of the directory including trailing slash,
 * e.g. f/fa/
 * If the repository is not hashed, returns an empty string.
 */
function getHashPath() {
	if ( !isset( $this->hashPath ) ) {
		$this->hashPath = $this->repo->getHashPath( $this->getName() );
	}
	return $this->hashPath;
}

Судя по коду, ответ нужно искать в файле FileRepo.php

Решение

А вот и нужная функция:

/**
 * Get a relative path including trailing slash, e.g. f/fa/
 * If the repo is not hashed, returns an empty string
 */
function getHashPath( $name ) {
	return self::getHashPathForLevel( $name, $this->hashLevels );
}

Вызывает она вот этот код:

static function getHashPathForLevel( $name, $levels ) {
	if ( $levels == 0 ) {
		return '';
	} else {
		$hash = md5( $name );
		$path = '';
		for ( $i = 1; $i <= $levels; $i++ ) {
			$path .= substr( $hash, 0, $i ) . '/';
		}
		return $path;
	}
}

Получается, что никакой sha1 хэш из базы данных нам не нужен. При генерации ссылки используется только уникальное имя файла (это первичный ключ таблицы) и уровень вложенности (по умолчанию равный двум). Чуть позже я нашел, что более компактно всё это описано вот тут.

Зная этот алгоритм несложно посчитать md5 хэш и взять из него первые 2 символа, чтобы составить ссылку.

Проблема решена

logo