prod_check.update.inc 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. <?php
  2. /**
  3. * ALL code here is taken from the Drupal core's update module. prod_check
  4. * recommends that you turn this module off, so we provide the VERY BASIC
  5. * functionality to get a list of the installed modules with version info and
  6. * transfer this to prod_monitor over XMLRPC (alot of data here!) so that
  7. * prod_monitor can do all the update checking locally.
  8. * This approach was chosen over this approach:
  9. *
  10. * module_load_include('module', 'update', 'update');
  11. *
  12. * if ($available = update_get_available(TRUE)) {
  13. * $data['modules'] = update_calculate_project_data($available);
  14. * }
  15. *
  16. * This way, we might just as well turn on the update module and periodically
  17. * download the results. It's just not efficient...
  18. * Also note that passing TRUE to update_get_available() will bypass (refresh)
  19. * the caches, essentially the same as we do in _prod_check_module_list().
  20. * See inline comments there for more info.
  21. */
  22. /**
  23. * Taken from Core: modules/update/update.compare.inc, line 81
  24. *
  25. * Populate an array of project data.
  26. *
  27. * This iterates over a list of the installed modules or themes and groups
  28. * them by project and status. A few parts of this function assume that
  29. * enabled modules and themes are always processed first, and if disabled
  30. * modules or themes are being processed (there is a setting to control if
  31. * disabled code should be included in the Available updates report or not),
  32. * those are only processed after $projects has been populated with
  33. * information about the enabled code. 'Hidden' modules and themes are always
  34. * ignored. This function also records the latest change time on the .info
  35. * files for each module or theme, which is important data which is used when
  36. * deciding if the cached available update data should be invalidated.
  37. *
  38. * @param $projects
  39. * Reference to the array of project data of what's installed on this site.
  40. * @param $list
  41. * Array of data to process to add the relevant info to the $projects array.
  42. * @param $project_type
  43. * The kind of data in the list (can be 'module' or 'theme').
  44. * @param $status
  45. * Boolean that controls what status (enabled or disabled) to process out of
  46. * the $list and add to the $projects array.
  47. *
  48. * @see update_get_projects()
  49. */
  50. function _prod_check_process_info_list(&$projects, $list, $project_type, $status) {
  51. foreach ($list as $file) {
  52. // A disabled base theme of an enabled sub-theme still has all of its code
  53. // run by the sub-theme, so we include it in our "enabled" projects list.
  54. if ($status && !$file->status && !empty($file->sub_themes)) {
  55. foreach ($file->sub_themes as $key => $name) {
  56. // Build a list of enabled sub-themes.
  57. if ($list[$key]->status) {
  58. $file->enabled_sub_themes[$key] = $name;
  59. }
  60. }
  61. // If there are no enabled subthemes, we should ignore this base theme
  62. // for the enabled case. If the site is trying to display disabled
  63. // themes, we'll catch it then.
  64. if (empty($file->enabled_sub_themes)) {
  65. continue;
  66. }
  67. }
  68. // Otherwise, just add projects of the proper status to our list.
  69. elseif ($file->status != $status) {
  70. continue;
  71. }
  72. // Skip if the .info file is broken.
  73. if (empty($file->info)) {
  74. continue;
  75. }
  76. // Skip if it's a hidden module or theme.
  77. if (!empty($file->info['hidden'])) {
  78. continue;
  79. }
  80. // If the .info doesn't define the 'project', try to figure it out.
  81. if (!isset($file->info['project'])) {
  82. $file->info['project'] = _prod_check_get_project_name($file);
  83. }
  84. // If we still don't know the 'project', give up.
  85. if (empty($file->info['project'])) {
  86. continue;
  87. }
  88. // If we don't already know it, grab the change time on the .info file
  89. // itself. Note: we need to use the ctime, not the mtime (modification
  90. // time) since many (all?) tar implementations will go out of their way to
  91. // set the mtime on the files it creates to the timestamps recorded in the
  92. // tarball. We want to see the last time the file was changed on disk,
  93. // which is left alone by tar and correctly set to the time the .info file
  94. // was unpacked.
  95. if (!isset($file->info['_info_file_ctime'])) {
  96. $info_filename = dirname($file->uri) . '/' . $file->name . '.info';
  97. $file->info['_info_file_ctime'] = filectime($info_filename);
  98. }
  99. if (!isset($file->info['datestamp'])) {
  100. $file->info['datestamp'] = 0;
  101. }
  102. $project_name = $file->info['project'];
  103. // Figure out what project type we're going to use to display this module
  104. // or theme. If the project name is 'drupal', we don't want it to show up
  105. // under the usual "Modules" section, we put it at a special "Drupal Core"
  106. // section at the top of the report.
  107. if ($project_name == 'drupal') {
  108. $project_display_type = 'core';
  109. }
  110. else {
  111. $project_display_type = $project_type;
  112. }
  113. if (empty($status) && empty($file->enabled_sub_themes)) {
  114. // If we're processing disabled modules or themes, append a suffix.
  115. // However, we don't do this to a a base theme with enabled
  116. // subthemes, since we treat that case as if it is enabled.
  117. $project_display_type .= '-disabled';
  118. }
  119. // Add a list of sub-themes that "depend on" the project and a list of base
  120. // themes that are "required by" the project.
  121. if ($project_name == 'drupal') {
  122. // Drupal core is always required, so this extra info would be noise.
  123. $sub_themes = array();
  124. $base_themes = array();
  125. }
  126. else {
  127. // Add list of enabled sub-themes.
  128. $sub_themes = !empty($file->enabled_sub_themes) ? $file->enabled_sub_themes : array();
  129. // Add list of base themes.
  130. $base_themes = !empty($file->base_themes) ? $file->base_themes : array();
  131. }
  132. if (!isset($projects[$project_name])) {
  133. // Only process this if we haven't done this project, since a single
  134. // project can have multiple modules or themes.
  135. $projects[$project_name] = array(
  136. 'name' => $project_name,
  137. // Only save attributes from the .info file we care about so we do not
  138. // bloat our RAM usage needlessly.
  139. 'info' => _prod_check_filter_project_info($file->info),
  140. 'datestamp' => $file->info['datestamp'],
  141. 'includes' => array($file->name => $file->info['name']),
  142. 'project_type' => $project_display_type,
  143. 'project_status' => $status,
  144. 'sub_themes' => $sub_themes,
  145. 'base_themes' => $base_themes,
  146. );
  147. }
  148. elseif ($projects[$project_name]['project_type'] == $project_display_type) {
  149. // Only add the file we're processing to the 'includes' array for this
  150. // project if it is of the same type and status (which is encoded in the
  151. // $project_display_type). This prevents listing all the disabled
  152. // modules included with an enabled project if we happen to be checking
  153. // for disabled modules, too.
  154. $projects[$project_name]['includes'][$file->name] = $file->info['name'];
  155. $projects[$project_name]['info']['_info_file_ctime'] = max($projects[$project_name]['info']['_info_file_ctime'], $file->info['_info_file_ctime']);
  156. $projects[$project_name]['datestamp'] = max($projects[$project_name]['datestamp'], $file->info['datestamp']);
  157. if (!empty($sub_themes)) {
  158. $projects[$project_name]['sub_themes'] += $sub_themes;
  159. }
  160. if (!empty($base_themes)) {
  161. $projects[$project_name]['base_themes'] += $base_themes;
  162. }
  163. }
  164. elseif (empty($status)) {
  165. // If we have a project_name that matches, but the project_display_type
  166. // does not, it means we're processing a disabled module or theme that
  167. // belongs to a project that has some enabled code. In this case, we add
  168. // the disabled thing into a separate array for separate display.
  169. $projects[$project_name]['disabled'][$file->name] = $file->info['name'];
  170. }
  171. }
  172. }
  173. /**
  174. * Taken from Core: modules/update/update.compare.inc, line 238
  175. *
  176. * Given a $file object (as returned by system_get_files_database()), figure
  177. * out what project it belongs to.
  178. *
  179. * @see system_get_files_database()
  180. */
  181. function _prod_check_get_project_name($file) {
  182. $project_name = '';
  183. if (isset($file->info['project'])) {
  184. $project_name = $file->info['project'];
  185. }
  186. elseif (isset($file->info['package']) && (strpos($file->info['package'], 'Core') === 0)) {
  187. $project_name = 'drupal';
  188. }
  189. return $project_name;
  190. }
  191. /**
  192. * Taken from Core: modules/update/update.compare.inc, line 684
  193. *
  194. * Filter the project .info data to only save attributes we need.
  195. *
  196. * @param array $info
  197. * Array of .info file data as returned by drupal_parse_info_file().
  198. *
  199. * @return
  200. * Array of .info file data we need for the Update manager.
  201. *
  202. * @see _prod_check_process_info_list()
  203. */
  204. function _prod_check_filter_project_info($info) {
  205. $whitelist = array(
  206. '_info_file_ctime',
  207. 'datestamp',
  208. 'major',
  209. 'name',
  210. 'package',
  211. 'project',
  212. 'project status url',
  213. 'version',
  214. );
  215. return array_intersect_key($info, drupal_map_assoc($whitelist));
  216. }