Merge branch 'unit-tests' into develop

This commit is contained in:
Daniel Kraus
2017-08-27 22:53:12 +02:00
20 changed files with 1701 additions and 822 deletions

1
.atomignore Normal file
View File

@ -0,0 +1 @@
gh-pages/

425
Doxyfile
View File

@ -1,4 +1,4 @@
# Doxyfile 1.8.6 # Doxyfile 1.8.13
# This file describes the settings to be used by the documentation system # This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project. # doxygen (www.doxygen.org) for a project.
@ -46,10 +46,10 @@ PROJECT_NUMBER =
PROJECT_BRIEF = "Automatically add links to existing pages." PROJECT_BRIEF = "Automatically add links to existing pages."
# With the PROJECT_LOGO tag one can specify an logo or icon that is included in # With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# the documentation. The maximum height of the logo should not exceed 55 pixels # in the documentation. The maximum height of the logo should not exceed 55
# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
# to the output directory. # the logo to the output directory.
PROJECT_LOGO = PROJECT_LOGO =
@ -60,7 +60,7 @@ PROJECT_LOGO =
OUTPUT_DIRECTORY = OUTPUT_DIRECTORY =
# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
# directories (in 2 levels) under the output directory of each output format and # directories (in 2 levels) under the output directory of each output format and
# will distribute the generated files over these directories. Enabling this # will distribute the generated files over these directories. Enabling this
# option can be useful when feeding doxygen a huge amount of source files, where # option can be useful when feeding doxygen a huge amount of source files, where
@ -70,6 +70,14 @@ OUTPUT_DIRECTORY =
CREATE_SUBDIRS = NO CREATE_SUBDIRS = NO
# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
# characters to appear in the names of generated files. If set to NO, non-ASCII
# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
# U+3044.
# The default value is: NO.
ALLOW_UNICODE_NAMES = NO
# The OUTPUT_LANGUAGE tag is used to specify the language in which all # The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this # documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language. # information to generate all constant output in the proper language.
@ -85,14 +93,14 @@ CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English OUTPUT_LANGUAGE = English
# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
# descriptions after the members that are listed in the file and class # descriptions after the members that are listed in the file and class
# documentation (similar to Javadoc). Set to NO to disable this. # documentation (similar to Javadoc). Set to NO to disable this.
# The default value is: YES. # The default value is: YES.
BRIEF_MEMBER_DESC = YES BRIEF_MEMBER_DESC = YES
# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
# description of a member or function before the detailed description # description of a member or function before the detailed description
# #
# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
@ -127,7 +135,7 @@ ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO INLINE_INHERITED_MEMB = NO
# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
# before files name in the file list and in the header files. If set to NO the # before files name in the file list and in the header files. If set to NO the
# shortest path that makes the file name unique will be used # shortest path that makes the file name unique will be used
# The default value is: YES. # The default value is: YES.
@ -197,9 +205,9 @@ MULTILINE_CPP_IS_BRIEF = NO
INHERIT_DOCS = YES INHERIT_DOCS = YES
# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
# new page for each member. If set to NO, the documentation of a member will be # page for each member. If set to NO, the documentation of a member will be part
# part of the file/class/namespace that contains it. # of the file/class/namespace that contains it.
# The default value is: NO. # The default value is: NO.
SEPARATE_MEMBER_PAGES = NO SEPARATE_MEMBER_PAGES = NO
@ -261,11 +269,14 @@ OPTIMIZE_OUTPUT_VHDL = NO
# extension. Doxygen has a built-in mapping, but you can override or extend it # extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and # using this tag. The format is ext=language, where ext is a file extension, and
# language is one of the parsers supported by doxygen: IDL, Java, Javascript, # language is one of the parsers supported by doxygen: IDL, Java, Javascript,
# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make # C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
# (default is Fortran), use: inc=Fortran f=C. # Fortran. In the later case the parser tries to guess whether the code is fixed
# or free formatted code, this is the default for Fortran type files), VHDL. For
# instance to make doxygen treat .inc files as Fortran files (default is PHP),
# and .f files as C (default is Fortran), use: inc=Fortran f=C.
# #
# Note For files without extension you can use no_extension as a placeholder. # Note: For files without extension you can use no_extension as a placeholder.
# #
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise # Note that for custom extensions you also need to set FILE_PATTERNS otherwise
# the files are not read by doxygen. # the files are not read by doxygen.
@ -282,10 +293,19 @@ EXTENSION_MAPPING =
MARKDOWN_SUPPORT = YES MARKDOWN_SUPPORT = YES
# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
# to that level are automatically included in the table of contents, even if
# they do not have an id attribute.
# Note: This feature currently applies only to Markdown headings.
# Minimum value: 0, maximum value: 99, default value: 0.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
TOC_INCLUDE_HEADINGS = 0
# When enabled doxygen tries to link words that correspond to documented # When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can # classes, or namespaces to their corresponding documentation. Such a link can
# be prevented in individual cases by by putting a % sign in front of the word # be prevented in individual cases by putting a % sign in front of the word or
# or globally by setting AUTOLINK_SUPPORT to NO. # globally by setting AUTOLINK_SUPPORT to NO.
# The default value is: YES. # The default value is: YES.
AUTOLINK_SUPPORT = YES AUTOLINK_SUPPORT = YES
@ -325,13 +345,20 @@ SIP_SUPPORT = NO
IDL_PROPERTY_SUPPORT = YES IDL_PROPERTY_SUPPORT = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
# tag is set to YES, then doxygen will reuse the documentation of the first # tag is set to YES then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default # member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly. # all members of a group must be documented explicitly.
# The default value is: NO. # The default value is: NO.
DISTRIBUTE_GROUP_DOC = NO DISTRIBUTE_GROUP_DOC = NO
# If one adds a struct or class to a group and this option is enabled, then also
# any nested class or struct is added to the same group. By default this option
# is disabled and one has to add nested compounds explicitly via \ingroup.
# The default value is: NO.
GROUP_NESTED_COMPOUNDS = NO
# Set the SUBGROUPING tag to YES to allow class member groups of the same type # Set the SUBGROUPING tag to YES to allow class member groups of the same type
# (for instance a group of public functions) to be put as a subgroup of that # (for instance a group of public functions) to be put as a subgroup of that
# type (e.g. under the Public Functions section). Set it to NO to prevent # type (e.g. under the Public Functions section). Set it to NO to prevent
@ -390,7 +417,7 @@ LOOKUP_CACHE_SIZE = 0
# Build related configuration options # Build related configuration options
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
# documentation are documented, even if no documentation was available. Private # documentation are documented, even if no documentation was available. Private
# class members and static file members will be hidden unless the # class members and static file members will be hidden unless the
# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
@ -400,35 +427,35 @@ LOOKUP_CACHE_SIZE = 0
EXTRACT_ALL = NO EXTRACT_ALL = NO
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
# be included in the documentation. # be included in the documentation.
# The default value is: NO. # The default value is: NO.
EXTRACT_PRIVATE = NO EXTRACT_PRIVATE = NO
# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
# scope will be included in the documentation. # scope will be included in the documentation.
# The default value is: NO. # The default value is: NO.
EXTRACT_PACKAGE = NO EXTRACT_PACKAGE = NO
# If the EXTRACT_STATIC tag is set to YES all static members of a file will be # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
# included in the documentation. # included in the documentation.
# The default value is: NO. # The default value is: NO.
EXTRACT_STATIC = NO EXTRACT_STATIC = NO
# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
# locally in source files will be included in the documentation. If set to NO # locally in source files will be included in the documentation. If set to NO,
# only classes defined in header files are included. Does not have any effect # only classes defined in header files are included. Does not have any effect
# for Java sources. # for Java sources.
# The default value is: YES. # The default value is: YES.
EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_CLASSES = YES
# This flag is only useful for Objective-C code. When set to YES local methods, # This flag is only useful for Objective-C code. If set to YES, local methods,
# which are defined in the implementation section but not in the interface are # which are defined in the implementation section but not in the interface are
# included in the documentation. If set to NO only methods in the interface are # included in the documentation. If set to NO, only methods in the interface are
# included. # included.
# The default value is: NO. # The default value is: NO.
@ -453,21 +480,21 @@ HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy. If set # undocumented classes that are normally visible in the class hierarchy. If set
# to NO these classes will be included in the various overviews. This option has # to NO, these classes will be included in the various overviews. This option
# no effect if EXTRACT_ALL is enabled. # has no effect if EXTRACT_ALL is enabled.
# The default value is: NO. # The default value is: NO.
HIDE_UNDOC_CLASSES = NO HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
# (class|struct|union) declarations. If set to NO these declarations will be # (class|struct|union) declarations. If set to NO, these declarations will be
# included in the documentation. # included in the documentation.
# The default value is: NO. # The default value is: NO.
HIDE_FRIEND_COMPOUNDS = NO HIDE_FRIEND_COMPOUNDS = NO
# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
# documentation blocks found inside the body of a function. If set to NO these # documentation blocks found inside the body of a function. If set to NO, these
# blocks will be appended to the function's detailed documentation block. # blocks will be appended to the function's detailed documentation block.
# The default value is: NO. # The default value is: NO.
@ -481,7 +508,7 @@ HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO INTERNAL_DOCS = NO
# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
# names in lower-case letters. If set to YES upper-case letters are also # names in lower-case letters. If set to YES, upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ # allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows # in case and if your file system supports case sensitive file names. Windows
# and Mac users are advised to set this option to NO. # and Mac users are advised to set this option to NO.
@ -490,12 +517,19 @@ INTERNAL_DOCS = NO
CASE_SENSE_NAMES = YES CASE_SENSE_NAMES = YES
# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
# their full class and namespace scopes in the documentation. If set to YES the # their full class and namespace scopes in the documentation. If set to YES, the
# scope will be hidden. # scope will be hidden.
# The default value is: NO. # The default value is: NO.
HIDE_SCOPE_NAMES = NO HIDE_SCOPE_NAMES = NO
# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
# append additional text to a page's title, such as Class Reference. If set to
# YES the compound reference will be hidden.
# The default value is: NO.
HIDE_COMPOUND_REFERENCE= NO
# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
# the files that are included by a file in the documentation of that file. # the files that are included by a file in the documentation of that file.
# The default value is: YES. # The default value is: YES.
@ -523,14 +557,14 @@ INLINE_INFO = YES
# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
# (detailed) documentation of file and class members alphabetically by member # (detailed) documentation of file and class members alphabetically by member
# name. If set to NO the members will appear in declaration order. # name. If set to NO, the members will appear in declaration order.
# The default value is: YES. # The default value is: YES.
SORT_MEMBER_DOCS = YES SORT_MEMBER_DOCS = YES
# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
# descriptions of file, namespace and class members alphabetically by member # descriptions of file, namespace and class members alphabetically by member
# name. If set to NO the members will appear in declaration order. Note that # name. If set to NO, the members will appear in declaration order. Note that
# this will also influence the order of the classes in the class list. # this will also influence the order of the classes in the class list.
# The default value is: NO. # The default value is: NO.
@ -575,16 +609,14 @@ SORT_BY_SCOPE_NAME = NO
STRICT_PROTO_MATCHING = NO STRICT_PROTO_MATCHING = NO
# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
# todo list. This list is created by putting \todo commands in the # list. This list is created by putting \todo commands in the documentation.
# documentation.
# The default value is: YES. # The default value is: YES.
GENERATE_TODOLIST = YES GENERATE_TODOLIST = YES
# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
# test list. This list is created by putting \test commands in the # list. This list is created by putting \test commands in the documentation.
# documentation.
# The default value is: YES. # The default value is: YES.
GENERATE_TESTLIST = YES GENERATE_TESTLIST = YES
@ -620,8 +652,8 @@ ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30 MAX_INITIALIZER_LINES = 30
# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
# the bottom of the documentation of classes and structs. If set to YES the list # the bottom of the documentation of classes and structs. If set to YES, the
# will mention the files that were used to generate the documentation. # list will mention the files that were used to generate the documentation.
# The default value is: YES. # The default value is: YES.
SHOW_USED_FILES = YES SHOW_USED_FILES = YES
@ -669,8 +701,7 @@ LAYOUT_FILE =
# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
# For LaTeX the style of the bibliography can be controlled using # For LaTeX the style of the bibliography can be controlled using
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
# search path. Do not use file names with spaces, bibtex cannot handle them. See # search path. See also \cite for info how to create references.
# also \cite for info how to create references.
CITE_BIB_FILES = CITE_BIB_FILES =
@ -694,7 +725,7 @@ QUIET = NO
WARNINGS = YES WARNINGS = YES
# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
# will automatically be disabled. # will automatically be disabled.
# The default value is: YES. # The default value is: YES.
@ -711,12 +742,18 @@ WARN_IF_DOC_ERROR = YES
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
# are documented, but have no documentation for their parameters or return # are documented, but have no documentation for their parameters or return
# value. If set to NO doxygen will only warn about wrong or incomplete parameter # value. If set to NO, doxygen will only warn about wrong or incomplete
# documentation, but not about the absence of documentation. # parameter documentation, but not about the absence of documentation.
# The default value is: NO. # The default value is: NO.
WARN_NO_PARAMDOC = NO WARN_NO_PARAMDOC = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered.
# The default value is: NO.
WARN_AS_ERROR = NO
# The WARN_FORMAT tag determines the format of the warning messages that doxygen # The WARN_FORMAT tag determines the format of the warning messages that doxygen
# can produce. The string should contain the $file, $line, and $text tags, which # can produce. The string should contain the $file, $line, and $text tags, which
# will be replaced by the file and line number from which the warning originated # will be replaced by the file and line number from which the warning originated
@ -740,7 +777,7 @@ WARN_LOGFILE =
# The INPUT tag is used to specify the files and/or directories that contain # The INPUT tag is used to specify the files and/or directories that contain
# documented source files. You may enter file names like myfile.cpp or # documented source files. You may enter file names like myfile.cpp or
# directories like /usr/src/myproject. Separate the files or directories with # directories like /usr/src/myproject. Separate the files or directories with
# spaces. # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched. # Note: If this tag is empty the current directory is searched.
INPUT = INPUT =
@ -756,12 +793,17 @@ INPUT_ENCODING = UTF-8
# If the value of the INPUT tag contains directories, you can use the # If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
# *.h) to filter out the source-files in the directories. If left blank the # *.h) to filter out the source-files in the directories.
# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, #
# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, # Note that for custom extensions or not directly supported extensions you also
# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, # need to set EXTENSION_MAPPING for the extension otherwise the files are not
# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # read by doxygen.
# *.qsf, *.as and *.js. #
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
FILE_PATTERNS = FILE_PATTERNS =
@ -769,7 +811,7 @@ FILE_PATTERNS =
# be searched for input files as well. # be searched for input files as well.
# The default value is: NO. # The default value is: NO.
RECURSIVE = NO RECURSIVE = YES
# The EXCLUDE tag can be used to specify files and/or directories that should be # The EXCLUDE tag can be used to specify files and/or directories that should be
# excluded from the INPUT source files. This way you can easily exclude a # excluded from the INPUT source files. This way you can easily exclude a
@ -778,7 +820,8 @@ RECURSIVE = NO
# Note that relative paths are relative to the directory from which doxygen is # Note that relative paths are relative to the directory from which doxygen is
# run. # run.
EXCLUDE = README.md Maintenance.php EXCLUDE = README.md \
Maintenance.php
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded # directories that are symbolic links (a Unix file system feature) are excluded
@ -847,6 +890,10 @@ IMAGE_PATH =
# Note that the filter must not add or remove lines; it is applied before the # Note that the filter must not add or remove lines; it is applied before the
# code is scanned, but not when the output code is generated. If lines are added # code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly. # or removed, the anchors will not be placed correctly.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
INPUT_FILTER = INPUT_FILTER =
@ -856,6 +903,10 @@ INPUT_FILTER =
# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
# filters are used. If the FILTER_PATTERNS tag is empty or if none of the # filters are used. If the FILTER_PATTERNS tag is empty or if none of the
# patterns match the file name, INPUT_FILTER is applied. # patterns match the file name, INPUT_FILTER is applied.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
FILTER_PATTERNS = FILTER_PATTERNS =
@ -920,7 +971,7 @@ REFERENCED_BY_RELATION = NO
REFERENCES_RELATION = NO REFERENCES_RELATION = NO
# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
# to YES, then the hyperlinks from functions in REFERENCES_RELATION and # to YES then the hyperlinks from functions in REFERENCES_RELATION and
# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
# link to the documentation. # link to the documentation.
# The default value is: YES. # The default value is: YES.
@ -967,6 +1018,25 @@ USE_HTAGS = NO
VERBATIM_HEADERS = YES VERBATIM_HEADERS = YES
# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
# cost of reduced performance. This can be particularly helpful with template
# rich C++ code for which doxygen's built-in parser lacks the necessary type
# information.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse-libclang=ON option for CMake.
# The default value is: NO.
CLANG_ASSISTED_PARSING = NO
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# the include paths will already be set by doxygen for the files and directories
# specified with INPUT and INCLUDE_PATH.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_OPTIONS =
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index # Configuration options related to the alphabetical class index
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
@ -997,7 +1067,7 @@ IGNORE_PREFIX =
# Configuration options related to the HTML output # Configuration options related to the HTML output
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
# The default value is: YES. # The default value is: YES.
GENERATE_HTML = YES GENERATE_HTML = YES
@ -1059,13 +1129,15 @@ HTML_FOOTER =
HTML_STYLESHEET = HTML_STYLESHEET =
# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# defined cascading style sheet that is included after the standard style sheets # cascading style sheets that are included after the standard style sheets
# created by doxygen. Using this option one can overrule certain style aspects. # created by doxygen. Using this option one can overrule certain style aspects.
# This is preferred over using HTML_STYLESHEET since it does not replace the # This is preferred over using HTML_STYLESHEET since it does not replace the
# standard style sheet and is therefor more robust against future updates. # standard style sheet and is therefore more robust against future updates.
# Doxygen will copy the style sheet file to the output directory. For an example # Doxygen will copy the style sheet files to the output directory.
# see the documentation. # Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list). For an example see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET = HTML_EXTRA_STYLESHEET =
@ -1112,8 +1184,9 @@ HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
# page will contain the date and time when the page was generated. Setting this # page will contain the date and time when the page was generated. Setting this
# to NO can help when comparing the output of multiple runs. # to YES can help to show when doxygen was last run and thus if the
# The default value is: YES. # documentation is up to date.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
HTML_TIMESTAMP = YES HTML_TIMESTAMP = YES
@ -1209,15 +1282,15 @@ GENERATE_HTMLHELP = NO
CHM_FILE = CHM_FILE =
# The HHC_LOCATION tag can be used to specify the location (absolute path # The HHC_LOCATION tag can be used to specify the location (absolute path
# including file name) of the HTML help compiler ( hhc.exe). If non-empty # including file name) of the HTML help compiler (hhc.exe). If non-empty,
# doxygen will try to run the HTML help compiler on the generated index.hhp. # doxygen will try to run the HTML help compiler on the generated index.hhp.
# The file has to be specified with full path. # The file has to be specified with full path.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES. # This tag requires that the tag GENERATE_HTMLHELP is set to YES.
HHC_LOCATION = HHC_LOCATION =
# The GENERATE_CHI flag controls if a separate .chi index file is generated ( # The GENERATE_CHI flag controls if a separate .chi index file is generated
# YES) or that it should be included in the master .chm file ( NO). # (YES) or that it should be included in the master .chm file (NO).
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES. # This tag requires that the tag GENERATE_HTMLHELP is set to YES.
@ -1229,8 +1302,9 @@ GENERATE_CHI = NO
CHM_INDEX_ENCODING = CHM_INDEX_ENCODING =
# The BINARY_TOC flag controls whether a binary table of contents is generated ( # The BINARY_TOC flag controls whether a binary table of contents is generated
# YES) or a normal table of contents ( NO) in the .chm file. # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
# enables the Previous and Next buttons.
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES. # This tag requires that the tag GENERATE_HTMLHELP is set to YES.
@ -1371,7 +1445,7 @@ ENUM_VALUES_PER_LINE = 4
TREEVIEW_WIDTH = 250 TREEVIEW_WIDTH = 250
# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
# external symbols imported via tag files in a separate window. # external symbols imported via tag files in a separate window.
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES. # This tag requires that the tag GENERATE_HTML is set to YES.
@ -1400,7 +1474,7 @@ FORMULA_TRANSPARENT = YES
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
# http://www.mathjax.org) which uses client side Javascript for the rendering # http://www.mathjax.org) which uses client side Javascript for the rendering
# instead of using prerendered bitmaps. Use this if you do not have LaTeX # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When # installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path # enabled you may also need to install MathJax separately and configure the path
# to it using the MATHJAX_RELPATH option. # to it using the MATHJAX_RELPATH option.
@ -1470,11 +1544,11 @@ SEARCHENGINE = YES
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be # When the SERVER_BASED_SEARCH tag is enabled the search engine will be
# implemented using a web server instead of a web client using Javascript. There # implemented using a web server instead of a web client using Javascript. There
# are two flavours of web server based searching depending on the # are two flavors of web server based searching depending on the EXTERNAL_SEARCH
# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for # setting. When disabled, doxygen will generate a PHP script for searching and
# searching and an index file used by the script. When EXTERNAL_SEARCH is # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
# enabled the indexing and searching needs to be provided by external tools. See # and searching needs to be provided by external tools. See the section
# the section "External Indexing and Searching" for details. # "External Indexing and Searching" for details.
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag SEARCHENGINE is set to YES. # This tag requires that the tag SEARCHENGINE is set to YES.
@ -1537,7 +1611,7 @@ EXTRA_SEARCH_MAPPINGS =
# Configuration options related to the LaTeX output # Configuration options related to the LaTeX output
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output. # If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
# The default value is: YES. # The default value is: YES.
GENERATE_LATEX = NO GENERATE_LATEX = NO
@ -1568,7 +1642,7 @@ LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex MAKEINDEX_CMD_NAME = makeindex
# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX # If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
# documents. This may be useful for small projects and may help to save some # documents. This may be useful for small projects and may help to save some
# trees in general. # trees in general.
# The default value is: NO. # The default value is: NO.
@ -1586,9 +1660,12 @@ COMPACT_LATEX = NO
PAPER_TYPE = a4 PAPER_TYPE = a4
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
# that should be included in the LaTeX output. To get the times font for # that should be included in the LaTeX output. The package can be specified just
# instance you can specify # by its name or with the correct syntax as to be used with the LaTeX
# EXTRA_PACKAGES=times # \usepackage command. To get the times font for instance you can specify :
# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
# To use the option intlimits with the amsmath package you can specify:
# EXTRA_PACKAGES=[intlimits]{amsmath}
# If left blank no extra packages will be included. # If left blank no extra packages will be included.
# This tag requires that the tag GENERATE_LATEX is set to YES. # This tag requires that the tag GENERATE_LATEX is set to YES.
@ -1602,23 +1679,36 @@ EXTRA_PACKAGES =
# #
# Note: Only use a user-defined header if you know what you are doing! The # Note: Only use a user-defined header if you know what you are doing! The
# following commands have a special meaning inside the header: $title, # following commands have a special meaning inside the header: $title,
# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will # $datetime, $date, $doxygenversion, $projectname, $projectnumber,
# replace them by respectively the title of the page, the current date and time, # $projectbrief, $projectlogo. Doxygen will replace $title with the empty
# only the current date, the version number of doxygen, the project name (see # string, for the replacement values of the other commands the user is referred
# PROJECT_NAME), or the project number (see PROJECT_NUMBER). # to HTML_HEADER.
# This tag requires that the tag GENERATE_LATEX is set to YES. # This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER = LATEX_HEADER =
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
# generated LaTeX document. The footer should contain everything after the last # generated LaTeX document. The footer should contain everything after the last
# chapter. If it is left blank doxygen will generate a standard footer. # chapter. If it is left blank doxygen will generate a standard footer. See
# LATEX_HEADER for more information on how to generate a default footer and what
# special commands can be used inside the footer.
# #
# Note: Only use a user-defined footer if you know what you are doing! # Note: Only use a user-defined footer if you know what you are doing!
# This tag requires that the tag GENERATE_LATEX is set to YES. # This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_FOOTER = LATEX_FOOTER =
# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# LaTeX style sheets that are included after the standard style sheets created
# by doxygen. Using this option one can overrule certain style aspects. Doxygen
# will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list).
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_EXTRA_STYLESHEET =
# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the LATEX_OUTPUT output # other source files which should be copied to the LATEX_OUTPUT output
# directory. Note that the files will be copied as-is; there are no commands or # directory. Note that the files will be copied as-is; there are no commands or
@ -1636,8 +1726,8 @@ LATEX_EXTRA_FILES =
PDF_HYPERLINKS = YES PDF_HYPERLINKS = YES
# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate # If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
# the PDF file directly from the LaTeX files. Set this option to YES to get a # the PDF file directly from the LaTeX files. Set this option to YES, to get a
# higher quality PDF documentation. # higher quality PDF documentation.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag GENERATE_LATEX is set to YES. # This tag requires that the tag GENERATE_LATEX is set to YES.
@ -1678,11 +1768,19 @@ LATEX_SOURCE_CODE = NO
LATEX_BIB_STYLE = plain LATEX_BIB_STYLE = plain
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
# page will contain the date and time when the page was generated. Setting this
# to NO can help when comparing the output of multiple runs.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_TIMESTAMP = NO
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to the RTF output # Configuration options related to the RTF output
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The # If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
# RTF output is optimized for Word 97 and may not look too pretty with other RTF # RTF output is optimized for Word 97 and may not look too pretty with other RTF
# readers/editors. # readers/editors.
# The default value is: NO. # The default value is: NO.
@ -1697,7 +1795,7 @@ GENERATE_RTF = NO
RTF_OUTPUT = rtf RTF_OUTPUT = rtf
# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF # If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
# documents. This may be useful for small projects and may help to save some # documents. This may be useful for small projects and may help to save some
# trees in general. # trees in general.
# The default value is: NO. # The default value is: NO.
@ -1734,11 +1832,21 @@ RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE = RTF_EXTENSIONS_FILE =
# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
# with syntax highlighting in the RTF output.
#
# Note that which sources are shown also depends on other settings such as
# SOURCE_BROWSER.
# The default value is: NO.
# This tag requires that the tag GENERATE_RTF is set to YES.
RTF_SOURCE_CODE = NO
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options related to the man page output # Configuration options related to the man page output
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for # If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
# classes and files. # classes and files.
# The default value is: NO. # The default value is: NO.
@ -1762,6 +1870,13 @@ MAN_OUTPUT = man
MAN_EXTENSION = .3 MAN_EXTENSION = .3
# The MAN_SUBDIR tag determines the name of the directory created within
# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
# MAN_EXTENSION with the initial . removed.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_SUBDIR =
# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it # If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
# will generate one additional man file for each entity documented in the real # will generate one additional man file for each entity documented in the real
# man page(s). These additional files only source the real man page, but without # man page(s). These additional files only source the real man page, but without
@ -1775,7 +1890,7 @@ MAN_LINKS = NO
# Configuration options related to the XML output # Configuration options related to the XML output
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that # If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
# captures the structure of the code including all documentation. # captures the structure of the code including all documentation.
# The default value is: NO. # The default value is: NO.
@ -1789,19 +1904,7 @@ GENERATE_XML = NO
XML_OUTPUT = xml XML_OUTPUT = xml
# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a # If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
# validating XML parser to check the syntax of the XML files.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_SCHEMA =
# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
# validating XML parser to check the syntax of the XML files.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_DTD =
# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
# listings (including syntax highlighting and cross-referencing information) to # listings (including syntax highlighting and cross-referencing information) to
# the XML output. Note that enabling this will significantly increase the size # the XML output. Note that enabling this will significantly increase the size
# of the XML output. # of the XML output.
@ -1814,7 +1917,7 @@ XML_PROGRAMLISTING = YES
# Configuration options related to the DOCBOOK output # Configuration options related to the DOCBOOK output
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files # If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
# that can be used to generate PDF. # that can be used to generate PDF.
# The default value is: NO. # The default value is: NO.
@ -1828,14 +1931,23 @@ GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook DOCBOOK_OUTPUT = docbook
# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
# program listings (including syntax highlighting and cross-referencing
# information) to the DOCBOOK output. Note that enabling this will significantly
# increase the size of the DOCBOOK output.
# The default value is: NO.
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
DOCBOOK_PROGRAMLISTING = NO
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output # Configuration options for the AutoGen Definitions output
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
# Definitions (see http://autogen.sf.net) file that captures the structure of # AutoGen Definitions (see http://autogen.sf.net) file that captures the
# the code including all documentation. Note that this feature is still # structure of the code including all documentation. Note that this feature is
# experimental and incomplete at the moment. # still experimental and incomplete at the moment.
# The default value is: NO. # The default value is: NO.
GENERATE_AUTOGEN_DEF = NO GENERATE_AUTOGEN_DEF = NO
@ -1844,7 +1956,7 @@ GENERATE_AUTOGEN_DEF = NO
# Configuration options related to the Perl module output # Configuration options related to the Perl module output
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module # If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
# file that captures the structure of the code including all documentation. # file that captures the structure of the code including all documentation.
# #
# Note that this feature is still experimental and incomplete at the moment. # Note that this feature is still experimental and incomplete at the moment.
@ -1852,7 +1964,7 @@ GENERATE_AUTOGEN_DEF = NO
GENERATE_PERLMOD = NO GENERATE_PERLMOD = NO
# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary # If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI # Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
# output from the Perl module output. # output from the Perl module output.
# The default value is: NO. # The default value is: NO.
@ -1860,9 +1972,9 @@ GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO PERLMOD_LATEX = NO
# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely # If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
# formatted so it can be parsed by a human reader. This is useful if you want to # formatted so it can be parsed by a human reader. This is useful if you want to
# understand what is going on. On the other hand, if this tag is set to NO the # understand what is going on. On the other hand, if this tag is set to NO, the
# size of the Perl module output will be much smaller and Perl will parse it # size of the Perl module output will be much smaller and Perl will parse it
# just the same. # just the same.
# The default value is: YES. # The default value is: YES.
@ -1882,14 +1994,14 @@ PERLMOD_MAKEVAR_PREFIX =
# Configuration options related to the preprocessor # Configuration options related to the preprocessor
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all # If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
# C-preprocessor directives found in the sources and include files. # C-preprocessor directives found in the sources and include files.
# The default value is: YES. # The default value is: YES.
ENABLE_PREPROCESSING = YES ENABLE_PREPROCESSING = YES
# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names # If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
# in the source code. If set to NO only conditional compilation will be # in the source code. If set to NO, only conditional compilation will be
# performed. Macro expansion can be done in a controlled way by setting # performed. Macro expansion can be done in a controlled way by setting
# EXPAND_ONLY_PREDEF to YES. # EXPAND_ONLY_PREDEF to YES.
# The default value is: NO. # The default value is: NO.
@ -1905,7 +2017,7 @@ MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO EXPAND_ONLY_PREDEF = NO
# If the SEARCH_INCLUDES tag is set to YES the includes files in the # If the SEARCH_INCLUDES tag is set to YES, the include files in the
# INCLUDE_PATH will be searched if a #include is found. # INCLUDE_PATH will be searched if a #include is found.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
@ -1947,9 +2059,9 @@ PREDEFINED =
EXPAND_AS_DEFINED = EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
# remove all refrences to function-like macros that are alone on a line, have an # remove all references to function-like macros that are alone on a line, have
# all uppercase name, and do not end with a semicolon. Such function macros are # an all uppercase name, and do not end with a semicolon. Such function macros
# typically used for boiler-plate code, and will confuse the parser if not # are typically used for boiler-plate code, and will confuse the parser if not
# removed. # removed.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
@ -1969,15 +2081,11 @@ SKIP_FUNCTION_MACROS = YES
# where loc1 and loc2 can be relative or absolute paths or URLs. See the # where loc1 and loc2 can be relative or absolute paths or URLs. See the
# section "Linking to external documentation" for more information about the use # section "Linking to external documentation" for more information about the use
# of tag files. # of tag files.
# Note: Each tag file must have an unique name (where the name does NOT include # Note: Each tag file must have a unique name (where the name does NOT include
# the path). If a tag file is not located in the directory in which doxygen is # the path). If a tag file is not located in the directory in which doxygen is
# run, you must also specify the path to the tagfile here. # run, you must also specify the path to the tagfile here.
# Documentation was cluttered when all MediaWiki content was included. It's TAGFILES =
# too bad for the missing links to the two parent classes (Maintenance and
# SpecialPage), but if users want to look up these, they can just head over
# to mediawiki.org.
# TAGFILES = "Doxygen_mwtags.xml = https://doc.wikimedia.org/mediawiki-core/REL1_23/php/html"
# When a file name is specified after GENERATE_TAGFILE, doxygen will create a # When a file name is specified after GENERATE_TAGFILE, doxygen will create a
# tag file that is based on the input files it reads. See section "Linking to # tag file that is based on the input files it reads. See section "Linking to
@ -1985,20 +2093,21 @@ SKIP_FUNCTION_MACROS = YES
GENERATE_TAGFILE = GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES all external class will be listed in the # If the ALLEXTERNALS tag is set to YES, all external class will be listed in
# class index. If set to NO only the inherited external classes will be listed. # the class index. If set to NO, only the inherited external classes will be
# listed.
# The default value is: NO. # The default value is: NO.
ALLEXTERNALS = NO ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in # If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
# the modules index. If set to NO, only the current project's groups will be # in the modules index. If set to NO, only the current project's groups will be
# listed. # listed.
# The default value is: YES. # The default value is: YES.
EXTERNAL_GROUPS = YES EXTERNAL_GROUPS = YES
# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in # If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
# the related pages index. If set to NO, only the current project's pages will # the related pages index. If set to NO, only the current project's pages will
# be listed. # be listed.
# The default value is: YES. # The default value is: YES.
@ -2015,7 +2124,7 @@ PERL_PATH = /usr/bin/perl
# Configuration options related to the dot tool # Configuration options related to the dot tool
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram # If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to # (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
# NO turns the diagrams off. Note that this option also works with HAVE_DOT # NO turns the diagrams off. Note that this option also works with HAVE_DOT
# disabled, but it is recommended to install and use dot, since it yields more # disabled, but it is recommended to install and use dot, since it yields more
@ -2040,7 +2149,7 @@ MSCGEN_PATH =
DIA_PATH = DIA_PATH =
# If set to YES, the inheritance and collaboration graphs will hide inheritance # If set to YES the inheritance and collaboration graphs will hide inheritance
# and usage relations if the target is undocumented or is not a class. # and usage relations if the target is undocumented or is not a class.
# The default value is: YES. # The default value is: YES.
@ -2051,7 +2160,7 @@ HIDE_UNDOC_RELATIONS = YES
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is # Bell Labs. The other options in this section have no effect if this option is
# set to NO # set to NO
# The default value is: NO. # The default value is: YES.
HAVE_DOT = NO HAVE_DOT = NO
@ -2065,7 +2174,7 @@ HAVE_DOT = NO
DOT_NUM_THREADS = 0 DOT_NUM_THREADS = 0
# When you want a differently looking font n the dot files that doxygen # When you want a differently looking font in the dot files that doxygen
# generates you can specify the font name using DOT_FONTNAME. You need to make # generates you can specify the font name using DOT_FONTNAME. You need to make
# sure dot is able to find the font, which can be done by putting it in a # sure dot is able to find the font, which can be done by putting it in a
# standard location or by setting the DOTFONTPATH environment variable or by # standard location or by setting the DOTFONTPATH environment variable or by
@ -2113,7 +2222,7 @@ COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES GROUP_GRAPHS = YES
# If the UML_LOOK tag is set to YES doxygen will generate inheritance and # If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
# collaboration diagrams in a style similar to the OMG's Unified Modeling # collaboration diagrams in a style similar to the OMG's Unified Modeling
# Language. # Language.
# The default value is: NO. # The default value is: NO.
@ -2165,7 +2274,8 @@ INCLUDED_BY_GRAPH = YES
# #
# Note that enabling this option will significantly increase the time of a run. # Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable call graphs for selected # So in most cases it will be better to enable call graphs for selected
# functions only using the \callgraph command. # functions only using the \callgraph command. Disabling a call graph can be
# accomplished by means of the command \hidecallgraph.
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
@ -2176,7 +2286,8 @@ CALL_GRAPH = NO
# #
# Note that enabling this option will significantly increase the time of a run. # Note that enabling this option will significantly increase the time of a run.
# So in most cases it will be better to enable caller graphs for selected # So in most cases it will be better to enable caller graphs for selected
# functions only using the \callergraph command. # functions only using the \callergraph command. Disabling a caller graph can be
# accomplished by means of the command \hidecallergraph.
# The default value is: NO. # The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
@ -2199,11 +2310,17 @@ GRAPHICAL_HIERARCHY = YES
DIRECTORY_GRAPH = YES DIRECTORY_GRAPH = YES
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. # generated by dot. For an explanation of the image formats see the section
# output formats in the documentation of the dot tool (Graphviz (see:
# http://www.graphviz.org/)).
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this # to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement). # requirement).
# Possible values are: png, jpg, gif and svg. # Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo,
# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
# png:gdiplus:gdiplus.
# The default value is: png. # The default value is: png.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.
@ -2246,6 +2363,24 @@ MSCFILE_DIRS =
DIAFILE_DIRS = DIAFILE_DIRS =
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
# path where java can find the plantuml.jar file. If left blank, it is assumed
# PlantUML is not used or called during a preprocessing step. Doxygen will
# generate a warning when it encounters a \startuml command in this case and
# will not generate output for the diagram.
PLANTUML_JAR_PATH =
# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
# configuration file for plantuml.
PLANTUML_CFG_FILE =
# When using plantuml, the specified paths are searched for files specified by
# the !include statement in a plantuml block.
PLANTUML_INCLUDE_PATH =
# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
# that will be shown in the graph. If the number of nodes in a graph becomes # that will be shown in the graph. If the number of nodes in a graph becomes
# larger than this value, doxygen will truncate the graph, which is visualized # larger than this value, doxygen will truncate the graph, which is visualized
@ -2282,7 +2417,7 @@ MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = NO DOT_TRANSPARENT = NO
# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This # files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support # makes dot run faster, but since only newer versions of dot (>1.8.10) support
# this, this feature is disabled by default. # this, this feature is disabled by default.
@ -2299,7 +2434,7 @@ DOT_MULTI_TARGETS = YES
GENERATE_LEGEND = YES GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot # If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
# files that are used to generate the various graphs. # files that are used to generate the various graphs.
# The default value is: YES. # The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES. # This tag requires that the tag HAVE_DOT is set to YES.

View File

@ -31,3 +31,66 @@ Contributors
- Daniel Kraus (@bovender), main developer - Daniel Kraus (@bovender), main developer
- Ulrich Strauss (@c0nnex), namespaces - Ulrich Strauss (@c0nnex), namespaces
- Brent Laabs (@labster), code review and bug fixes - Brent Laabs (@labster), code review and bug fixes
Testing
-------
Starting from version 4.2.0, LinkTitles finally comes with phpunit tests.
Here's how I set up the testing environment. This may not be the canonical way
to do it. Basic information on testing MediaWiki can be found [here](https://www.mediawiki.org/wiki/Manual:PHP_unit_testing).
The following assumes that you have an instance of MediaWiki running locally
on your development machine. This assumes that you are running Linux (I personally
use Ubuntu).
1. Pull the MediaWiki repository:
cd ~/Code
git clone --depth 1 https://phabricator.wikimedia.org/source/mediawiki.git
2. Install [composer](https://getcomposer.org) locally and fetch the
dependencies (including development dependencies):
Follow the instructions on the [composer download page](https://getcomposer.org/download),
but instead of running `php composer-setup.php`, run:
php composer-setup.php --install-dir=bin --filename=composer
bin/composer install
3. Install phpunit (it was already installed on my Ubuntu system when I began
testing LinkTitles, so I leave it up to you to figure out how to do it).
4. Copy your `LocalSettings.php` over from your local MediaWiki installation
and remove (or comment out) any lines that reference extensions or skins that
you are not going to install to your test environment. For the purposes of
testing the LinkTitles extension, leave the following line in place:
wfLoadExtensions( array( 'LinkTitles' ));
And ensure the settings file contains the following:
$wgShowDBErrorBacktrace = true;
5. Create a symbolic link to your copy of the LinkTitles repository:
cd ~/Code/mediawiki/extensions
ln -s ~/Code/LinkTitles
6. Make sure your local MediaWiki instance is up to date. Otherwise phpunit may
fail and tell you about database problems.
This is because the local database is used as a template for the unit tests.
For example, I initially had MW 1.26 installed on my laptop, but the cloned
repository was MW 1.29.1. It's probably also possible to clone the repository
with a specific version tag which matches your local installation.
7. Run the tests:
cd ~/Code/mediawiki/tests/phpunit
php phpunit.php --group bovender
This will run all tests from the 'bovender' group, i.e. tests for my extensions.
If you linked just the LinkTitles extension in step 5, only this extension
will be tested.

View File

@ -33,8 +33,14 @@
] ]
}, },
"AutoloadClasses": { "AutoloadClasses": {
"LinkTitles\\Extension": "includes/LinkTitles_Extension.php", "LinkTitles\\Extension": "includes/Extension.php",
"LinkTitles\\Special": "includes/LinkTitles_Special.php" "LinkTitles\\Linker": "includes/Linker.php",
"LinkTitles\\Target": "includes/Target.php",
"LinkTitles\\Targets": "includes/Targets.php",
"LinkTitles\\Splitter": "includes/Splitter.php",
"LinkTitles\\Config": "includes/Config.php",
"LinkTitles\\Special": "includes/Special.php",
"LinkTitles\\TestCase": "tests/phpunit/TestCase.php"
}, },
"SpecialPages": { "SpecialPages": {
"LinkTitles": "LinkTitles\\Special" "LinkTitles": "LinkTitles\\Special"
@ -61,9 +67,8 @@
"LinkTitles\\Extension::onParserFirstCallInit" "LinkTitles\\Extension::onParserFirstCallInit"
] ]
}, },
"callback": "LinkTitles\\Extension::setup",
"ExtensionMessagesFiles": { "ExtensionMessagesFiles": {
"LinkTitlesMagic": "includes/LinkTitles_Magic.php" "LinkTitlesMagic": "includes/Magic.php"
}, },
"MessagesDirs": { "MessagesDirs": {
"LinkTitles": [ "LinkTitles": [

196
includes/Config.php Normal file
View File

@ -0,0 +1,196 @@
<?php
/**
* The LinkTitles\Config class holds configuration for the LinkTitles extension.
*
* Copyright 2012-2017 Daniel Kraus <bovender@bovender.de> ('bovender')
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* @author Daniel Kraus <bovender@bovender.de>
*/
namespace LinkTitles;
/**
* Holds LinkTitles configuration.
*
* This class encapsulates the global configuration variables so we do not have
* to pull those globals into scope in the individual LinkTitles classes.
*
* Using a dedicated configuration class also facilitates overriding certain
* options, i.e. in a maintenance script that is invoked with flags from the
* command line.
*
* @since 5.0.0
*/
class Config {
/**
* Whether to add links to a page when the page is edited/saved.
* @var bool $parseOnEdit
*/
public $parseOnEdit;
/**
* Whether to add links to a page when the page is rendered.
* @var bool $parseOnRender
*/
public $parseOnRender;
/**
* Indicates whether to prioritize short over long titles.
* @var bool $preferShortTitles
*/
public $preferShortTitles;
/**
* Minimum length of a page title for it to qualify as a potential link target.
* @var int $minimumTitleLength
*/
public $minimumTitleLength;
/**
* Array of page titles that must never be link targets.
*
* This may be useful to exclude common abbreviations or acronyms from
* automatic linking.
* @var Array $blackList
*/
public $blackList;
/**
* Array of those name spaces (integer constants) whose pages may be linked.
* @var Array $nameSpaces
*/
public $nameSpaces;
/**
* Indicates whether to add a link to the first occurrence of a page title
* only (true), or add links to all occurrences on the source page (false).
* @var bool $firstOnly;
*/
public $firstOnly;
/**
* Indicates whether to operate in smart mode, i.e. link to pages even if the
* case does not match. Without smart mode, pages are linked to only if the
* exact title appears on the source page.
* @var bool $smartMode;
*/
public $smartMode;
/**
* Mirrors the global MediaWiki variable $wgCapitalLinks that indicates
* whether or not page titles are fully case sensitive
* @var bool $capitalLinks;
*/
public $capitalLinks;
/**
* Whether or not to link to pages only if the page title appears at the
* start of a word on the target page (i.e., link 'MediaWiki' to a page
* 'Media', but not to a page 'Wiki').
*
* Set both $wordStartOnly and $wordEndOnly to true to enforce matching
* whole titles.
*
* @var bool $wordStartOnly;
*/
public $wordStartOnly;
/**
* Whether or not to link to pages only if the page title appears at the
* end of a word on the target page (i.e., link 'MediaWiki' to a page
* 'Wiki', but not to a page 'Media').
*
* Set both $wordStartOnly and $wordEndOnly to true to enforce matching
* whole titles.
*
* @var bool $wordEndOnly;
*/
public $wordEndOnly;
/**
* Whether or not to skip templates. If set to true, text inside transclusions
* will not be linked.
* @var bool $skipTemplates
*/
public $skipTemplates;
/**
* Whether or not to parse headings.
* @var bool $parseHeadings
*/
public $parseHeadings;
/**
* Whether to check if a potential target page links back to the source page.
* Set this to true to avoid indirect linkbacks.
*
* @var bool $checkRedirect
*/
public $checkRedirect;
/**
* Whether to enable the __NOAUTOLINKTARGET__ magic word which prevents
* a potential target page from being linked to.
*
* @var bool $enableNoTargetMagicWord
*/
public $enableNoTargetMagicWord;
public $enableConsoleOutput;
public $enableDebugConsoleOutput;
/**
* Constructs a new Config object.
*
* The object's member variables will automatically be set with the values
* from the corresponding global variables.
*/
public function __construct() {
global $wgLinkTitlesParseOnEdit;
global $wgLinkTitlesParseOnRender;
global $wgLinkTitlesPreferShortTitles;
global $wgLinkTitlesMinimumTitleLength;
global $wgLinkTitlesBlackList;
global $wgLinkTitlesNamespaces;
global $wgLinkTitlesFirstOnly;
global $wgLinkTitlesSmartMode;
global $wgCapitalLinks;
global $wgLinkTitlesWordStartOnly;
global $wgLinkTitlesWordEndOnly;
global $wgLinkTitlesSkipTemplates;
global $wgLinkTitlesParseHeadings;
global $wgLinkTitlesEnableNoTargetMagicWord;
global $wgLinkTitlesCheckRedirect;
$this->parseOnEdit = $wgLinkTitlesParseOnEdit;
$this->parseOnRender = $wgLinkTitlesParseOnRender;
$this->preferShortTitles = $wgLinkTitlesPreferShortTitles;
$this->minimumTitleLength = $wgLinkTitlesMinimumTitleLength;
$this->blackList = $wgLinkTitlesBlackList;
$this->nameSpaces = $wgLinkTitlesNamespaces;
$this->firstOnly = $wgLinkTitlesFirstOnly;
$this->smartMode = $wgLinkTitlesSmartMode;
$this->capitalLinks = $wgCapitalLinks; // MediaWiki global variable
$this->wordStartOnly = $wgLinkTitlesWordStartOnly;
$this->wordEndOnly = $wgLinkTitlesWordEndOnly;
$this->skipTemplates = $wgLinkTitlesSkipTemplates;
$this->parseHeadings = $wgLinkTitlesParseHeadings;
$this->enableNoTargetMagicWord = $wgLinkTitlesEnableNoTargetMagicWord;;
$this->checkRedirect = $wgLinkTitlesCheckRedirect;;
$this->enableConsoleOutput = false;
$this->enableDebugConsoleOutput = false;
}
}

147
includes/Extension.php Normal file
View File

@ -0,0 +1,147 @@
<?php
/**
* The LinkTitles\Extension class provides event handlers and entry points for the extension.
*
* Copyright 2012-2017 Daniel Kraus <bovender@bovender.de> ('bovender')
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* @author Daniel Kraus <bovender@bovender.de>
*/
namespace LinkTitles;
/**
* Provides event handlers and entry points for the extension.
*/
class Extension {
/// Event handler that is hooked to the PageContentSave event.
public static function onPageContentSave( &$wikiPage, &$user, &$content, &$summary,
$isMinor, $isWatch, $section, &$flags, &$status ) {
global $wgLinkTitlesParseOnEdit;
global $wgLinkTitlesNamespaces;
if ( !$wgLinkTitlesParseOnEdit ) return true; // TODO: refactor with following if
if ( !$isMinor ) {
$title = $wikiPage->getTitle();
// Only process if page is in one of our namespaces we want to link
// Fixes ugly autolinking of sidebar pages
if ( in_array( $title->getNamespace(), $wgLinkTitlesNamespaces )) {
$text = $content->getContentHandler()->serializeContent( $content );
if ( !\MagicWord::get( 'MAG_LINKTITLES_NOAUTOLINKS' )->match( $text ) ) {
$config = new Config();
$linker = new Linker( $config );
$newText = $linker->linkContent( $title, $text );
if ( $newText != $text ) {
$content = $content->getContentHandler()->unserializeContent( $newText );
}
}
}
};
return true;
}
/*
* Event handler that is hooked to the InternalParseBeforeLinks event.
* @param Parser $parser Parser that raised the event.
* @param $text Preprocessed text of the page
*/
public static function onInternalParseBeforeLinks( \Parser &$parser, &$text ) {
$config = new Config();
if (!$config->parseOnRender) return true;
$title = $parser->getTitle();
// If the page contains the magic word '__NOAUTOLINKS__', do not parse it.
// Only process if page is in one of our namespaces we want to link
if ( !\MagicWord::get( 'MAG_LINKTITLES_NOAUTOLINKS' )->match( $text ) &&
in_array( $title->getNamespace(), $config->nameSpaces ) ) {
$linker = new Linker( $config );
$text = $linker->linkContent( $title, $text );
}
return true;
}
/*
* Automatically processes a single page, given a $title Title object.
* This function is called by the SpecialLinkTitles class and the
* LinkTitlesJob class.
* @param Title $title Title object.
* @param RequestContext $context Current request context. If in doubt, call MediaWiki's `RequestContext::getMain()` to obtain such an object.
* @returns bool True if the page exists, false if the page does not exist
*/
public static function processPage( \Title $title, \RequestContext $context ) {
$page = \WikiPage::factory($title);
$content = $page->getContent();
if ( $content != null ) {
$text = $content->getContentHandler()->serializeContent($content);
$config = new Config();
$linker = new Linker( $config );
$newText = $linker->linkContent($title, $text);
if ( $text != $newText ) {
$content = $content->getContentHandler()->unserializeContent( $newText );
$page->doEditContent(
$content,
"Links to existing pages added by LinkTitles bot.", // TODO: i18n
EDIT_MINOR | EDIT_FORCE_BOT,
false, // baseRevId
$context->getUser()
);
};
return true;
}
else {
return false;
}
}
/// Adds the two magic words defined by this extension to the list of
/// 'double-underscore' terms that are automatically removed before a
/// page is displayed.
/// @param $doubleUnderscoreIDs Array of magic word IDs.
/// @return true
public static function onGetDoubleUnderscoreIDs( array &$doubleUnderscoreIDs ) {
$doubleUnderscoreIDs[] = 'MAG_LINKTITLES_NOTARGET';
$doubleUnderscoreIDs[] = 'MAG_LINKTITLES_NOAUTOLINKS';
return true;
}
public static function onParserFirstCallInit( \Parser $parser ) {
$parser->setHook( 'noautolinks', 'LinkTitles\Extension::doNoautolinksTag' );
$parser->setHook( 'autolinks', 'LinkTitles\Extension::doAutolinksTag' );
}
/// Removes the extra tag that this extension provides (<noautolinks>)
/// by simply returning the text between the tags (if any).
/// See https://www.mediawiki.org/wiki/Manual:Tag_extensions#Example
public static function doNoautolinksTag( $input, array $args, \Parser $parser, \PPFrame $frame ) {
return htmlspecialchars( $input );
}
/// Removes the extra tag that this extension provides (<noautolinks>)
/// by simply returning the text between the tags (if any).
/// See https://www.mediawiki.org/wiki/Manual:Tag_extensions#How_do_I_render_wikitext_in_my_extension.3F
public static function doAutolinksTag( $input, array $args, \Parser $parser, \PPFrame $frame ) {
$config = new Config();
$linker = new Linker( $config );
$title = $parser->getTitle();
$withLinks = $linker->linkContent( $title, $input );
$output = $parser->recursiveTagParse( $withLinks, $frame );
return $output;
}
}
// vim: ts=2:sw=2:noet:comments^=\:///

View File

@ -1,527 +0,0 @@
<?php
/*
* Copyright 2012-2017 Daniel Kraus <bovender@bovender.de> ('bovender')
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
/// @file
namespace LinkTitles;
/// Helper function for development and debugging.
/// @param $var Any variable. Raw content will be dumped to stderr.
/// @return undefined
function dump($var) {
error_log(print_r($var, TRUE) . "\n", 3, 'php://stderr');
};
/// Central class of the extension. Sets up parser hooks.
/// This class contains only static functions; do not instantiate.
class Extension {
/// Caching variable for page titles that are fetched from the DB.
private static $pageTitles;
/// Caching variable for the current namespace.
/// This is needed because the sort order of the page titles that
/// are cached in self::$pageTitles depends on the namespace of
/// the page currently being processed.
private static $currentNamespace;
/// A Title object for the page that is being parsed.
private static $currentTitle;
/// A Title object for the target page currently being examined.
private static $targetTitle;
// The TitleValue object of the target page
private static $targetTitleValue;
/// The content object for the currently processed target page.
/// This variable is necessary to be able to prevent loading the target
/// content twice.
private static $targetContent;
/// Holds the page title of the currently processed target page
/// as a string.
private static $targetTitleText;
/// Delimiter used in a regexp split operation to seperate those parts
/// of the page that should be parsed from those that should not be
/// parsed (e.g. inside pre-existing links etc.).
private static $delimiter;
private static $wordStartDelim;
private static $wordEndDelim;
public static $ltConsoleOutput;
public static $ltConsoleOutputDebug;
/// Setup method
public static function setup() {
self::BuildDelimiters();
}
/// Event handler that is hooked to the PageContentSave event.
public static function onPageContentSave( &$wikiPage, &$user, &$content, &$summary,
$isMinor, $isWatch, $section, &$flags, &$status ) {
global $wgLinkTitlesParseOnEdit;
global $wgLinkTitlesNamespaces;
if ( !$wgLinkTitlesParseOnEdit ) return true;
if ( !$isMinor ) {
$title = $wikiPage->getTitle();
// Only process if page is in one of our namespaces we want to link
// Fixes ugly autolinking of sidebar pages
if ( in_array( $title->getNamespace(), $wgLinkTitlesNamespaces )) {
$text = $content->getContentHandler()->serializeContent( $content );
if ( !\MagicWord::get( 'MAG_LINKTITLES_NOAUTOLINKS' )->match( $text ) ) {
$newText = self::parseContent( $title, $text );
if ( $newText != $text ) {
$content = $content->getContentHandler()->unserializeContent( $newText );
}
}
}
};
return true;
}
/// Event handler that is hooked to the InternalParseBeforeLinks event.
/// @param Parser $parser Parser that raised the event.
/// @param $text Preprocessed text of the page.
public static function onInternalParseBeforeLinks( \Parser &$parser, &$text ) {
global $wgLinkTitlesParseOnRender;
if (!$wgLinkTitlesParseOnRender) return true;
global $wgLinkTitlesNamespaces;
$title = $parser->getTitle();
// If the page contains the magic word '__NOAUTOLINKS__', do not parse it.
// Only process if page is in one of our namespaces we want to link
if ( !\MagicWord::get( 'MAG_LINKTITLES_NOAUTOLINKS' )->match( $text ) &&
in_array( $title->getNamespace(), $wgLinkTitlesNamespaces ) ) {
$text = self::parseContent( $title, $text );
}
return true;
}
/// Core function of the extension, performs the actual parsing of the content.
/// @param Parser $parser Parser instance for the current page
/// @param $text String that holds the article content
/// @returns string: parsed text with links added if needed
private static function parseContent( $title, &$text ) {
// Configuration variables need to be defined here as globals.
global $wgLinkTitlesFirstOnly;
global $wgLinkTitlesSmartMode;
global $wgCapitalLinks;
( $wgLinkTitlesFirstOnly ) ? $limit = 1 : $limit = -1;
$limitReached = false;
self::$currentTitle = $title;
$currentNamespace = $title->getNamespace();
$newText = $text;
if ( !isset( self::$pageTitles ) || ( $currentNamespace != self::$currentNamespace ) ) {
self::$currentNamespace = $currentNamespace;
self::$pageTitles = self::fetchPageTitles( $currentNamespace );
}
// Iterate through the page titles
foreach( self::$pageTitles as $row ) {
self::newTarget( $row->page_namespace, $row->page_title );
// Don't link current page
if ( self::$targetTitle->equals( self::$currentTitle ) ) { continue; }
// split the page content by [[...]] groups
// credits to inhan @ StackOverflow for suggesting preg_split
// see http://stackoverflow.com/questions/10672286
$arr = preg_split( self::$delimiter, $newText, -1, PREG_SPLIT_DELIM_CAPTURE );
// Escape certain special characters in the page title to prevent
// regexp compilation errors
self::$targetTitleText = self::$targetTitle->getText();
$quotedTitle = preg_quote( self::$targetTitleText, '/' );
self::ltDebugLog( 'TargetTitle='. self::$targetTitleText, 'private' );
self::ltDebugLog( 'TargetTitleQuoted='. $quotedTitle, 'private' );
// Depending on the global configuration setting $wgCapitalLinks,
// the title has to be searched for either in a strictly case-sensitive
// way, or in a 'fuzzy' way where the first letter of the title may
// be either case.
if ( $wgCapitalLinks && ( $quotedTitle[0] != '\\' )) {
$searchTerm = '((?i)' . $quotedTitle[0] . '(?-i)' .
substr($quotedTitle, 1) . ')';
} else {
$searchTerm = '(' . $quotedTitle . ')';
}
$regex = '/(?<![\:\.\@\/\?\&])' . self::$wordStartDelim .
$searchTerm . self::$wordEndDelim . '/S';
for ( $i = 0; $i < count( $arr ); $i+=2 ) {
// even indexes will point to text that is not enclosed by brackets
$arr[$i] = preg_replace_callback( $regex,
'LinkTitles\Extension::simpleModeCallback', $arr[$i], $limit, $count );
if ( $wgLinkTitlesFirstOnly && ( $count > 0 ) ) {
$limitReached = true;
break;
};
};
$newText = implode( '', $arr );
// If smart mode is turned on, the extension will perform a second
// pass on the page and add links with aliases where the case does
// not match.
if ( $wgLinkTitlesSmartMode && !$limitReached ) {
$arr = preg_split( self::$delimiter, $newText, -1, PREG_SPLIT_DELIM_CAPTURE );
for ( $i = 0; $i < count( $arr ); $i+=2 ) {
// even indexes will point to text that is not enclosed by brackets
$arr[$i] = preg_replace_callback( '/(?<![\:\.\@\/\?\&])' .
self::$wordStartDelim . '(' . $quotedTitle . ')' .
self::$wordEndDelim . '/iS', 'LinkTitles\Extension::smartModeCallback',
$arr[$i], $limit, $count );
if ( $wgLinkTitlesFirstOnly && ( $count > 0 )) {
break;
};
};
$newText = implode( '', $arr );
} // $wgLinkTitlesSmartMode
}; // foreach $res as $row
return $newText;
}
/// Automatically processes a single page, given a $title Title object.
/// This function is called by the SpecialLinkTitles class and the
/// LinkTitlesJob class.
/// @param Title $title Title object.
/// @param RequestContext $context Current request context.
/// If in doubt, call MediaWiki's `RequestContext::getMain()`
/// to obtain such an object.
/// @returns boolean True if the page exists, false if the page does not exist
public static function processPage( \Title $title, \RequestContext $context ) {
self::ltLog('Processing '. $title->getPrefixedText());
$page = \WikiPage::factory($title);
$content = $page->getContent();
if ( $content != null ) {
$text = $content->getContentHandler()->serializeContent($content);
$newText = self::parseContent($title, $text);
if ( $text != $newText ) {
$content = $content->getContentHandler()->unserializeContent( $newText );
$page->doEditContent(
$content,
"Links to existing pages added by LinkTitles bot.", // TODO: i18n
EDIT_MINOR | EDIT_FORCE_BOT,
false, // baseRevId
$context->getUser()
);
};
return true;
}
else {
return false;
}
}
/// Adds the two magic words defined by this extension to the list of
/// 'double-underscore' terms that are automatically removed before a
/// page is displayed.
/// @param $doubleUnderscoreIDs Array of magic word IDs.
/// @return true
public static function onGetDoubleUnderscoreIDs( array &$doubleUnderscoreIDs ) {
$doubleUnderscoreIDs[] = 'MAG_LINKTITLES_NOTARGET';
$doubleUnderscoreIDs[] = 'MAG_LINKTITLES_NOAUTOLINKS';
return true;
}
public static function onParserFirstCallInit( \Parser $parser ) {
$parser->setHook( 'noautolinks', 'LinkTitles\Extension::doNoautolinksTag' );
$parser->setHook( 'autolinks', 'LinkTitles\Extension::doAutolinksTag' );
}
/// Removes the extra tag that this extension provides (<noautolinks>)
/// by simply returning the text between the tags (if any).
/// See https://www.mediawiki.org/wiki/Manual:Tag_extensions#Example
public static function doNoautolinksTag( $input, array $args, \Parser $parser, \PPFrame $frame ) {
return htmlspecialchars( $input );
}
/// Removes the extra tag that this extension provides (<noautolinks>)
/// by simply returning the text between the tags (if any).
/// See https://www.mediawiki.org/wiki/Manual:Tag_extensions#How_do_I_render_wikitext_in_my_extension.3F
public static function doAutolinksTag( $input, array $args, \Parser $parser, \PPFrame $frame ) {
$withLinks = self::parseContent( $parser->getTitle(), $input );
$output = $parser->recursiveTagParse( $withLinks, $frame );
return $output;
}
// Fetches the page titles from the database.
// @param $currentNamespace String holding the namespace of the page currently being processed.
private static function fetchPageTitles( $currentNamespace ) {
global $wgLinkTitlesPreferShortTitles;
global $wgLinkTitlesMinimumTitleLength;
global $wgLinkTitlesBlackList;
global $wgLinkTitlesNamespaces;
( $wgLinkTitlesPreferShortTitles ) ? $sort_order = 'ASC' : $sort_order = 'DESC';
// Build a blacklist of pages that are not supposed to be link
// targets. This includes the current page.
$blackList = str_replace( ' ', '_', '("' . implode( '","',$wgLinkTitlesBlackList ) . '")' );
// Build our weight list. Make sure current namespace is first element
$namespaces = array_diff( $wgLinkTitlesNamespaces, [ $currentNamespace ] );
array_unshift( $namespaces, $currentNamespace );
// No need for sanitiy check. we are sure that we have at least one element in the array
$weightSelect = "CASE page_namespace ";
$currentWeight = 0;
foreach ($namespaces as &$namspacevalue) {
$currentWeight = $currentWeight + 100;
$weightSelect = $weightSelect . " WHEN " . $namspacevalue . " THEN " . $currentWeight . PHP_EOL;
}
$weightSelect = $weightSelect . " END ";
$namespacesClause = '(' . implode( ', ', $namespaces ) . ')';
// Build an SQL query and fetch all page titles ordered by length from
// shortest to longest. Only titles from 'normal' pages (namespace uid
// = 0) are returned. Since the db may be sqlite, we need a try..catch
// structure because sqlite does not support the CHAR_LENGTH function.
$dbr = wfGetDB( DB_SLAVE );
try {
$res = $dbr->select(
'page',
array( 'page_title', 'page_namespace' , "weight" => $weightSelect),
array(
'page_namespace IN ' . $namespacesClause,
'CHAR_LENGTH(page_title) >= ' . $wgLinkTitlesMinimumTitleLength,
'page_title NOT IN ' . $blackList,
),
__METHOD__,
array( 'ORDER BY' => 'weight ASC, CHAR_LENGTH(page_title) ' . $sort_order )
);
} catch (Exception $e) {
$res = $dbr->select(
'page',
array( 'page_title', 'page_namespace' , "weight" => $weightSelect ),
array(
'page_namespace IN ' . $namespacesClause,
'LENGTH(page_title) >= ' . $wgLinkTitlesMinimumTitleLength,
'page_title NOT IN ' . $blackList,
),
__METHOD__,
array( 'ORDER BY' => 'weight ASC, LENGTH(page_title) ' . $sort_order )
);
}
return $res;
}
// Build an anonymous callback function to be used in simple mode.
private static function simpleModeCallback( array $matches ) {
if ( self::checkTargetPage() ) {
self::ltLog( "Linking '$matches[0]' to '" . self::$targetTitle . "'" );
return '[[' . $matches[0] . ']]';
}
else
{
return $matches[0];
}
}
// Callback function for use with preg_replace_callback.
// This essentially performs a case-sensitive comparison of the
// current page title and the occurrence found on the page; if
// the cases do not match, it builds an aliased (piped) link.
// If $wgCapitalLinks is set to true, the case of the first
// letter is ignored by MediaWiki and we don't need to build a
// piped link if only the case of the first letter is different.
private static function smartModeCallback( array $matches ) {
global $wgCapitalLinks;
if ( $wgCapitalLinks ) {
// With $wgCapitalLinks set to true we have a slightly more
// complicated version of the callback than if it were false;
// we need to ignore the first letter of the page titles, as
// it does not matter for linking.
if ( self::checkTargetPage() ) {
self::ltLog( "Linking (smart) '$matches[0]' to '" . self::$targetTitle . "'" );
if ( strcmp(substr(self::$targetTitleText, 1), substr($matches[0], 1)) == 0 ) {
// Case-sensitive match: no need to bulid piped link.
return '[[' . $matches[0] . ']]';
} else {
// Case-insensitive match: build piped link.
return '[[' . self::$targetTitleText . '|' . $matches[0] . ']]';
}
}
else
{
return $matches[0];
}
} else {
// If $wgCapitalLinks is false, we can use the simple variant
// of the callback function.
if ( self::checkTargetPage() ) {
self::ltLog( "Linking (smart) '$matches[0]' to '" . self::$targetTitle . "'" );
if ( strcmp(self::$targetTitleText, $matches[0]) == 0 ) {
// Case-sensitive match: no need to bulid piped link.
return '[[' . $matches[0] . ']]';
} else {
// Case-insensitive match: build piped link.
return '[[' . self::$targetTitleText . '|' . $matches[0] . ']]';
}
}
else
{
return $matches[0];
}
}
}
/// Sets member variables for the current target page.
private static function newTarget( $ns, $title ) {
self::$targetTitle = \Title::makeTitleSafe( $ns, $title );
self::ltDebugLog( 'newtarget='. self::$targetTitle->getText(), "private" );
self::$targetTitleValue = self::$targetTitle->getTitleValue();
self::ltDebugLog( 'altTarget='. self::$targetTitleValue->getText(), "private" );
self::$targetContent = null;
}
/// Returns the content of the current target page.
/// This function serves to be used in preg_replace_callback callback
/// functions, in order to load the target page content from the
/// database only when needed.
/// @note It is absolutely necessary that the newTarget()
/// function is called for every new page.
private static function getTargetContent() {
if ( ! isset( $targetContent ) ) {
self::$targetContent = \WikiPage::factory(
self::$targetTitle)->getContent();
};
return self::$targetContent;
}
/// Examines the current target page. Returns true if it may be linked;
/// false if not. This depends on the settings
/// $wgLinkTitlesCheckRedirect and $wgLinkTitlesEnableNoTargetMagicWord
/// and whether the target page is a redirect or contains the
/// __NOAUTOLINKTARGET__ magic word.
/// @returns boolean
private static function checkTargetPage() {
global $wgLinkTitlesEnableNoTargetMagicWord;
global $wgLinkTitlesCheckRedirect;
// If checking for redirects is enabled and the target page does
// indeed redirect to the current page, return the page title as-is
// (unlinked).
if ( $wgLinkTitlesCheckRedirect ) {
$redirectTitle = self::getTargetContent()->getUltimateRedirectTarget();
if ( $redirectTitle && $redirectTitle->equals(self::$currentTitle) ) {
return false;
}
};
// If the magic word __NOAUTOLINKTARGET__ is enabled and the target
// page does indeed contain this magic word, return the page title
// as-is (unlinked).
if ( $wgLinkTitlesEnableNoTargetMagicWord ) {
if ( self::getTargetContent()->matchMagicWord(
\MagicWord::get('MAG_LINKTITLES_NOTARGET') ) ) {
return false;
}
};
return true;
}
/// Builds the delimiter that is used in a regexp to separate
/// text that should be parsed from text that should not be
/// parsed (e.g. inside existing links etc.)
private static function BuildDelimiters() {
// Configuration variables need to be defined here as globals.
global $wgLinkTitlesParseHeadings;
global $wgLinkTitlesSkipTemplates;
global $wgLinkTitlesWordStartOnly;
global $wgLinkTitlesWordEndOnly;
// Use unicode character properties rather than \b escape sequences
// to detect whole words containing non-ASCII characters as well.
// Note that this requires a PCRE library that was compiled with
// --enable-unicode-properties
( $wgLinkTitlesWordStartOnly ) ? self::$wordStartDelim = '(?<!\pL)' : self::$wordStartDelim = '';
( $wgLinkTitlesWordEndOnly ) ? self::$wordEndDelim = '(?!\pL)' : self::$wordEndDelim = '';
if ( $wgLinkTitlesSkipTemplates )
{
// Use recursive regex to balance curly braces;
// see http://www.regular-expressions.info/recurse.html
$templatesDelimiter = '{{(?>[^{}]|(?R))*}}|';
} else {
// Match template names (ignoring any piped [[]] links in them)
// along with the trailing pipe and parameter name or closing
// braces; also match sequences of '|wordcharacters=' (without
// spaces in them) that usually only occur as parameter names in
// transclusions (but could also occur as wiki table cell contents).
// TODO: Find a way to match parameter names in transclusions, but
// not in table cells or other sequences involving a pipe character
// and equal sign.
$templatesDelimiter = '{{[^|]*?(?:(?:\[\[[^]]+]])?)[^|]*?(?:\|(?:\w+=)?|(?:}}))|\|\w+=|';
}
// Build a regular expression that will capture existing wiki links ("[[...]]"),
// wiki headings ("= ... =", "== ... ==" etc.),
// urls ("http://example.com", "[http://example.com]", "[http://example.com Description]",
// and email addresses ("mail@example.com").
// Since there is a user option to skip headings, we make this part of the expression
// optional. Note that in order to use preg_split(), it is important to have only one
// capturing subpattern (which precludes the use of conditional subpatterns).
( $wgLinkTitlesParseHeadings ) ? $delimiter = '' : $delimiter = '=+.+?=+|';
$urlPattern = '[a-z]+?\:\/\/(?:\S+\.)+\S+(?:\/.*)?';
self::$delimiter = '/(' . // exclude from linking:
'\[\[.*?\]\]|' . // links
$delimiter . // titles (if requested)
$templatesDelimiter . // templates (if requested)
'^ .+?\n|\n .+?\n|\n .+?$|^ .+?$|' . // preformatted text
'<nowiki>.*?<.nowiki>|<code>.*?<\/code>|' . // nowiki/code
'<pre>.*?<\/pre>|<html>.*?<\/html>|' . // pre/html
'<script>.*?<\/script>|' . // script
'<gallery>.*?<\/gallery>|' . // gallery
'<div.+?>|<\/div>|' . // attributes of div elements
'<span.+?>|<\/span>|' . // attributes of span elements
'<file>[^<]*<\/file>|' . // stuff inside file elements
'style=".+?"|class=".+?"|' . // styles and classes (e.g. of wikitables)
'<noautolinks>.*?<\/noautolinks>|' . // custom tag 'noautolinks'
'\[' . $urlPattern . '\s.+?\]|'. $urlPattern . '(?=\s|$)|' . // urls
'(?<=\b)\S+\@(?:\S+\.)+\S+(?=\b)' . // email addresses
')/ismS';
}
/// Local Debugging output function which can send output to console as well
public static function ltDebugLog($text) {
if ( self::$ltConsoleOutputDebug ) {
print $text . "\n";
}
wfDebugLog( 'LinkTitles', $text , 'private' );
}
/// Local Logging output function which can send output to console as well
public static function ltLog($text) {
if (self::$ltConsoleOutput) {
print $text . "\n";
}
wfDebugLog( 'LinkTitles', $text , 'private' );
}
}
// vim: ts=2:sw=2:noet:comments^=\:///

184
includes/Linker.php Normal file
View File

@ -0,0 +1,184 @@
<?php
/**
* The LinkTitles\Linker class does the heavy linking for the extension.
*
* Copyright 2012-2017 Daniel Kraus <bovender@bovender.de> ('bovender')
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* @author Daniel Kraus <bovender@bovender.de>
*/
namespace LinkTitles;
/**
* Performs the actual linking of content to existing pages.
*/
class Linker {
/**
* LinkTitles configuration.
*
* @var Config $config
*/
public $config;
/**
* The string representation of the title object for the potential target page
* that is currently being processed.
*
* This is an instance variable (rather than a local method variable) so it
* can be accessed in the preg_replace_callback callbacks.
*
* @var String $targetTitleString
*/
private $targetTitleText;
/**
* Constructs a new instance of the Linker class.
*
* @param Config $config LinkTitles configuration object.
*/
public function __construct( Config &$config ) {
$this->config = $config;
}
/**
* Core function of the extension, performs the actual parsing of the content.
*
* This method receives a Title object and the string representation of the
* source page. It does not work on a WikiPage object directly because the
* callbacks in the Extension class do not always get a WikiPage object in the
* first place.
*
* @param \Title &$title Title object for the current page.
* @param String $text String that holds the article content
* @return String with links to target pages
*/
public function linkContent( \Title &$title, &$text ) {
( $this->config->firstOnly ) ? $limit = 1 : $limit = -1;
$limitReached = false;
$newText = $text;
$splitter = Splitter::default( $this->config );
$targets = Targets::default( $title, $this->config );
// Iterate through the target page titles
foreach( $targets->queryResult as $row ) {
$target = new Target( $row->page_namespace, $row->page_title, $this->config );
// Don't link current page and don't link if the target page redirects
// to the current page or has the __NOAUTOLINKTARGET__ magic word
// (as required by the actual LinkTitles configuration).
if ( $target->isSameTitle( $title ) || !$target->mayLinkTo( $title ) ) {
continue;
}
// Split the page content by non-linkable sections.
// Credits to inhan @ StackOverflow for suggesting preg_split.
// See http://stackoverflow.com/questions/10672286
$arr = $splitter->split( $newText );
$count = 0;
// Cache the target title text for the regex callbacks
$this->targetTitleText = $target->getTitleText();
// Even indexes will point to sections of the text that may be linked
for ( $i = 0; $i < count( $arr ); $i += 2 ) {
$arr[$i] = preg_replace_callback( $target->getCaseSensitiveRegex(),
array( $this, 'simpleModeCallback'),
$arr[$i], $limit, $count );
if ( $this->config->firstOnly && ( $count > 0 ) ) {
$limitReached = true;
break;
};
};
$newText = implode( '', $arr );
// If smart mode is turned on, the extension will perform a second
// pass on the page and add links with aliases where the case does
// not match.
if ( $this->config->smartMode && !$limitReached ) {
if ( $count > 0 ) {
// Split the text again because it was changed in the first pass.
$arr = $splitter->split( $newText );
}
for ( $i = 0; $i < count( $arr ); $i+=2 ) {
// even indexes will point to text that is not enclosed by brackets
$arr[$i] = preg_replace_callback( $target->getCaseInsensitiveRegex(),
array( $this, 'smartModeCallback'),
$arr[$i], $limit, $count );
if ( $this->config->firstOnly && ( $count > 0 )) {
break;
};
};
$newText = implode( '', $arr );
} // $wgLinkTitlesSmartMode
}; // foreach $res as $row
return $newText;
}
/**
* Callback for preg_replace_callback in simple mode.
*
* @param array $matches Matches provided by preg_replace_callback
* @return string Target page title with or without link markup
*/
private function simpleModeCallback( array $matches ) {
return '[[' . $matches[0] . ']]';
}
/**
* Callback function for use with preg_replace_callback.
* This essentially performs a case-sensitive comparison of the
* current page title and the occurrence found on the page; if
* the cases do not match, it builds an aliased (piped) link.
* If $wgCapitalLinks is set to true, the case of the first
* letter is ignored by MediaWiki and we don't need to build a
* piped link if only the case of the first letter is different.
*
* @param array $matches Matches provided by preg_replace_callback
* @return string Target page title with or without link markup
*/
private function smartModeCallback( array $matches ) {
if ( $this->config->capitalLinks ) {
// With $wgCapitalLinks set to true we have a slightly more
// complicated version of the callback than if it were false;
// we need to ignore the first letter of the page titles, as
// it does not matter for linking.
if ( strcmp( substr( $this->targetTitleText, 1 ), substr( $matches[ 0 ], 1) ) == 0 ) {
// Case-sensitive match: no need to bulid piped link.
return '[[' . $matches[ 0 ] . ']]';
} else {
// Case-insensitive match: build piped link.
return '[[' . $this->targetTitleText . '|' . $matches[ 0 ] . ']]';
}
} else {
// If $wgCapitalLinks is false, we can use the simple variant
// of the callback function.
if ( strcmp( $this->targetTitleText, $matches[ 0 ] ) == 0 ) {
// Case-sensitive match: no need to bulid piped link.
return '[[' . $matches[ 0 ] . ']]';
} else {
// Case-insensitive match: build piped link.
return '[[' . $this->targetTitleText . '|' . $matches[ 0 ] . ']]';
}
}
}
}
// vim: ts=2:sw=2:noet:comments^=\:///

View File

@ -1,5 +1,7 @@
<?php <?php
/* /**
* Provides a special page for the LinkTitles extension.
*
* Copyright 2012-2017 Daniel Kraus <bovender@bovender.de> ('bovender') * Copyright 2012-2017 Daniel Kraus <bovender@bovender.de> ('bovender')
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -16,6 +18,8 @@
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA. * MA 02110-1301, USA.
*
* @author Daniel Kraus <bovender@bovender.de>
*/ */
namespace LinkTitles; namespace LinkTitles;
/// @defgroup batch Batch processing /// @defgroup batch Batch processing
@ -26,13 +30,17 @@ if ( !defined( 'MEDIAWIKI' ) ) {
} }
/// @endcond /// @endcond
/// Provides a special page that can be used to batch-process all pages in /**
/// the wiki. By default, this can only be performed by sysops. * Provides a special page that can be used to batch-process all pages in
/// @ingroup batch * the wiki. By default, this can only be performed by sysops.
* @ingroup batch
*
*/
class Special extends \SpecialPage { class Special extends \SpecialPage {
/// Constructor. Announces the special page title and required user right /**
/// to the parent constructor. * Constructor. Announces the special page title and required user right to the parent constructor.
*/
function __construct() { function __construct() {
// the second parameter in the following function call ensures that only // the second parameter in the following function call ensures that only
// users who have the 'linktitles-batch' right get to see this page (by // users who have the 'linktitles-batch' right get to see this page (by
@ -44,9 +52,11 @@ class Special extends \SpecialPage {
return 'pagetools'; return 'pagetools';
} }
/// Entry function of the special page class. Will abort if the user does
/// not have appropriate permissions ('linktitles-batch'). /**
/// @return undefined * Entry function of the special page class. Will abort if the user does not have appropriate permissions ('linktitles-batch').
* @param $par Additional parameters (required by interface; currently not used)
*/
function execute($par) { function execute($par) {
// Prevent non-authorized users from executing the batch processing. // Prevent non-authorized users from executing the batch processing.
if ( !$this->userCanExecute( $this->getUser() ) ) { if ( !$this->userCanExecute( $this->getUser() ) ) {
@ -76,12 +86,13 @@ class Special extends \SpecialPage {
} }
} }
/// Processes wiki articles, starting at the page indicated by /**
/// $startTitle. If $wgLinkTitlesTimeLimit is reached before all pages are * Processes wiki articles, starting at the page indicated by
/// processed, returns the title of the next page that needs processing. * $startTitle. If $wgLinkTitlesTimeLimit is reached before all pages are
/// @param WebRequest $request WebRequest object that is associated with the special * processed, returns the title of the next page that needs processing.
/// page. * @param WebRequest $request WebRequest object that is associated with the special page.
/// @param OutputPage $output Output page for the special page. * @param OutputPage $output Output page for the special page.
*/
private function process( \WebRequest &$request, \OutputPage &$output) { private function process( \WebRequest &$request, \OutputPage &$output) {
global $wgLinkTitlesTimeLimit; global $wgLinkTitlesTimeLimit;
global $wgLinkTitlesNamespaces; global $wgLinkTitlesNamespaces;
@ -113,9 +124,7 @@ class Special extends \SpecialPage {
$end = $this->countPages($dbr, $namespacesClause ); $end = $this->countPages($dbr, $namespacesClause );
}; };
array_key_exists('r', $postValues) ? array_key_exists('r', $postValues) ? $reloads = $postValues['r'] : $reloads = 0;
$reloads = $postValues['r'] :
$reloads = 0;
// Retrieve page names from the database. // Retrieve page names from the database.
$res = $dbr->select( $res = $dbr->select(
@ -162,8 +171,10 @@ class Special extends \SpecialPage {
} }
} }
/// Adds WikiText to the output containing information about the extension /*
/// and a form and button to start linking. * Adds WikiText to the output containing information about the extension
* and a form and button to start linking.
*/
private function buildInfoPage( &$request, &$output ) { private function buildInfoPage( &$request, &$output ) {
$url = $request->getRequestURL(); $url = $request->getRequestURL();
@ -192,12 +203,13 @@ EOF
); );
} }
/// Produces informative output in WikiText format to show while working. /*
/// @param $output Output object. * Produces informative output in WikiText format to show while working.
/// @param $curTitle Title of the currently processed page. * @param $output Output object.
/// @param $index Index of the currently processed page. * @param $curTitle Title of the currently processed page.
/// @param $end Last index that will be processed (i.e., number of * @param $index Index of the currently processed page.
/// pages). * @param $end Last index that will be processed (i.e., number of pages).
*/
private function addProgressInfo( &$output, $curTitle, $index, $end ) { private function addProgressInfo( &$output, $curTitle, $index, $end ) {
$progress = $index / $end * 100; $progress = $index / $end * 100;
$percent = sprintf("%01.1f", $progress); $percent = sprintf("%01.1f", $progress);
@ -232,14 +244,15 @@ EOF
); );
} }
/// Generates an HTML form and JavaScript to automatically submit the /*
/// form. * Generates an HTML form and JavaScript to automatically submit the
/// @param $url URL to reload with a POST request. * form.
/// @param $start Index of the next page that shall be processed. * @param $url URL to reload with a POST request.
/// @param $end Index of the last page to be processed. * @param $start Index of the next page that shall be processed.
/// @param $reloads Counter that holds the number of reloads so far. * @param $end Index of the last page to be processed.
/// @returns String that holds the HTML for a form and a * @param $reloads Counter that holds the number of reloads so far.
/// JavaScript command. * @returns String that holds the HTML for a form and a JavaScript command.
*/
private function getReloaderForm( $url, $start, $end, $reloads ) { private function getReloaderForm( $url, $start, $end, $reloads ) {
return return
<<<EOF <<<EOF
@ -255,12 +268,14 @@ EOF
; ;
} }
/// Adds statistics to the page when all processing is done. /*
/// @param $output Output object * Adds statistics to the page when all processing is done.
/// @param $start Index of the first page that was processed. * @param $output Output object
/// @param $end Index of the last processed page. * @param $start Index of the first page that was processed.
/// @param $reloads Number of reloads of the page. * @param $end Index of the last processed page.
/// @returns undefined * @param $reloads Number of reloads of the page.
* @returns undefined
*/
private function addCompletedInfo( &$output, $start, $end, $reloads ) { private function addCompletedInfo( &$output, $start, $end, $reloads ) {
global $wgLinkTitlesTimeLimit; global $wgLinkTitlesTimeLimit;
$pagesPerReload = sprintf('%0.1f', $end / $reloads); $pagesPerReload = sprintf('%0.1f', $end / $reloads);
@ -281,9 +296,11 @@ EOF
); );
} }
/// Counts the number of pages in a read-access wiki database ($dbr). /*
/// @param $dbr Read-only `Database` object. * Counts the number of pages in a read-access wiki database ($dbr).
/// @returns Number of pages in the default namespace (0) of the wiki. * @param $dbr Read-only `Database` object.
* @returns Number of pages in the default namespace (0) of the wiki.
*/
private function countPages(&$dbr, $namespacesClause) { private function countPages(&$dbr, $namespacesClause) {
$res = $dbr->select( $res = $dbr->select(
'page', 'page',

140
includes/Splitter.php Normal file
View File

@ -0,0 +1,140 @@
<?php
/**
* The Splitter class caches a regular expression that delimits text to be parsed.
*
* Copyright 2012-2017 Daniel Kraus <bovender@bovender.de> ('bovender')
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* @author Daniel Kraus <bovender@bovender.de>
*/
namespace LinkTitles;
/**
* Caches a regular expression that delimits text to be parsed.
*/
class Splitter {
/**
* The splitting expression that separates text to be parsed from text that
* must not be parsed.
* @var String $splitter
*/
public $splitter;
/**
* The LinkTitles configuration for this Splitter instance.
* @var Config $config
*/
public $config;
private static $instance;
/**
* Gets the Splitter singleton; may build one with the given config or the
* default config if none is given.
*
* If the instance was already created, it does not matter what Config this
* method is called with. To re-create an instance with a different Config,
* call Splitter::invalidate() first.
*
* @param Config|null $config LinkTitles configuration.
*/
public static function default( Config &$config = null ) {
if ( self::$instance === null ) {
if ( $config === null ) {
$config = new Config();
}
self::$instance = new Splitter( $config );
}
return self::$instance;
}
/**
* Invalidates the singleton instance.
*
* Used for unit testing.
*/
public static function invalidate() {
self::$instance = null;
}
protected function __construct( Config $config) {
$this->config = $config;
$this->buildSplitter();
}
/**
* Splits a text into sections that may be linked and sections that may not
* be linked (e.g., because they already are a link, or a template, etc.).
*
* @param String &$text Text to split.
* @return Array of strings where even indexes point to linkable sections.
*/
public function split( &$text ) {
return preg_split( $this->splitter, $text, -1, PREG_SPLIT_DELIM_CAPTURE );
}
/*
* Builds the delimiter that is used in a regexp to separate
* text that should be parsed from text that should not be
* parsed (e.g. inside existing links etc.)
*/
private function buildSplitter() {
if ( $this->config->skipTemplates )
{
// Use recursive regex to balance curly braces;
// see http://www.regular-expressions.info/recurse.html
$templatesDelimiter = '{{(?>[^{}]|(?R))*}}|';
} else {
// Match template names (ignoring any piped [[]] links in them)
// along with the trailing pipe and parameter name or closing
// braces; also match sequences of '|wordcharacters=' (without
// spaces in them) that usually only occur as parameter names in
// transclusions (but could also occur as wiki table cell contents).
// TODO: Find a way to match parameter names in transclusions, but
// not in table cells or other sequences involving a pipe character
// and equal sign.
$templatesDelimiter = '{{[^|]*?(?:(?:\[\[[^]]+]])?)[^|]*?(?:\|(?:\w+=)?|(?:}}))|\|\w+=|';
}
// Build a regular expression that will capture existing wiki links ("[[...]]"),
// wiki headings ("= ... =", "== ... ==" etc.),
// urls ("http://example.com", "[http://example.com]", "[http://example.com Description]",
// and email addresses ("mail@example.com").
// Since there is a user option to skip headings, we make this part of the expression
// optional. Note that in order to use preg_split(), it is important to have only one
// capturing subpattern (which precludes the use of conditional subpatterns).
( $this->config->parseHeadings ) ? $delimiter = '' : $delimiter = '=+.+?=+|';
$urlPattern = '[a-z]+?\:\/\/(?:\S+\.)+\S+(?:\/.*)?';
$this->splitter = '/(' . // exclude from linking:
'\[\[.*?\]\]|' . // links
$delimiter . // titles (if requested)
$templatesDelimiter . // templates (if requested)
'^ .+?\n|\n .+?\n|\n .+?$|^ .+?$|' . // preformatted text
'<nowiki>.*?<.nowiki>|<code>.*?<\/code>|' . // nowiki/code
'<pre>.*?<\/pre>|<html>.*?<\/html>|' . // pre/html
'<script>.*?<\/script>|' . // script
'<gallery>.*?<\/gallery>|' . // gallery
'<div.+?>|<\/div>|' . // attributes of div elements
'<span.+?>|<\/span>|' . // attributes of span elements
'<file>[^<]*<\/file>|' . // stuff inside file elements
'style=".+?"|class=".+?"|' . // styles and classes (e.g. of wikitables)
'<noautolinks>.*?<\/noautolinks>|' . // custom tag 'noautolinks'
'\[' . $urlPattern . '\s.+?\]|'. $urlPattern . '(?=\s|$)|' . // urls
'(?<=\b)\S+\@(?:\S+\.)+\S+(?=\b)' . // email addresses
')/ismS';
}
}

194
includes/Target.php Normal file
View File

@ -0,0 +1,194 @@
<?php
/**
* The LinkTitles\Target represents a Wiki page that is a potential link target.
*
* Copyright 2012-2017 Daniel Kraus <bovender@bovender.de> ('bovender')
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* @author Daniel Kraus <bovender@bovender.de>
*/
namespace LinkTitles;
/**
* Represents a page that is a potential link target.
*/
class Target {
/**
* A Title object for the target page currently being examined.
* @var \Title $title
*/
private $title;
/**
* Caches the target page content as a \Content object.
*
* @var \Content $content
*/
private $content;
/**
* Regex that matches the start of a word; this expression depends on the
* setting of LinkTitles\Config->wordStartOnly;
* @var String $wordStart
*/
public $wordStart;
/**
* Regex that matches the end of a word; this expression depends on the
* setting of LinkTitles\Config->wordEndOnly;
* @var String $wordEnd
*/
public $wordEnd;
/**
* LinkTitles configuration.
* @var Config $config
*/
private $config;
/**
* Constructs a new Target object
*
* The parameters may be taken from database rows, for example.
*
* @param Int $nameSpace Name space of the target page
* @param String &$title Title of the target page
*/
public function __construct( $nameSpace, $title, Config &$config ) {
$this->title = \Title::makeTitleSafe( $nameSpace, $title );
$this->titleValue = $this->title->getTitleValue();
$this->config = $config;
// Use unicode character properties rather than \b escape sequences
// to detect whole words containing non-ASCII characters as well.
// Note that this requires a PCRE library that was compiled with
// --enable-unicode-properties
( $config->wordStartOnly ) ? $this->wordStart = '(?<!\pL)' : $this->wordStart = '';
( $config->wordEndOnly ) ? $this->wordEnd = '(?!\pL)' : $this->wordEnd = '';
}
/**
* Gets the string representation of the target title.
* @return String title text
*/
public function getTitleText() {
return $this->title->getText();
}
/**
* Gets the title string with certain characters escaped that may interfere
* with regular expressions.
* @return String representation of the title, regex-safe
*/
public function getRegexSafeTitle() {
return preg_quote( $this->title->getText(), '/' );
}
/**
* Builds a regular expression of the title
* @return String regular expression for this title.
*/
public function getCaseSensitiveRegex() {
$regexSafeTitle = $this->getRegexSafeTitle();
// Depending on the $config->capitalLinks setting,
// the title has to be searched for either in a strictly case-sensitive
// way, or in a 'fuzzy' way where the first letter of the title may
// be either case.
//
if ( $this->config->capitalLinks && ( $regexSafeTitle[0] != '\\' )) {
$searchTerm = '((?i)' . $regexSafeTitle[0] . '(?-i)' . substr($regexSafeTitle, 1) . ')';
} else {
$searchTerm = '(' . $regexSafeTitle . ')';
}
return $this->buildRegex( $searchTerm );
}
/**
* Builds a regular expression pattern for the title in a case-insensitive
* way.
* @return String case-insensitive regular expression pattern for the title
*/
public function getCaseInsensitiveRegex() {
return $this->buildRegex( $this->getRegexSafeTitle() ) . 'i';
}
/**
* Builds the basic regex that is used to match target page titles in a source
* text.
* @param String $searchTerm Target page title (special characters must be quoted)
* @return String regular expression pattern
*/
private function buildRegex( $searchTerm ) {
return '/(?<![\:\.\@\/\?\&])' . $this->wordStart . $searchTerm . $this->wordEnd . '/S';
}
/**
* Returns the \Content of the target page.
*
* The value is cached.
* @return \Content Content of the Target page.
*/
public function getContent() {
if ( $this->content === null ) {
$this->content = \WikiPage::factory( $this->title )->getContent();
};
return $this->content;
}
/**
* Examines the current target page. Returns true if it may be linked;
* false if not. This depends on two settings:
* $wgLinkTitlesCheckRedirect and $wgLinkTitlesEnableNoTargetMagicWord
* and whether the target page is a redirect or contains the
* __NOAUTOLINKTARGET__ magic word.
*
* @param \Title $fromTitle
*
* @return boolean
*/
public function mayLinkTo( \Title $fromTitle ) {
// If checking for redirects is enabled and the target page does
// indeed redirect to the current page, return the page title as-is
// (unlinked).
if ( $this->config->checkRedirect ) {
$redirectTitle = $this->getContent()->getUltimateRedirectTarget();
if ( $redirectTitle && $redirectTitle->equals( $fromTitle ) ) {
return false;
}
};
// If the magic word __NOAUTOLINKTARGET__ is enabled and the target
// page does indeed contain this magic word, return the page title
// as-is (unlinked).
if ( $this->config->enableNoTargetMagicWord ) {
if ( $this->getContent()->matchMagicWord( \MagicWord::get('MAG_LINKTITLES_NOTARGET') ) ) {
return false;
}
};
return true;
}
/**
* Determines if the Target's title is the same as another title.
* @param Title $otherTitle Other title
* @return boolean True if the $otherTitle is the same, false if not.
*/
public function isSameTitle( \Title $otherTitle) {
return $this->title->equals( $otherTitle );
}
}

142
includes/Targets.php Normal file
View File

@ -0,0 +1,142 @@
<?php
/**
* The LinkTitles\Targets class.
*
* Copyright 2012-2017 Daniel Kraus <bovender@bovender.de> ('bovender')
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* @author Daniel Kraus <bovender@bovender.de>
*/
namespace LinkTitles;
/**
* Fetches potential target page titles from the database.
*/
class Targets {
private static $instance;
/**
* Singleton factory that returns a (cached) database query results with
* potential target page titles.
*
* The subset of pages that may serve as target pages depends on the
* name space of the source page. Therefore, if the $nameSpace differs from
* the cached name space, the database is queried again.
*
* @param String $nameSpace The namespace of the current page.
* @param Config $config LinkTitles configuration.
*/
public static function default( \Title $title, Config $config ) {
if ( ( self::$instance === null ) || ( self::$instance->nameSpace != $title->getNamespace() ) ) {
self::$instance = new Targets( $title, $config );
}
return self::$instance;
}
/**
* Invalidates the cache; the next call of Targets::default() will trigger
* a database query.
*
* Use this in unit tests which are performed in a single request cycle so that
* changes to the pages list may not be picked up by the cached Targets instance.
*/
public static function invalidate() {
self::$instance = null;
}
/**
* Holds the results of a database query for target page titles, filtered
* and sorted.
* @var IResultWrapper $queryResult
*/
public $queryResult;
/**
* Holds the name space (integer) for which the list of target pages was built.
* @var Int $nameSpace
*/
public $nameSpace;
private $config;
/**
* The constructor is private to enforce using the singleton pattern.
* @param \Title $title
*/
private function __construct( \Title $title, Config $config) {
$this->config = $config;
$this->nameSpace = $title->getNameSpace();
$this->fetch();
}
//
/**
* Fetches the page titles from the database.
*/
private function fetch() {
( $this->config->preferShortTitles ) ? $sortOrder = 'ASC' : $sortOrder = 'DESC';
// Build a blacklist of pages that are not supposed to be link
// targets. This includes the current page.
$blackList = str_replace( ' ', '_', '("' . implode( '","',$this->config->blackList ) . '")' );
// Build our weight list. Make sure current namespace is first element
$nameSpaces = array_diff( $this->config->nameSpaces, [ $this->nameSpace ] );
array_unshift( $nameSpaces, $this->nameSpace );
// No need for sanitiy check. we are sure that we have at least one element in the array
$weightSelect = "CASE page_namespace ";
$currentWeight = 0;
foreach ($nameSpaces as &$nameSpaceValue) {
$currentWeight = $currentWeight + 100;
$weightSelect = $weightSelect . " WHEN " . $nameSpaceValue . " THEN " . $currentWeight . PHP_EOL;
}
$weightSelect = $weightSelect . " END ";
$nameSpacesClause = '(' . implode( ', ', $nameSpaces ) . ')';
// Build an SQL query and fetch all page titles ordered by length from
// shortest to longest. Only titles from 'normal' pages (namespace uid
// = 0) are returned. Since the db may be sqlite, we need a try..catch
// structure because sqlite does not support the CHAR_LENGTH function.
$dbr = wfGetDB( DB_SLAVE );
try {
$this->queryResult = $dbr->select(
'page',
array( 'page_title', 'page_namespace' , "weight" => $weightSelect),
array(
'page_namespace IN ' . $nameSpacesClause,
'CHAR_LENGTH(page_title) >= ' . $this->config->minimumTitleLength,
'page_title NOT IN ' . $blackList,
),
__METHOD__,
array( 'ORDER BY' => 'weight ASC, CHAR_LENGTH(page_title) ' . $sortOrder )
);
} catch (Exception $e) {
$this->queryResult = $dbr->select(
'page',
array( 'page_title', 'page_namespace' , "weight" => $weightSelect ),
array(
'page_namespace IN ' . $nameSpacesClause,
'LENGTH(page_title) >= ' . $this->config->minimumTitleLength,
'page_title NOT IN ' . $blackList,
),
__METHOD__,
array( 'ORDER BY' => 'weight ASC, LENGTH(page_title) ' . $sortOrder )
);
}
}
}

View File

@ -1,5 +1,7 @@
<?php <?php
/* /**
* LinkTitles command line interface (CLI)/maintenance script
*
* Copyright 2012-2017 Daniel Kraus <bovender@bovender.de> @bovender * Copyright 2012-2017 Daniel Kraus <bovender@bovender.de> @bovender
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
@ -44,17 +46,21 @@ else
} }
}; };
require_once( __DIR__ . "/includes/LinkTitles_Extension.php" ); require_once( __DIR__ . "/includes/Extension.php" );
/// Core class of the maintanance script. /**
/// @note Note that the execution of maintenance scripts is prohibited for * Core class of the maintanance script.
/// an Apache web server due to a `.htaccess` file that declares `deny from * @note Note that the execution of maintenance scripts is prohibited for
/// all`. Other webservers may exhibit different behavior. Be aware that * an Apache web server due to a `.htaccess` file that declares `deny from
/// anybody who is able to execute this script may place a high load on the * all`. Other webservers may exhibit different behavior. Be aware that
/// server. * anybody who is able to execute this script may place a high load on the
/// @ingroup batch * server.
* @ingroup batch
*/
class Cli extends \Maintenance { class Cli extends \Maintenance {
/// The constructor adds a description and one option. /**
* Constructor.
*/
public function __construct() { public function __construct() {
parent::__construct(); parent::__construct();
$this->addDescription("Iterates over wiki pages and automatically adds links to other pages."); $this->addDescription("Iterates over wiki pages and automatically adds links to other pages.");
@ -72,34 +78,38 @@ class Cli extends \Maintenance {
true, // requires argument true, // requires argument
"p" "p"
); );
$this->addOption( // TODO: Add back logging options.
"log", // TODO: Add configuration options.
"enables logging to console", // $this->addOption(
false, // not required // "log",
false, // requires no argument // "enables logging to console",
"l" // false, // not required
); // false, // requires no argument
$this->addOption( // "l"
"debug", // );
"enables debug logging to console", // $this->addOption(
false, // not required // "debug",
false // requires no argument // "enables debug logging to console",
); // false, // not required
// false // requires no argument
// );
} }
/// Main function of the maintenance script. /*
/// Will iterate over all pages in the wiki (starting at a certain index, * Main function of the maintenance script.
/// if the `--start` option is given) and call LinkTitles::processPage() for * Will iterate over all pages in the wiki (starting at a certain index,
/// each page. * if the `--start` option is given) and call LinkTitles::processPage() for
* each page.
*/
public function execute() { public function execute() {
if ($this->hasOption('log')) // if ($this->hasOption('log'))
{ // {
Extension::$ltConsoleOutput = true; // Extension::$ltConsoleOutput = true;
} // }
if ($this->hasOption('debug')) // if ($this->hasOption('debug'))
{ // {
Extension::$ltConsoleOutputDebug = true; // Extension::$ltConsoleOutputDebug = true;
} // }
if ( $this->hasOption('page') ) { if ( $this->hasOption('page') ) {
if ( !$this->hasOption( 'start' ) ) { if ( !$this->hasOption( 'start' ) ) {
$this->singlePage(); $this->singlePage();
@ -117,6 +127,10 @@ class Cli extends \Maintenance {
} }
} }
/**
* Processes a single page.
* @return bool True on success, false on failure.
*/
private function singlePage() { private function singlePage() {
$pageName = strval( $this->getOption( 'page' ) ); $pageName = strval( $this->getOption( 'page' ) );
$this->output( "Processing single page: '$pageName'\n" ); $this->output( "Processing single page: '$pageName'\n" );
@ -131,17 +145,22 @@ class Cli extends \Maintenance {
return $success; return $success;
} }
/**
* Process all pages in the Wiki.
* @param integer $index Index of the start page.
* @return bool True on success, false on failure.
*/
private function allPages( $index = 0 ) { private function allPages( $index = 0 ) {
global $wgLinkTitlesNamespaces; $config = new Config();
// Retrieve page names from the database. // Retrieve page names from the database.
$dbr = $this->getDB( DB_SLAVE ); $dbr = $this->getDB( DB_SLAVE );
$namespacesClause = str_replace( '_', ' ','(' . implode( ', ', $wgLinkTitlesNamespaces ) . ')' ); $nameSpacesClause = str_replace( '_', ' ','(' . implode( ', ', $config->nameSpaces ) . ')' );
$res = $dbr->select( $res = $dbr->select(
'page', 'page',
array( 'page_title', 'page_namespace' ), array( 'page_title', 'page_namespace' ),
array( array(
'page_namespace IN ' . $namespacesClause, 'page_namespace IN ' . $nameSpacesClause,
), ),
__METHOD__, __METHOD__,
array( array(

View File

@ -0,0 +1,20 @@
<?php
/**
* Tests the LinkTitles\Config class.
*
* This single unit test basically serves to ensure the Config class is working.
* @group bovender
* @group Database
*/
class ConfigTest extends LinkTitles\TestCase {
public function testParseOnEdit() {
$this->setMwGlobals( [
'wgLinkTitlesParseOnEdit' => true,
'wgLinkTitlesParseOnRender' => false
] );
$config = new LinkTitles\Config();
global $wgLinkTitlesParseOnEdit;
$this->assertSame( $config->parseOnEdit, $wgLinkTitlesParseOnEdit );
}
}

View File

@ -0,0 +1,27 @@
<?php
/**
* @group bovender
* @group Database
*/
class ParseOnEditTest extends LinkTitles\TestCase {
public function testParseOnEdit() {
$this->setMwGlobals( [
'wgLinkTitlesParseOnEdit' => true,
'wgLinkTitlesParseOnRender' => false
] );
$pageId = $this->insertPage( 'test page', 'This page should link to the link target but not to test page' )['id'];
$page = WikiPage::newFromId( $pageId );
$this->assertSame( 'This page should link to the [[link target]] but not to test page', self::getPageText( $page ) );
}
public function testDoNotParseOnEdit() {
$this->setMwGlobals( [
'wgLinkTitlesParseOnEdit' => false,
'wgLinkTitlesParseOnRender' => false
] );
$pageId = $this->insertPage( 'test page', 'This page should not link to the link target' )['id'];
$page = WikiPage::newFromId( $pageId );
$this->assertSame( 'This page should not link to the link target', self::getPageText( $page ) );
}
}

View File

@ -0,0 +1,31 @@
<?php
/**
* @group bovender
*/
class SplitterTest extends MediaWikiTestCase {
/**
* @dataProvider provideSplitData
*/
public function testSplit( $input, $expectedOutput ) {
$splitter = LinkTitles\Splitter::default();
$this->assertSame( $expectedOutput, $splitter->split( $input ) );
}
// TODO: Add more examples.
public static function provideSplitData() {
return [
[
'this may be linked [[this may not be linked]]',
[ 'this may be linked ', '[[this may not be linked]]', '' ]
],
[
'this may be linked <gallery>this may not be linked</gallery>',
[ 'this may be linked ', '<gallery>this may not be linked</gallery>', '' ]
],
[
'this may be linked {{mytemplate|param={{transcluded}}}}',
[ 'this may be linked ', '{{mytemplate|param={{transcluded}}}}', '' ]
],
];
}
}

View File

@ -0,0 +1,40 @@
<?php
/**
* @group bovender
*/
class TargetTest extends MediaWikiTestCase {
/**
* @dataProvider provideStartOnly
*/
public function testTargetWordStartOnly( $enabled, $delimiter ) {
$config = new LinkTitles\Config();
$config->wordStartOnly = $enabled;
$target = new LinKTitles\Target( NS_MAIN, 'test page', $config );
$this->assertSame( $delimiter, $target->wordStart );
}
public static function provideStartOnly() {
return [
[ true, '(?<!\pL)' ],
[ false, '' ]
];
}
/**
* @dataProvider provideEndOnly
*/
public function testTargetWordEndOnly( $enabled, $delimiter ) {
$config = new LinkTitles\Config();
$config->wordEndOnly = $enabled;
$target = new LinKTitles\Target( NS_MAIN, 'test page', $config );
$this->assertSame( $delimiter, $target->wordEnd );
}
public static function provideEndOnly() {
return [
[ true, '(?!\pL)' ],
[ false, '' ]
];
}
}

View File

@ -0,0 +1,26 @@
<?php
/**
* Tests the LinkTitles\Targets class.
*
* @group bovender
* @group Database
*/
class TargetsTest extends LinkTitles\TestCase {
/**
* This test asserts that the list of potential link targets is 0
* @return [type] [description]
*/
public function testTargets() {
$title = \Title::newFromText( 'link target' );
$targets = LinkTitles\Targets::default( $title, new LinkTitles\Config() );
// Count number of articles: Inspired by updateArticleCount.php maintenance
// script: https://doc.wikimedia.org/mediawiki-core/master/php/updateArticleCount_8php_source.html
$dbr = wfGetDB( DB_SLAVE );
$counter = new SiteStatsInit( $dbr );
$count = $counter->pages();
$this->assertEquals( $targets->queryResult->numRows(), $count );
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace LinkTitles;
abstract class TestCase extends \MediaWikiTestCase {
protected function setUp() {
parent::setUp();
$this->insertPage( 'link target', 'This page serves as a link target' );
Targets::invalidate(); // force re-querying the pages table
}
protected function tearDown() {
parent::tearDown();
}
protected function getPageText( \WikiPage $page ) {
$content = $page->getContent();
return $page->getContentHandler()->serializeContent( $content );
}
}