apachesolr.admin.inc 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373
  1. <?php
  2. /**
  3. * @file
  4. * Administrative pages for the Apache Solr framework.
  5. */
  6. /**
  7. * Form to delete a search environment
  8. *
  9. * @param array $form
  10. * @param array $form_state
  11. * @param array $environment
  12. *
  13. * @return array output of confirm_form()
  14. */
  15. function apachesolr_environment_delete_form(array $form, array &$form_state, array $environment) {
  16. $form['env_id'] = array(
  17. '#type' => 'value',
  18. '#value' => $environment['env_id'],
  19. );
  20. if (isset($environment['export_type']) && $environment['export_type'] == 3) {
  21. $verb = t('Revert');
  22. }
  23. else {
  24. $verb = t('Delete');
  25. }
  26. return confirm_form(
  27. $form,
  28. t('Are you sure you want to !verb search environment %name?', array('%name' => $environment['name'], '!verb' => strtolower($verb))),
  29. 'admin/config/search/apachesolr',
  30. t('This action cannot be undone.'),
  31. $verb,
  32. t('Cancel')
  33. );
  34. }
  35. /**
  36. * Submit handler for the delete form
  37. *
  38. * @param array $form
  39. * @param array $form_state
  40. */
  41. function apachesolr_environment_delete_form_submit(array $form, array &$form_state) {
  42. if (apachesolr_environment_delete($form_state['values']['env_id'])) {
  43. drupal_set_message(t('The search environment was deleted'));
  44. }
  45. $form_state['redirect'] = 'admin/config/search/apachesolr/settings';
  46. }
  47. function apachesolr_environment_edit_delete_submit($form, &$form_state) {
  48. $form_state['redirect'] = 'admin/config/search/apachesolr/settings/' . $form_state['values']['env_id'] . '/delete';
  49. // Regardlessly of the destination parameter we want to go to another page
  50. unset($_GET['destination']);
  51. drupal_static_reset('drupal_get_destination');
  52. drupal_get_destination();
  53. }
  54. /**
  55. * Settings page for a specific environment (or default one if not provided)
  56. *
  57. * @param array|bool $environment
  58. *
  59. * @return array Render array for a settings page
  60. */
  61. function apachesolr_environment_settings_page(array $environment = array()) {
  62. if (empty($environment)) {
  63. $env_id = apachesolr_default_environment();
  64. $environment = apachesolr_environment_load($env_id);
  65. }
  66. $env_id = $environment['env_id'];
  67. // Initializes output with information about which environment's setting we are
  68. // editing, as it is otherwise not transparent to the end user.
  69. $output = array(
  70. 'apachesolr_environment' => array(
  71. '#theme' => 'apachesolr_settings_title',
  72. '#env_id' => $env_id,
  73. ),
  74. );
  75. $output['form'] = drupal_get_form('apachesolr_environment_edit_form', $environment);
  76. return $output;
  77. }
  78. /**
  79. * Form to clone a certain environment
  80. *
  81. * @param array $form
  82. * @param array $form_state
  83. * @param array $environment
  84. *
  85. * @return array output of confirm_form()
  86. */
  87. function apachesolr_environment_clone_form(array $form, array &$form_state, array $environment) {
  88. $form['env_id'] = array(
  89. '#type' => 'value',
  90. '#value' => $environment['env_id'],
  91. );
  92. return confirm_form(
  93. $form,
  94. t('Are you sure you want to clone search environment %name?', array('%name' => $environment['name'])),
  95. 'admin/config/search/apachesolr',
  96. '',
  97. t('Clone'),
  98. t('Cancel')
  99. );
  100. }
  101. /**
  102. * Submit handler for the clone form
  103. *
  104. * @param array $form
  105. * @param array $form_state
  106. */
  107. function apachesolr_environment_clone_form_submit(array $form, array &$form_state) {
  108. if (apachesolr_environment_clone($form_state['values']['env_id'])) {
  109. drupal_set_message(t('The search environment was cloned'));
  110. }
  111. $form_state['redirect'] = 'admin/config/search/apachesolr/settings';
  112. }
  113. /**
  114. * Submit handler for the confirmation page of cloning an environment
  115. *
  116. * @param array $form
  117. * @param array $form_state
  118. */
  119. function apachesolr_environment_clone_submit(array $form, array &$form_state) {
  120. $form_state['redirect'] = 'admin/config/search/apachesolr/settings/' . $form_state['values']['env_id'] . '/clone';
  121. }
  122. /**
  123. * Form builder for adding/editing a Solr environment used as a menu callback.
  124. */
  125. function apachesolr_environment_edit_form(array $form, array &$form_state, array $environment = array()) {
  126. if (empty($environment)) {
  127. $environment = array();
  128. }
  129. $environment += array('env_id' => '', 'name' => '', 'url' => '', 'service_class' => '', 'conf' => array());
  130. $form['#environment'] = $environment;
  131. $form['url'] = array(
  132. '#type' => 'textfield',
  133. '#title' => t('Solr server URL'),
  134. '#default_value' => $environment['url'],
  135. '#description' => t('Example: http://localhost:8983/solr'),
  136. '#required' => TRUE,
  137. );
  138. $is_default = $environment['env_id'] == apachesolr_default_environment();
  139. $form['make_default'] = array(
  140. '#type' => 'checkbox',
  141. '#title' => t('Make this Solr search environment the default'),
  142. '#default_value' => $is_default,
  143. '#disabled' => $is_default,
  144. );
  145. $form['name'] = array(
  146. '#type' => 'textfield',
  147. '#title' => t('Description'),
  148. '#default_value' => $environment['name'],
  149. '#required' => TRUE,
  150. );
  151. $form['env_id'] = array(
  152. '#type' => 'machine_name',
  153. '#title' => t('Environment id'),
  154. '#machine_name' => array(
  155. 'exists' => 'apachesolr_environment_load',
  156. ),
  157. '#default_value' => $environment['env_id'],
  158. '#disabled' => !empty($environment['env_id']), // Cannot change it once set.
  159. '#description' => t('Unique, machine-readable identifier for this Solr environment.'),
  160. '#required' => TRUE,
  161. );
  162. $form['service_class'] = array(
  163. '#type' => 'value',
  164. '#value' => $environment['service_class'],
  165. );
  166. $form['conf'] = array(
  167. '#tree' => TRUE,
  168. );
  169. $form['conf']['apachesolr_read_only'] = array(
  170. '#type' => 'radios',
  171. '#title' => t('Index write access'),
  172. '#default_value' => isset($environment['conf']['apachesolr_read_only']) ? $environment['conf']['apachesolr_read_only'] : APACHESOLR_READ_WRITE,
  173. '#options' => array(APACHESOLR_READ_WRITE => t('Read and write (normal)'), APACHESOLR_READ_ONLY => t('Read only')),
  174. '#description' => t('<em>Read only</em> stops this site from sending updates to this search environment. Useful for development sites.'),
  175. );
  176. $form['conf']['apachesolr_direct_commit'] = array(
  177. '#type' => 'checkbox',
  178. '#title' => t('Commit changes after the index process has submitted them'),
  179. '#description' => t('Commits the updates to solr. Uses soft commits where possible'),
  180. '#default_value' => isset($environment['conf']['apachesolr_direct_commit']) ? $environment['conf']['apachesolr_direct_commit'] : false,
  181. );
  182. $form['conf']['apachesolr_soft_commit'] = array(
  183. '#type' => 'checkbox',
  184. '#title' => t('Commit changes to memory.'),
  185. '#description' => t(' Reduces the load on your disk. Also known as Soft Commits, recommended for Solr 4.'),
  186. '#default_value' => isset($environment['conf']['apachesolr_soft_commit']) ? $environment['conf']['apachesolr_soft_commit'] : false,
  187. );
  188. $form['actions'] = array(
  189. '#type' => 'actions',
  190. );
  191. $form['actions']['save'] = array(
  192. '#type' => 'submit',
  193. '#validate' => array('apachesolr_environment_edit_validate'),
  194. '#submit' => array('apachesolr_environment_edit_submit'),
  195. '#value' => t('Save'),
  196. );
  197. $form['actions']['save_edit'] = array(
  198. '#type' => 'submit',
  199. '#validate' => array('apachesolr_environment_edit_validate'),
  200. '#submit' => array('apachesolr_environment_edit_submit'),
  201. '#value' => t('Save and edit'),
  202. );
  203. $form['actions']['test'] = array(
  204. '#type' => 'submit',
  205. '#validate' => array('apachesolr_environment_edit_validate'),
  206. '#submit' => array('apachesolr_environment_edit_test_submit'),
  207. '#value' => t('Test connection'),
  208. );
  209. if (!empty($environment['env_id']) && !$is_default) {
  210. $form['actions']['delete'] = array(
  211. '#type' => 'submit',
  212. '#submit' => array('apachesolr_environment_edit_delete_submit'),
  213. '#value' => t('Delete'),
  214. );
  215. }
  216. // Ensures destination is an internal URL, builds "cancel" link.
  217. if (isset($_GET['destination']) && !url_is_external($_GET['destination'])) {
  218. $destination = $_GET['destination'];
  219. }
  220. else {
  221. $destination = 'admin/config/search/apachesolr/settings';
  222. }
  223. $form['actions']['cancel'] = array(
  224. '#type' => 'link',
  225. '#title' => t('Cancel'),
  226. '#href' => $destination,
  227. );
  228. return $form;
  229. }
  230. /**
  231. * Submit handler for the test button in the environment edit page
  232. *
  233. * @param array $form
  234. * @param array $form_state
  235. */
  236. function apachesolr_environment_edit_test_submit(array $form, array &$form_state) {
  237. $ping = apachesolr_server_status($form_state['values']['url'], $form_state['values']['service_class']);
  238. if ($ping) {
  239. drupal_set_message(t('Your site has contacted the Apache Solr server.'));
  240. }
  241. else {
  242. drupal_set_message(t('Your site was unable to contact the Apache Solr server.'), 'error');
  243. }
  244. $form_state['rebuild'] = TRUE;
  245. }
  246. /**
  247. * Validate handler for the environment edit page
  248. *
  249. * @param array $form
  250. * @param array $form_state
  251. */
  252. function apachesolr_environment_edit_validate(array $form, array &$form_state) {
  253. $parts = parse_url($form_state['values']['url']);
  254. foreach (array('scheme', 'host', 'path') as $key) {
  255. if (empty($parts[$key])) {
  256. form_set_error('url', t('The Solr server URL needs to include a !part', array('!part' => $key)));
  257. }
  258. }
  259. if (isset($parts['port'])) {
  260. // parse_url() should always give an integer for port. Since drupal_http_request()
  261. // also uses parse_url(), we don't need to validate anything except the range.
  262. $pattern = empty($parts['user']) ? '@://[^:]+:([^/]+)@' : '#://[^@]+@[^:]+:([^/]+)#';
  263. preg_match($pattern, $form_state['values']['url'], $m);
  264. if (empty($m[1]) || !ctype_digit($m[1]) || $m[1] < 1 || $m[1] > 65535) {
  265. form_set_error('port', t('The port has to be an integer between 1 and 65535.'));
  266. }
  267. else {
  268. // Normalize the url by removing extra slashes and whitespace.
  269. $form_state['values']['url'] = trim($form_state['values']['url'], "/ \t\r\n\0\x0B");
  270. }
  271. }
  272. }
  273. /**
  274. * Submit handler for the environment edit page
  275. *
  276. * @param array $form
  277. * @param array $form_state
  278. */
  279. function apachesolr_environment_edit_submit(array $form, array &$form_state) {
  280. apachesolr_environment_save($form_state['values']);
  281. if (!empty($form_state['values']['make_default'])) {
  282. apachesolr_set_default_environment($form_state['values']['env_id']);
  283. }
  284. cache_clear_all('apachesolr:environments', 'cache_apachesolr');
  285. drupal_set_message(t('The %name search environment has been saved.', array('%name' => $form_state['values']['name'])));
  286. if ($form_state['values']['op'] == t('Save')) {
  287. $form_state['redirect'] = 'admin/config/search/apachesolr/settings';
  288. }
  289. else {
  290. $form_state['redirect'] = current_path();
  291. }
  292. // Regardlessly of the destination parameter we want to go to another page
  293. unset($_GET['destination']);
  294. drupal_static_reset('drupal_get_destination');
  295. drupal_get_destination();
  296. }
  297. /**
  298. * Check to see if the facetapi module is installed, and if not put up
  299. * a message.
  300. *
  301. * Only call this function if the user is already in a position for this to
  302. * be useful.
  303. */
  304. function apachesolr_check_facetapi() {
  305. if (!module_exists('facetapi')) {
  306. $filename = db_query_range("SELECT filename FROM {system} WHERE type = 'module' AND name = 'facetapi'", 0, 1)
  307. ->fetchField();
  308. if ($filename && file_exists($filename)) {
  309. drupal_set_message(t('If you <a href="@modules">enable the facetapi module</a>, Apache Solr Search will provide you with configurable facets.', array('@modules' => url('admin/modules'))));
  310. }
  311. else {
  312. drupal_set_message(t('If you install the facetapi module from !href, Apache Solr Search will provide you with configurable facets.', array('!href' => url('http://drupal.org/project/facetapi'))));
  313. }
  314. }
  315. }
  316. /**
  317. * Form builder for general settings used as a menu callback.
  318. *
  319. * @param array $form
  320. * @param array $form_state
  321. *
  322. * @return array Output of the system_settings_form()
  323. */
  324. function apachesolr_settings(array $form, array &$form_state) {
  325. $form = array();
  326. $rows = array();
  327. // Environment settings
  328. $id = apachesolr_default_environment();
  329. $environments = apachesolr_load_all_environments();
  330. $default_environment = apachesolr_default_environment();
  331. apachesolr_check_facetapi();
  332. // Reserve a row for the default one
  333. $rows[$default_environment] = array();
  334. foreach ($environments as $environment_id => $data) {
  335. // Define all the Operations
  336. $confs = array();
  337. $ops = array();
  338. // Whenever facetapi is enabled we also enable our operation link
  339. if (module_exists('facetapi')) {
  340. $confs['facets'] = array(
  341. 'class' => 'operation',
  342. 'data' => l(t('Facets'),
  343. 'admin/config/search/apachesolr/settings/' . $data['env_id'] . '/facets',
  344. array('query' => array('destination' => current_path()))
  345. ),
  346. );
  347. }
  348. // These are our result and bias settings
  349. if (module_exists('apachesolr_search')) {
  350. $confs['result_bias'] = array(
  351. 'class' => 'operation',
  352. 'data' => l(t('Bias'),
  353. 'admin/config/search/apachesolr/settings/' . $data['env_id'] . '/bias',
  354. array('query' => array('destination' => current_path()))
  355. ),
  356. );
  357. }
  358. $confs['index'] = array(
  359. 'class' => 'operation',
  360. 'data' => l(t('Index'),
  361. 'admin/config/search/apachesolr/settings/' . $data['env_id'] . '/index'
  362. ),
  363. );
  364. $ops['edit'] = array(
  365. 'class' => 'operation',
  366. 'data' => l(t('Edit'),
  367. 'admin/config/search/apachesolr/settings/' . $data['env_id'] . '/edit',
  368. array('query' => array('destination' => current_path()))
  369. ),
  370. );
  371. $ops['clone'] = array(
  372. 'class' => 'operation',
  373. 'data' => l(t('Clone'),
  374. 'admin/config/search/apachesolr/settings/' . $data['env_id'] . '/clone',
  375. array('query' => array('destination' => $_GET['q']))
  376. ),
  377. );
  378. $env_name = l($data['name'], 'admin/config/search/apachesolr/settings/' . $data['env_id'] . '/edit', array('query' => array('destination' => $_GET['q'])));
  379. // Is this row our default environment?
  380. if ($environment_id == $default_environment) {
  381. $env_name = t('!environment <em>(Default)</em>', array('!environment' => $env_name));
  382. $env_class_row = 'default-environment';
  383. }
  384. else {
  385. $env_class_row = '';
  386. }
  387. // For every non-default we add a delete link
  388. // Allow to revert a search environment or to delete it
  389. $delete_value = '';
  390. if (!isset($data['in_code_only'])) {
  391. if ((isset($data['type']) && $data['type'] == 'Overridden')) {
  392. $delete_value = array(
  393. 'class' => 'operation',
  394. 'data' => l(t('Revert'), 'admin/config/search/apachesolr/settings/' . $data['env_id'] . '/delete'),
  395. );
  396. }
  397. // don't allow the deletion of the default environment
  398. elseif ($environment_id != $default_environment) {
  399. $delete_value = array(
  400. 'class' => 'operation',
  401. 'data' => l(t('Delete'), 'admin/config/search/apachesolr/settings/' . $data['env_id'] . '/delete'),
  402. );
  403. }
  404. }
  405. $ops['delete'] = $delete_value;
  406. // When we are receiving a http POST (so the page does not show) we do not
  407. // want to check the statusses of any environment
  408. $class = '';
  409. if (empty($form_state['input'])) {
  410. $class = apachesolr_server_status($data['url'], $data['service_class']) ? 'ok' : 'error';
  411. }
  412. $headers = array(
  413. array('data' => t('Name'), 'colspan' => 2),
  414. t('URL'),
  415. array('data' => t('Configuration'), 'colspan' => count($confs)),
  416. array('data' => t('Operations'), 'colspan' => count($ops)),
  417. );
  418. $rows[$environment_id] = array('data' =>
  419. array(
  420. // Cells
  421. array(
  422. 'class' => 'status-icon',
  423. 'data' => '<div title="' . $class . '"><span class="element-invisible">' . $class . '</span></div>',
  424. ),
  425. array(
  426. 'class' => $env_class_row,
  427. 'data' => $env_name,
  428. ),
  429. check_plain($data['url']),
  430. ),
  431. 'class' => array(drupal_html_class($class)),
  432. );
  433. // Add the links to the page
  434. $rows[$environment_id]['data'] = array_merge($rows[$environment_id]['data'], $confs);
  435. $rows[$environment_id]['data'] = array_merge($rows[$environment_id]['data'], $ops);
  436. }
  437. $form['apachesolr_host_settings']['actions'] = array(
  438. '#markup' => '<ul class="action-links">' . drupal_render($actions) . '</ul>',
  439. );
  440. $form['apachesolr_host_settings']['table'] = array(
  441. '#theme' => 'table',
  442. '#header' => $headers,
  443. '#rows' => array_values($rows),
  444. '#attributes' => array('class' => array('admin-apachesolr')),
  445. );
  446. $form['advanced'] = array(
  447. '#type' => 'fieldset',
  448. '#title' => t('Advanced configuration'),
  449. '#collapsed' => TRUE,
  450. '#collapsible' => TRUE,
  451. );
  452. $form['advanced']['apachesolr_set_nodeapi_messages'] = array(
  453. '#type' => 'radios',
  454. '#title' => t('Extra help messages for administrators'),
  455. '#description' => t('Adds notices to a page whenever Drupal changed content that needs reindexing'),
  456. '#default_value' => variable_get('apachesolr_set_nodeapi_messages', 1),
  457. '#options' => array(0 => t('Disabled'), 1 => t('Enabled')),
  458. );
  459. // Number of Items to index
  460. $numbers = drupal_map_assoc(array(1, 5, 10, 20, 50, 100, 200));
  461. $default_cron_limit = variable_get('apachesolr_cron_limit', 50);
  462. // apachesolr_cron_limit may be overridden in settings.php. If its current
  463. // value is not among the default set of options, add it.
  464. if (!isset($numbers[$default_cron_limit])) {
  465. $numbers[$default_cron_limit] = $default_cron_limit;
  466. }
  467. $form['advanced']['apachesolr_cron_limit'] = array(
  468. '#type' => 'select',
  469. '#title' => t('Number of items to index per cron run'),
  470. '#default_value' => $default_cron_limit,
  471. '#options' => $numbers,
  472. '#description' => t('Reduce the number of items to prevent timeouts and memory errors while indexing.', array('@cron' => url('admin/reports/status')))
  473. );
  474. $options = array('apachesolr:show_error' => t('Show error message'));
  475. $system_info = system_get_info('module');
  476. if (module_exists('search')) {
  477. foreach (search_get_info() as $module => $search_info) {
  478. // Don't allow apachesolr to return results on failure of apachesolr.
  479. if ($module == 'apachesolr_search') {
  480. continue;
  481. }
  482. $options[$module] = t('Show @name search results', array('@name' => $system_info[$module]['name']));
  483. }
  484. }
  485. $options['apachesolr:show_no_results'] = t('Show no results');
  486. $form['advanced']['apachesolr_failure'] = array(
  487. '#type' => 'select',
  488. '#title' => t('On failure'),
  489. '#options' => $options,
  490. '#default_value' => variable_get('apachesolr_failure', 'apachesolr:show_error'),
  491. );
  492. $form['advanced']['apachesolr_watchdog_successes'] = array(
  493. '#type' => 'checkbox',
  494. '#title' => t('Log successful Apache Solr Search actions'),
  495. '#description' => t('Watchdog will log successful adds and indexes.'),
  496. '#default_value' => variable_get('apachesolr_watchdog_successes', TRUE),
  497. );
  498. return system_settings_form($form);
  499. }
  500. /**
  501. * Gets information about the fields already in solr index.
  502. *
  503. * @param array $environment
  504. * The environment for which we need to ask the status from
  505. *
  506. * @return array page render array
  507. */
  508. function apachesolr_status_page($environment = array()) {
  509. if (empty($environment)) {
  510. $env_id = apachesolr_default_environment();
  511. $environment = apachesolr_environment_load($env_id);
  512. }
  513. else {
  514. $env_id = $environment['env_id'];
  515. }
  516. // Check for availability
  517. if (!apachesolr_server_status($environment['url'], $environment['service_class'])) {
  518. drupal_set_message(t('The server seems to be unavailable. Please verify the server settings at the <a href="!settings_page">settings page</a>', array('!settings_page' => url("admin/config/search/apachesolr/settings/{$environment['env_id']}/edit", array('query' => drupal_get_destination())))), 'warning');
  519. return '';
  520. }
  521. try {
  522. $solr = apachesolr_get_solr($environment["env_id"]);
  523. $solr->clearCache();
  524. $data = $solr->getLuke();
  525. }
  526. catch (Exception $e) {
  527. watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
  528. drupal_set_message(nl2br(check_plain($e->getMessage())), "warning");
  529. $data = new stdClass;
  530. $data->fields = array();
  531. }
  532. $messages = array();
  533. if (isset($data->index->numDocs)) {
  534. try {
  535. // Collect the stats
  536. $stats_summary = $solr->getStatsSummary();
  537. module_load_include('inc', 'apachesolr', 'apachesolr.index');
  538. $status = apachesolr_index_status($environment["env_id"]);
  539. // We need a schema version greater than beta3. This is mostly to catch
  540. // people using the Drupal 6 schema.
  541. if (preg_match('/^drupal-[13]/', $stats_summary['@schema_version'])) {
  542. $minimum = 'drupal-3.0-beta4';
  543. if (version_compare($stats_summary['@schema_version'], $minimum, '<')) {
  544. drupal_set_message(t('Your schema.xml version is too old. You must update it to at least %minimum and re-index your content.', array('%minimum' => $minimum)), 'error');
  545. }
  546. }
  547. $pending_msg = $stats_summary['@pending_docs'] ? t('(@pending_docs sent but not yet processed)', $stats_summary) : '';
  548. $index_msg = $stats_summary['@index_size'] ? t('(@index_size on disk)', $stats_summary) : '';
  549. $indexed_message = t('@num Items !pending !index_msg', array(
  550. '@num' => $data->index->numDocs,
  551. '!pending' => $pending_msg,
  552. '!index_msg' => $index_msg,
  553. ));
  554. $messages[] = array(t('Indexed'), $indexed_message);
  555. $remaining_message = t('@items (@percentage% has been sent to the server)', array(
  556. '@items' => format_plural($status['remaining'], t('1 item'), t('@count items')),
  557. '@percentage' => ((int)min(100, 100 * ($status['total'] - $status['remaining']) / max(1, $status['total']))),
  558. )
  559. );
  560. $messages[] = array(t('Remaining'), $remaining_message);
  561. $messages[] = array(t('Schema'), t('@schema_version', $stats_summary));
  562. if (!empty($stats_summary['@core_name'])) {
  563. $messages[] = array(t('Solr Core Name'), t('@core_name', $stats_summary));
  564. }
  565. $messages[] = array(t('Delay'), t('@autocommit_time before updates are processed.', $stats_summary));
  566. $messages[] = array(t('Pending Deletions'), t('@deletes_total', $stats_summary));
  567. }
  568. catch (Exception $e) {
  569. watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
  570. }
  571. }
  572. if (empty($messages)) {
  573. $messages[] = array(t('Error'), t('No data was returned from the server. Check your log messages.'));
  574. }
  575. // Initializes output with information about which server's setting we are
  576. // editing, as it is otherwise not transparent to the end user.
  577. $output['apachesolr_index_action_status'] = array(
  578. '#prefix' => '<h3>' . t('@environment: Search Index Content', array('@environment' => $environment['name'])) . '</h3>',
  579. '#theme' => 'table',
  580. '#header' => array(t('Type'), t('Value')),
  581. '#rows' => $messages,
  582. );
  583. $output['viewmore'] = array(
  584. '#markup' => l(t('View more details on the search index contents'), 'admin/reports/apachesolr'),
  585. );
  586. $write_status = apachesolr_environment_variable_get($env_id, 'apachesolr_read_only', APACHESOLR_READ_WRITE);
  587. if ($write_status == APACHESOLR_READ_WRITE) {
  588. $output['index_action_form'] = drupal_get_form('apachesolr_index_action_form', $env_id);
  589. $output['index_config_form'] = drupal_get_form('apachesolr_index_config_form', $env_id);
  590. }
  591. else {
  592. drupal_set_message(t('Options for deleting and re-indexing are not available because the index is read-only. This can be changed on the <a href="!settings_page">settings page</a>', array('!settings_page' => url('admin/config/search/apachesolr/settings/' . $env_id . '/edit', array('query' => drupal_get_destination())))), 'warning');
  593. }
  594. return $output;
  595. }
  596. /**
  597. * Get the report, eg.: some statistics and useful data from the Apache Solr index
  598. *
  599. * @param array $environment
  600. *
  601. * @return array page render array
  602. */
  603. function apachesolr_index_report(array $environment = array()) {
  604. if (empty($environment)) {
  605. $env_id = apachesolr_default_environment();
  606. drupal_goto('admin/reports/apachesolr/' . $env_id);
  607. }
  608. $environments = apachesolr_load_all_environments();
  609. $environments_list = array();
  610. foreach ($environments as $env) {
  611. $var_status = array('!name' =>$env['name']);
  612. $environments_list[] = l(t('Statistics for !name', $var_status), 'admin/reports/apachesolr/' . $env['env_id']);
  613. }
  614. $output['environments_list'] = array(
  615. '#theme' => 'item_list',
  616. '#items' => $environments_list,
  617. );
  618. try {
  619. $solr = apachesolr_get_solr($environment['env_id']);
  620. $solr->clearCache();
  621. $data = $solr->getLuke();
  622. }
  623. catch (Exception $e) {
  624. watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
  625. drupal_set_message(nl2br(check_plain($e->getMessage())), "warning");
  626. return $output;
  627. }
  628. $messages = array();
  629. $messages[] = array(t('Number of documents in index'), $data->index->numDocs);
  630. $limit = variable_get('apachesolr_luke_limit', 20000);
  631. if (isset($data->index->numDocs) && $data->index->numDocs > $limit) {
  632. $messages[] = array(t('Limit'), t('You have more than @limit documents, so term frequencies are being omitted for performance reasons.', array('@limit' => $limit)));
  633. $not_found = t('<em>Omitted</em>');
  634. }
  635. elseif (isset($data->index->numDocs)) {
  636. $not_found = t('Not indexed');
  637. try {
  638. $solr = apachesolr_get_solr($environment['env_id']);
  639. // Note: we use 2 since 1 fails on Ubuntu Hardy.
  640. $data = $solr->getLuke(2);
  641. if (isset($data->index->numTerms)) {
  642. $messages[] = array(t('# of terms in index'), $data->index->numTerms);
  643. }
  644. }
  645. catch (Exception $e) {
  646. watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
  647. $data->fields = array();
  648. }
  649. }
  650. // Initializes output with information about which server's setting we are
  651. // editing, as it is otherwise not transparent to the end user.
  652. $fields = (array)$data->fields;
  653. if ($fields) {
  654. $messages[] = array(t('# of fields in index'), count($fields));
  655. }
  656. // Output the messages we have for this page
  657. $output['apachesolr_index_report'] = array(
  658. '#theme' => 'table',
  659. '#header' => array('type', 'value'),
  660. '#rows' => $messages,
  661. );
  662. if ($fields) {
  663. // Initializes table header.
  664. $header = array(
  665. 'name' => t('Field name'),
  666. 'type' => t('Index type'),
  667. 'terms' => t('Distinct terms'),
  668. );
  669. // Builds table rows.
  670. $rows = array();
  671. foreach ($fields as $name => $field) {
  672. // TODO: try to map the name to something more meaningful.
  673. $rows[$name] = array(
  674. 'name' => $name,
  675. 'type' => $field->type,
  676. 'terms' => isset($field->distinct) ? $field->distinct : $not_found
  677. );
  678. }
  679. ksort($rows);
  680. // Output the fields we found for this environment
  681. $output['field_table'] = array(
  682. '#theme' => 'table',
  683. '#header' => $header,
  684. '#rows' => $rows,
  685. );
  686. }
  687. else {
  688. $output['field_table'] = array('#markup' => t('No data on indexed fields.'));
  689. }
  690. return $output;
  691. }
  692. /**
  693. * Page callback to show available conf files.
  694. *
  695. * @param array $environment
  696. *
  697. * @return string
  698. * A non-render array but plain theme output for the config files overview. Could be done better probably
  699. */
  700. function apachesolr_config_files_overview(array $environment = array()) {
  701. if (empty($environment)) {
  702. $env_id = apachesolr_default_environment();
  703. }
  704. else {
  705. $env_id = $environment['env_id'];
  706. }
  707. $xml = NULL;
  708. try {
  709. $solr = apachesolr_get_solr($env_id);
  710. $response = $solr->makeServletRequest('admin/file', array('wt' => 'xml'));
  711. $xml = simplexml_load_string($response->data);
  712. }
  713. catch (Exception $e) {
  714. watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
  715. drupal_set_message(nl2br(check_plain($e->getMessage())), "warning");
  716. }
  717. if ($xml) {
  718. // Retrieve our items from the xml using xpath
  719. $items = $xml->xpath('//lst[@name="files"]/lst');
  720. // Add all the data of the file in a files array
  721. $files = array();
  722. foreach ($items as $item_id => $item) {
  723. // Do not list directories. Always a bool
  724. if (isset($item->bool)) {
  725. continue;
  726. }
  727. // Get data from the files.
  728. $name = ((string)$item->attributes()) ? (string)$item->attributes() : t('No name found');
  729. $files[$item_id]['name'] = l($name, 'admin/reports/apachesolr/' . $env_id . '/conf/' . $name);
  730. // Retrieve the date attribute
  731. if (isset($item->date)) {
  732. $modified = ((string)$item->date->attributes() == 'modified') ? (string) $item->date : t('No date found');
  733. $files[$item_id]['modified'] = format_date(strtotime($modified));
  734. }
  735. // Retrieve the size attribute
  736. if (isset($item->long)) {
  737. $size = ((string)$item->long->attributes() == 'size') ? (string) $item->long : t('No size found');
  738. $files[$item_id]['size'] = t('Size (bytes): @bytes', array('@bytes' => $size));
  739. }
  740. }
  741. // Sort our files alphabetically
  742. ksort($files);
  743. // Initializes table header.
  744. $header = array(
  745. 'name' => t('File name'),
  746. 'date' => t('Modified'),
  747. 'size' => t('Size'),
  748. );
  749. // Display the table of field names, index types, and term counts.
  750. $variables = array(
  751. 'header' => $header,
  752. 'rows' => $files,
  753. );
  754. $output = theme('table', $variables);
  755. }
  756. else {
  757. $output = '<p>' . t('No data about any file found.') . "</p>\n";
  758. }
  759. return $output;
  760. }
  761. /**
  762. * Page callback to show one conf file.
  763. *
  764. * @param string $name
  765. * @param array $environment
  766. *
  767. * @return string
  768. * the requested config file
  769. */
  770. function apachesolr_config_file($name, array $environment = array()) {
  771. if (empty($environment)) {
  772. $env_id = apachesolr_default_environment();
  773. }
  774. else {
  775. $env_id = $environment['env_id'];
  776. }
  777. $output = '';
  778. try {
  779. $solr = apachesolr_get_solr($env_id);
  780. $response = $solr->makeServletRequest('admin/file', array('file' => $name));
  781. $raw_file = $response->data;
  782. $output = '<pre>' . check_plain($raw_file) . '</pre>';
  783. drupal_set_title(check_plain($name));
  784. }
  785. catch (Exception $e) {
  786. watchdog('Apache Solr', nl2br(check_plain($e->getMessage())), NULL, WATCHDOG_ERROR);
  787. drupal_set_message(nl2br(check_plain($e->getMessage())), "warning");
  788. }
  789. return $output;
  790. }
  791. /**
  792. * Form builder for the Apachesolr Indexer actions form.
  793. *
  794. * @param array $form
  795. * @param array $form_state
  796. * @param string $env_id
  797. * The machine name of the environment.
  798. * @see apachesolr_index_action_form_delete_submit().
  799. *
  800. * @return array $form
  801. */
  802. function apachesolr_index_action_form(array $form, array $form_state, $env_id) {
  803. $form = array();
  804. $form['action'] = array(
  805. '#type' => 'fieldset',
  806. '#title' => t('Actions'),
  807. '#collapsible' => TRUE,
  808. );
  809. $form['action']['env_id'] = array(
  810. '#type' => 'value',
  811. '#value' => $env_id,
  812. );
  813. $form['action']['cron'] = array(
  814. '#prefix' => '<div>',
  815. '#type' => 'submit',
  816. '#value' => t('Index queued content (!amount)', array('!amount' => variable_get('apachesolr_cron_limit', 50))),
  817. '#submit' => array('apachesolr_index_action_form_cron_submit'),
  818. );
  819. $form['action']['cron_description'] = array(
  820. '#prefix' => '<span>',
  821. '#suffix' => '</span></div>',
  822. '#markup' => t('Indexes just as many items as 1 cron run would do.'),
  823. );
  824. $form['action']['remaining'] = array(
  825. '#prefix' => '<div>',
  826. '#type' => 'submit',
  827. '#value' => t('Index all queued content'),
  828. '#submit' => array('apachesolr_index_action_form_remaining_submit'),
  829. );
  830. $form['action']['remaining_description'] = array(
  831. '#prefix' => '<span>',
  832. '#suffix' => '</span></div>',
  833. '#markup' => t('Could take time and could put an increased load on your server.'),
  834. );
  835. $form['action']['reset'] = array(
  836. '#prefix' => '<div>',
  837. '#suffix' => '</div>',
  838. '#type' => 'submit',
  839. '#value' => t('Queue all content for reindexing'),
  840. '#submit' => array('apachesolr_index_action_form_reset_submit'),
  841. );
  842. $form['action']['delete'] = array(
  843. '#prefix' => '<div>',
  844. '#type' => 'submit',
  845. '#value' => t('Delete the Search & Solr index'),
  846. '#submit' => array('apachesolr_index_action_form_delete_submit'),
  847. );
  848. $form['action']['delete_description'] = array(
  849. '#prefix' => '<span>',
  850. '#suffix' => '</span></div>',
  851. '#markup' => t('Useful with a corrupt index or a new schema.xml.'),
  852. );
  853. return $form;
  854. }
  855. /**
  856. * Submit handler for the Indexer actions form, delete button.
  857. *
  858. * @param array $form
  859. * @param array $form_state
  860. */
  861. function apachesolr_index_action_form_remaining_submit(array $form, array &$form_state) {
  862. $destination = array();
  863. if (isset($_GET['destination'])) {
  864. $destination = drupal_get_destination();
  865. unset($_GET['destination']);
  866. }
  867. $env_id = $form_state['values']['env_id'];
  868. $form_state['redirect'] = array('admin/config/search/apachesolr/settings/' . $env_id . '/index/remaining', array('query' => $destination));
  869. }
  870. /**
  871. * Submit handler for the Indexer actions form, delete button.
  872. *
  873. * @param array $form
  874. * @param array $form_state
  875. */
  876. function apachesolr_index_action_form_delete_submit(array $form, array &$form_state) {
  877. $destination = array();
  878. if (isset($_GET['destination'])) {
  879. $destination = drupal_get_destination();
  880. unset($_GET['destination']);
  881. }
  882. $env_id = $form_state['values']['env_id'];
  883. $form_state['redirect'] = array('admin/config/search/apachesolr/settings/' . $env_id . '/index/delete', array('query' => $destination));
  884. }
  885. /**
  886. * Submit handler for the Indexer actions form, delete button.
  887. *
  888. * @param array $form
  889. * @param array $form_state
  890. */
  891. function apachesolr_index_action_form_reset_submit(array $form, array &$form_state) {
  892. $destination = array();
  893. if (isset($_GET['destination'])) {
  894. $destination = drupal_get_destination();
  895. unset($_GET['destination']);
  896. }
  897. $env_id = $form_state['values']['env_id'];
  898. $form_state['redirect'] = array('admin/config/search/apachesolr/settings/' . $env_id . '/index/reset', array('query' => $destination));
  899. }
  900. /**
  901. * Submit handler for the deletion form.
  902. *
  903. * @param array $form
  904. * @param array $form_state
  905. */
  906. function apachesolr_index_action_form_cron_submit(array $form, array &$form_state) {
  907. if (!empty($form_state['build_info']['args'][0])) {
  908. $env_id = $form_state['build_info']['args'][0];
  909. $form_state['redirect'] = 'admin/config/search/apachesolr/settings/' . $env_id . '/index';
  910. }
  911. else {
  912. $env_id = apachesolr_default_environment();
  913. $form_state['redirect'] = 'admin/config/search/apachesolr';
  914. }
  915. apachesolr_cron($env_id);
  916. drupal_set_message(t('Apachesolr cron successfully executed'));
  917. }
  918. /**
  919. * Form builder for to reindex the remaining items left in the queue.
  920. *
  921. * @see apachesolr_index_action_form_delete_confirm_submit().
  922. *
  923. * @param array $form
  924. * @param array $form_state
  925. * @param array $environment
  926. *
  927. * @return mixed
  928. */
  929. function apachesolr_index_action_form_remaining_confirm(array $form, array &$form_state, array $environment) {
  930. return confirm_form($form,
  931. t('Are you sure you want index all remaining content?'),
  932. 'admin/config/search/apachesolr/settings/' . $environment['env_id'] . '/index',
  933. NULL,
  934. t('Index all remaining')
  935. );
  936. }
  937. /**
  938. * Submit handler for the deletion form.
  939. *
  940. * @param array $form
  941. * @param array $form_state
  942. */
  943. function apachesolr_index_action_form_remaining_confirm_submit(array $form, array &$form_state) {
  944. if (!empty($form_state['build_info']['args'][0]['env_id'])) {
  945. $env_id = $form_state['build_info']['args'][0]['env_id'];
  946. $form_state['redirect'] = 'admin/config/search/apachesolr/settings/' . $env_id . '/index';
  947. }
  948. else {
  949. $env_id = apachesolr_default_environment();
  950. $form_state['redirect'] = 'admin/config/search/apachesolr';
  951. }
  952. apachesolr_index_batch_index_remaining($env_id);
  953. }
  954. /**
  955. * Form builder for the index re-enqueue form.
  956. *
  957. * @see apachesolr_index_action_form_reset_confirm_submit().
  958. *
  959. * @param array $form
  960. * @param array $form_state
  961. * @param array $environment
  962. *
  963. * @return mixed
  964. */
  965. function apachesolr_index_action_form_reset_confirm(array $form, array &$form_state, array $environment) {
  966. return confirm_form($form,
  967. t('Are you sure you want to queue content for reindexing?'),
  968. 'admin/config/search/apachesolr/settings/' . $environment['env_id'] . '/index',
  969. t('All content on the site will be queued for indexing. The documents currently in the Solr index will remain searchable.'),
  970. t('Queue all content')
  971. );
  972. }
  973. /**
  974. * Submit handler for the deletion form.
  975. *
  976. * @param array $form
  977. * @param array $form_state
  978. */
  979. function apachesolr_index_action_form_reset_confirm_submit(array $form, array &$form_state) {
  980. if (!empty($form_state['build_info']['args'][0]['env_id'])) {
  981. $env_id = $form_state['build_info']['args'][0]['env_id'];
  982. $form_state['redirect'] = 'admin/config/search/apachesolr/settings/' . $env_id . '/index';
  983. }
  984. else {
  985. $env_id = apachesolr_default_environment();
  986. $form_state['redirect'] = 'admin/config/search/apachesolr';
  987. }
  988. module_load_include('inc', 'apachesolr', 'apachesolr.index');
  989. apachesolr_index_mark_for_reindex($env_id);
  990. drupal_set_message(t('All the content on your site is queued for indexing. You can wait for it to be indexed during cron runs, or you can manually reindex it.'));
  991. }
  992. /**
  993. * Form builder for the index delete/clear form.
  994. *
  995. * @see apachesolr_index_action_form_delete_confirm_submit().
  996. * @param array $form
  997. * @param array $form_state
  998. * @param array $environment
  999. *
  1000. * @return array output of confirm_form()
  1001. */
  1002. function apachesolr_index_action_form_delete_confirm(array $form, array &$form_state, array $environment) {
  1003. return confirm_form($form,
  1004. t('Are you sure you want to clear your index?'),
  1005. 'admin/config/search/apachesolr/settings/' . $environment['env_id'] . '/index',
  1006. t('This will remove all data from your index and all search results will be incomplete until your site is reindexed.'),
  1007. t('Delete index')
  1008. );
  1009. }
  1010. /**
  1011. * Submit handler for the deletion form.
  1012. *
  1013. * @param array $form
  1014. * @param array $form_state
  1015. */
  1016. function apachesolr_index_action_form_delete_confirm_submit(array $form, array &$form_state) {
  1017. if (!empty($form_state['build_info']['args'][0]['env_id'])) {
  1018. $env_id = $form_state['build_info']['args'][0]['env_id'];
  1019. $form_state['redirect'] = 'admin/config/search/apachesolr/settings/' . $env_id . '/index';
  1020. }
  1021. else {
  1022. $env_id = apachesolr_default_environment();
  1023. $form_state['redirect'] = 'admin/config/search/apachesolr';
  1024. }
  1025. // Rebuild our tracking table.
  1026. module_load_include('inc', 'apachesolr', 'apachesolr.index');
  1027. if (apachesolr_index_delete_index($env_id)) {
  1028. drupal_set_message(t('The index has been deleted.'));
  1029. }
  1030. else {
  1031. drupal_set_message(t('An error occured while trying to delete the index.'), 'error');
  1032. }
  1033. }
  1034. /**
  1035. * Submit a batch job to index the remaining, non-indexed content.
  1036. *
  1037. * @param string $env_id
  1038. * The environment ID where it needs to index the remaining items for
  1039. */
  1040. function apachesolr_index_batch_index_remaining($env_id, $total_limit = null) {
  1041. $batch = array(
  1042. 'operations' => array(
  1043. array(
  1044. 'apachesolr_index_batch_index_entities',
  1045. array(
  1046. $env_id,
  1047. $total_limit,
  1048. ),
  1049. ),
  1050. ),
  1051. 'finished' => 'apachesolr_index_batch_index_finished',
  1052. 'title' => t('Indexing'),
  1053. 'init_message' => t('Preparing to submit content to Solr for indexing...'),
  1054. 'progress_message' => t('Submitting content to Solr...'),
  1055. 'error_message' => t('Solr indexing has encountered an error.'),
  1056. 'file' => drupal_get_path('module', 'apachesolr') . '/apachesolr.admin.inc',
  1057. );
  1058. batch_set($batch);
  1059. }
  1060. /**
  1061. * Batch Operation Callback
  1062. *
  1063. * @param string $env_id
  1064. * The machine name of the environment.
  1065. * @param $total_limit
  1066. * The total number of items to index across all batches
  1067. * @param array $context
  1068. *
  1069. * @return false
  1070. * return false when an exception was caught
  1071. *
  1072. * @throws Exception
  1073. * When solr gives an error, throw an exception that solr is not available
  1074. */
  1075. function apachesolr_index_batch_index_entities($env_id, $total_limit = NULL, &$context) {
  1076. module_load_include('inc', 'apachesolr', 'apachesolr.index');
  1077. if (empty($context['sandbox'])) {
  1078. try {
  1079. // Get the $solr object
  1080. $solr = apachesolr_get_solr($env_id);
  1081. // If there is no server available, don't continue.
  1082. if (!$solr->ping()) {
  1083. throw new Exception(t('No Solr instance available during indexing.'));
  1084. }
  1085. }
  1086. catch (Exception $e) {
  1087. watchdog('Apache Solr', $e->getMessage(), NULL, WATCHDOG_ERROR);
  1088. return FALSE;
  1089. }
  1090. $status = apachesolr_index_status($env_id);
  1091. $context['sandbox']['progress'] = 0;
  1092. $context['sandbox']['submitted'] = 0;
  1093. // How many items do we want to index? All or a limited set of items
  1094. if (empty($total_limit)) {
  1095. $context['sandbox']['max'] = $status['remaining'];
  1096. }
  1097. else {
  1098. $context['sandbox']['max'] = $total_limit;
  1099. }
  1100. }
  1101. // We can safely process the apachesolr_cron_limit nodes at a time without a
  1102. // timeout or out of memory error.
  1103. $limit = variable_get('apachesolr_cron_limit', 50);
  1104. // Reduce the limit for our final batch if we would be processing more than had been requested
  1105. if ($limit + $context['sandbox']['progress'] > $context['sandbox']['max']) {
  1106. $limit = $context['sandbox']['max'] - $context['sandbox']['progress'];
  1107. }
  1108. if ($context['sandbox']['max'] >= $context['sandbox']['progress'] + $limit) {
  1109. $context['sandbox']['progress'] += $limit;
  1110. }
  1111. else {
  1112. $context['sandbox']['progress'] = $context['sandbox']['max'];
  1113. }
  1114. $context['sandbox']['submitted'] += apachesolr_index_entities($env_id, $limit);
  1115. $arguments = array(
  1116. '@current' => $context['sandbox']['progress'],
  1117. '@total' => $context['sandbox']['max'],
  1118. '@submitted' => $context['sandbox']['submitted'],
  1119. );
  1120. $context['message'] = t('Inspected @current of @total entities. Submitted @submitted documents to Solr', $arguments);
  1121. // Inform the batch engine that we are not finished, and provide an
  1122. // estimation of the completion level we reached.
  1123. $context['finished'] = empty($context['sandbox']['max']) ? 1 : $context['sandbox']['progress'] / $context['sandbox']['max'];
  1124. // Put the total into the results section when we're finished so we can
  1125. // show it to the admin.
  1126. if ($context['finished']) {
  1127. $context['results']['count'] = $context['sandbox']['progress'];
  1128. $context['results']['submitted'] = $context['sandbox']['submitted'];
  1129. $context['results']['env_id'] = $env_id;
  1130. }
  1131. }
  1132. /**
  1133. * Batch 'finished' callback
  1134. *
  1135. * @param bool $success
  1136. * Whether the batch ended with success or not
  1137. * @param array $results
  1138. * @param array $operations
  1139. */
  1140. function apachesolr_index_batch_index_finished($success, array $results, array $operations) {
  1141. $message = '';
  1142. // $results['count'] will not be set if Solr is unavailable.
  1143. if (isset($results['count'])) {
  1144. $message .= format_plural($results['count'], '1 item processed successfully. ', '@count items successfully processed. ');
  1145. }
  1146. if (isset($results['submitted'])) {
  1147. $message .= format_plural($results['submitted'], '1 document successfully sent to Solr.', '@count documents successfully sent to Solr.');
  1148. }
  1149. if ($success) {
  1150. // Directly process those documents if direct commit was enabled.
  1151. $direct_commit = apachesolr_environment_variable_get($results['env_id'], 'apachesolr_direct_commit', FALSE);
  1152. if ($direct_commit) {
  1153. // Get the $solr object
  1154. $solr = apachesolr_get_solr($results['env_id']);
  1155. $solr->commit();
  1156. }
  1157. $type = 'status';
  1158. }
  1159. else {
  1160. // An error occurred. $operations contains the unprocessed operations.
  1161. $error_operation = reset($operations);
  1162. $message .= ' ' . t('An error occurred while processing @num with arguments: @args', array('@num' => $error_operation[0], '@args' => print_r($error_operation[0], TRUE)));
  1163. $type = 'error';
  1164. }
  1165. drupal_set_message($message, $type);
  1166. }
  1167. /**
  1168. * Form builder for the bundle configuration form.
  1169. *
  1170. * @see apachesolr_index_config_form_submit().
  1171. *
  1172. * @param array $form
  1173. * @param array $form_state
  1174. * @param string $env_id
  1175. * The machine name of the environment.
  1176. *
  1177. * @return array $form
  1178. */
  1179. function apachesolr_index_config_form(array $form, array $form_state, $env_id) {
  1180. $form['config'] = array(
  1181. '#type' => 'fieldset',
  1182. '#title' => t('Configuration'),
  1183. '#collapsible' => TRUE,
  1184. );
  1185. $form['config']['bundles'] = array(
  1186. '#type' => 'markup',
  1187. '#markup' => t('Select the entity types and bundles that should be indexed.'),
  1188. );
  1189. // For future extensibility, when we have multiple cores.
  1190. $form['config']['env_id'] = array(
  1191. '#type' => 'value',
  1192. '#value' => $env_id,
  1193. );
  1194. foreach (entity_get_info() as $entity_type => $entity_info) {
  1195. if (!empty($entity_info['apachesolr']['indexable'])) {
  1196. $options = array();
  1197. foreach ($entity_info['bundles'] as $key => $info) {
  1198. $options[$key] = $info['label'];
  1199. }
  1200. asort($options);
  1201. $form['config']['entities']['#tree'] = TRUE;
  1202. $form['config']['entities'][$entity_type] = array(
  1203. '#type' => 'checkboxes',
  1204. '#title' => check_plain($entity_info['label']),
  1205. '#options' => $options,
  1206. '#default_value' => apachesolr_get_index_bundles($env_id, $entity_type),
  1207. );
  1208. }
  1209. }
  1210. $form['config']['submit'] = array('#type' => 'submit', '#value' => t('Save'));
  1211. return $form;
  1212. }
  1213. /**
  1214. * Submit handler for the bundle configuration form.
  1215. *
  1216. * @param array $form
  1217. * @param array $form_state
  1218. */
  1219. function apachesolr_index_config_form_submit(array $form, array &$form_state) {
  1220. module_load_include('inc', 'apachesolr', 'apachesolr.index');
  1221. $form_values = $form_state['values'];
  1222. $env_id = $form_values['env_id'];
  1223. foreach ($form_values['entities'] as $entity_type => $bundles) {
  1224. $existing_bundles = apachesolr_get_index_bundles($env_id, $entity_type);
  1225. $all_bundles = array_keys($bundles);
  1226. $new_bundles = array_values(array_filter($bundles));
  1227. apachesolr_index_set_bundles($env_id, $entity_type, $new_bundles);
  1228. // Remove all excluded bundles - this happens on form submit
  1229. // even if there is no change so the admin can remove
  1230. // bundles if there was an error.
  1231. $excluded_bundles = array_diff($all_bundles, $new_bundles);
  1232. if (apachesolr_index_delete_bundles($env_id, $entity_type, $excluded_bundles)) {
  1233. $callback = apachesolr_entity_get_callback($entity_type, 'bundles changed callback');
  1234. if (!empty($callback)) {
  1235. call_user_func($callback, $env_id, $existing_bundles, $new_bundles);
  1236. }
  1237. }
  1238. else {
  1239. drupal_set_message(t('Search is temporarily unavailable. If the problem persists, please contact the site administrator.'), 'error');
  1240. }
  1241. }
  1242. // Clear the entity cache, since we will be changing its data.
  1243. entity_info_cache_clear();
  1244. cache_clear_all('apachesolr:environments', 'cache_apachesolr');
  1245. drupal_set_message(t('Your settings have been saved.'));
  1246. }
  1247. /**
  1248. * Page callback for node/%node/devel/apachesolr.
  1249. *
  1250. * @param object $node
  1251. * @return string debugging information
  1252. */
  1253. function apachesolr_devel($node) {
  1254. module_load_include('inc', 'apachesolr', 'apachesolr.index');
  1255. $item = new stdClass();
  1256. $item->entity_type = 'node';
  1257. $item->entity_id = $node->nid;
  1258. $output = '';
  1259. foreach (apachesolr_load_all_environments() as $env_id => $environment) {
  1260. $documents = apachesolr_index_entity_to_documents($item, $env_id);
  1261. $output .= '<h1>' . t('Environment %name (%env_id)', array('%name' => $environment['name'], '%env_id' => $env_id)). '</h1>';
  1262. foreach ($documents as $document) {
  1263. $debug_data = array();
  1264. foreach ($document as $key => $value) {
  1265. $debug_data[$key] = $value;
  1266. }
  1267. $output .= kdevel_print_object($debug_data);
  1268. }
  1269. }
  1270. return $output;
  1271. }